Please explain syntax

Oh, why people keep implying things I’ve never said? I swear I like Julia! Damn, I love it! Why is that defensive position? I never said anything about prefixing function calls or doubted merits of Julia package manager (which is far superior to most I’ve seen yet). What I was really suggesting is merely a file naming convention to alleviate code structuring. In fact, to implement what I’m asking only two things need to be done:

  1. Add a convention that files with *.jm name extensions represent modules with the same name as the file. ‘module’ keyword in such files can/should be omitted.
  2. Add functionality to existing import and using directives so additionally to existing behavior they will also look in path for a *.jm file with corresponding name/path.

No need to change any existing code, damp existing solutions with includes or do any other sinful things mentioned in above posts.

3 Likes

Hm, what I’ve read is that you:

  1. Find it hard to understand where things are defined in Julia.
  2. Believe that Python does it right, binding module path to file system path.

And thus you propose to follow Python way and bind modules names to file names e.g. with extension .jm. Is it what you mean? If so, in my answer I showed by example that:

  1. Modern Python often abuses this pattern using __all__ attribute and all kinds of reexports.
  2. In Julia it won’t help anyway because importing a function from module Foo doesn’t mean that a method you call is defined in that module too.

I also mentioned several tools that you can use to quickly and unambiguously navigate Julia code. So exactly what problem do you want to solve with the proposed *.jm files?

1 Like

While Oxinabox has already explained this construct, I’d like to add the documentation entry: Function-like-objects.

I am proposing to give developers additional means to organize code with no conflict to the existing system, should they choose to use it. I think it is convenient to know that the ModuleName.jm file represents a module. It is convenient being able to load this module with using ModuleName without specifying its file name or having to previously include its file. No argument this system can be abused, as it happens sometimes in Python. Yet when developers choose not to abuse it, it really helps to organize code. And no need to deprecate existing practice.
And I am just asking to consider this, discuss it maybe. Isn’t this what community forums are for?

Thank you for clarification!
The only confusion was because ::Type{...} construct was never mentioned in the documentation so I didn’t understand it was a functor on an abstract type instance.

It is in the docs, though I agree it isn’t entirely obvious. See Types · The Julia Language

Yes, of course it is fine to make suggestions like this. That said, the benefit is unclear to me; also, when learning a language it may be beneficial to understand existing practice (which takes a lot of time and use) before suggesting changes to something fundamental.

Which is how it works at the moment… so it is unclear what would improve by this.

You may want to look at the documentation for code loading. TL;DR:

  1. using & friends employ a sophisticated, robust and customizable system to actually locate the package,
  2. the standard location is src/PackageName.jl for the main file,
  3. it is customary to just include other files from there.
1 Like

Cool. So,

  1. To my understanding packages and modules are generally different things. One package may contain multiple modules and sub-modules. Can it contain sub-packages?
  2. So inside src/PackageName.jl can I also write using AnotherPackage to load src/AnotherPackage.jl?
  3. Having both file name convention and module declaration in the same file looks ambiguous. What if I write module DifferentModuleName inside src/PackageName.jl?

A package having a single module (and possibly submodules) is what is currently best supported (other things require hacks and workarounds), so this is what most people use.

I don’t know if “sub-packages” is a meaningful concept at the moment in Julia.

I don’t think so, at least not without adding it to the local project with an explicit path. Are you perhaps looking for submodules?

AFAIK you won’t be able to load it via using etc. So don’t :wink:

Do you have a code base, where it’s hard to navigate? I personally stick to what you recommend - most modules I define are defined in a file with the module name!
I do know what you’re talking about, though.
E.g. Core.Compiler isn’t defined in compiler.jl - which made it quite hard to find especially with the bad github search. I had to import Julia Base into Atom, to search for module Compiler :smiley:
But since you propose yet another optional feature to make this easier it doesn’t seem to solve any of this - we’d still be stuck with educating people to only define modules in a file with the same name :wink:

Do you now something like this (do it in REPL)?

julia> @edit Char(1)

So the sophisticated system you are describing works for packages, not modules. Yet Julia also has modules for some reason, modules have exports, etc., so creators thought about unclogging namespace instead of just putting all the code in one heap. I think it is convenient being able to reflect modules structure with file/directory structure within one package/program. It will help to organise the code and resolve ambiguity with module names and file names. I am not talking about packages here.

This is a cool feature, but it does not help to organize code. Besides it only works when Char(::Number) is already in context. Not all development is done in REPL. Try writing a web server and you will soon realize virtues of a fully functioning debugger, etc.

1 Like

Is it really true?

julia> import LinearAlgebra

julia> Base.show(a::LinearAlgebra.SingularException) = "A"

julia> show(LinearAlgebra.SingularException(1))
"A"

And you still could pollute global namespaces with using if you like! :slight_smile:

julia> using LinearAlgebra

julia> show(SingularException(1))
"A"

It works for code loading, via packages, which contain modules. Did you read the documentation I linked above? Specifically, in Julia, a package is an

an independent, reusable collection of Julia code, wrapped in a module

Yes. The reason is namespace management.

Sure. Use submodules. Eg see code in Base for a real-world use case.

Actually yes, there is a concept of sub-projects, which have their own sets of dependencies, and can contain their own top-level modules. We are using this for our test code – it’s its own project, with its own dependencies, including a dependency on the main package, and contains its own top-level module. By making it a sub-project, we can achieve this while keeping it all in the same git repository, and without including the test-specific dependencies in the main package. Although this technique works decently well, it feels a bit under-developed at the moment. More details in this post.

So if I understand you correctly, you want to organize your own code as follows:

src/
  |-- MainModule.jl
  |-- Submodule1.jl
  |-- Submodule2.jl

Then import submodules inside MainModule.jl as:

using Submodul1
# instead of 
# include("Submodule1.jl")
# using Submodule1

And later use then from another package as:

using MainModule.Submodule1

If so, then you can already do it with the only exception of include/using pair which can be combined into a macro, e.g.:

@using_module Submodule1

If it’s not what you want, please describe exactly the workflow you want.

1 Like

I will do the macro. Thank you!

1 Like

One more question, sorry.
How come

(::Type{T})(x::Number) where {T<:AbstractChar} = T(UInt32(x))

does not call itself infinitely?

In this case it is because there exists a more specific method for arguments of type UInt32. To see this consider

julia> @which Char(1)
(::Type{T})(x::Number) where T<:AbstractChar in Base at char.jl:48

julia> @which Char(UInt32(1))
Char(u::UInt32) in Base at char.jl:146

It does mean that you have to be careful when creating your own subtypes of AbstractChar to create all the required methods or you will, as you say, get an infinite loop.

1 Like