<p dir="auto">This is one of two proposals for a better module system for Julia. The goal is to promote good modular design. This proposal, as opposed to my other proposal, supports multiple modules per file.</p>
<p dir="auto">The main concept is that files are loaded into a program <em>always</em> at the toplevel. Once loaded any module within those files are available for use, either through their absolute paths or by way of importing.</p>
<p dir="auto">I will use the function <code class="notranslate">load</code> in the examples, but obviously another term can be used. When a file is loaded, it's modules are made available as are it's exported functions via absolute paths. e.g.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# Bar.jl
module Bar
  function f()
    ... 
  end
end

# Foo.jl
load Bar
module Foo
  function g()
    Bar.f()
  end
end"><pre class="notranslate"><code class="notranslate"># Bar.jl
module Bar
  function f()
    ... 
  end
end

# Foo.jl
load Bar
module Foo
  function g()
    Bar.f()
  end
end
</code></pre></div>
<p dir="auto">Any toplevel code in a file, i.e. outside the scope of a module or function, is processed only once on first load at compile time. This can be used to create computed resolutions, such as dynamic loads. e.g.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="if something
  load(something)
end"><pre class="notranslate"><code class="notranslate">if something
  load(something)
end
</code></pre></div>
<p dir="auto">With parentheses <code class="notranslate">load</code> works like a function. So in the above, <code class="notranslate">something</code> is a variable. Without parentheses <code class="notranslate">load</code> is more like keyword and treats the following text as if it were a function argument in quotes, i.e. <code class="notranslate">load foo</code> -&gt; <code class="notranslate">load("foo")</code>. It also assumes an extension of <code class="notranslate">.jl</code>. Multiple terms separated by <code class="notranslate">.</code> are converted to <code class="notranslate">/</code>, hence directory separations. So <code class="notranslate">load Foo.Bar</code> looks for <code class="notranslate">Foo/Bar.jl</code>. (Whether letter case matters can be decided latter. It makes no difference to this proposal.)</p>
<p dir="auto">All loads first look to the local directory of the loading file for a match. If not found it then looks to installed packages. Name clashes between local files and packages are generally easy enough to avoid by choosing non conflicting names. It a name conflict is unavoidable a "package marker" can be use to distinguish the package from the file. e.g. a <code class="notranslate">|</code> between the package name and file name (the exact marker to use is an open question).</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="load Foo|Bar"><pre class="notranslate"><code class="notranslate">load Foo|Bar
</code></pre></div>
<p dir="auto">Of course in most cases that will be <code class="notranslate">load Foo|Foo</code> which sucks for redundancy, but maybe someone else can think of a good way to denote that without the duplication. Also, local relative paths can be specified by prefixing <code class="notranslate">./</code> and <code class="notranslate">../</code> if necessary, but generally these should not be needed.</p>
<p dir="auto">As an alternative to the use of the <code class="notranslate">|</code> it could require a "from" term, e.g.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="load Bar from Foo"><pre class="notranslate"><code class="notranslate">load Bar from Foo
</code></pre></div>
<p dir="auto">That reads a little better, albeit it not as concise.</p>
<p dir="auto">I will use the term <code class="notranslate">import</code> to serve as the name of the function that brings module functions into the scope of other modules. I think the is a good term b/c it contrasts well with <code class="notranslate">export</code>.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# Bar.jl
module BarA
  export ga
  function ga() ...
end

module BarB
  export gb
  function gb() ...
end

# Baz.jl
module Baz
  export h
  function h() ...
end

# Foo.jl
load Bar
load Baz

module Foo
  import BarA
  import BarB
  function f()
    ga()
    gb()
  end
end"><pre class="notranslate"><code class="notranslate"># Bar.jl
module BarA
  export ga
  function ga() ...
end

module BarB
  export gb
  function gb() ...
end

# Baz.jl
module Baz
  export h
  function h() ...
end

# Foo.jl
load Bar
load Baz

module Foo
  import BarA
  import BarB
  function f()
    ga()
    gb()
  end
end
</code></pre></div>
<p dir="auto">By importing <code class="notranslate">BarA</code> and <code class="notranslate">BarB</code>, their exported functions are made visible within Foo without absolute paths. There doesn't need to be a separate <code class="notranslate">using</code>, as <code class="notranslate">import</code> allows the methods to both be found and to be extended. If there is a name clash between imported modules the later wins out. Specific methods can be imported by using the colon notation.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="module Foo
  import BarA: ga
end"><pre class="notranslate"><code class="notranslate">module Foo
  import BarA: ga
end
</code></pre></div>
<p dir="auto">This would import only <code class="notranslate">ga</code> and no other functions. Functions must be exported to be accessed. The only way to access none exported function would be to force them, by reopening the module and modifying it. Which beings me to last part of this proposal.</p>
<p dir="auto">If a module is "reopened" then it can be modified. This happens at compile time, so it is safe.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# Bar.jl
module Bar
  function q() ...
end

# Foo.jl
load Bar

module Foo
  function f()
    Bar.q()
  end
end

module Bar
  export q
end"><pre class="notranslate"><code class="notranslate"># Bar.jl
module Bar
  function q() ...
end

# Foo.jl
load Bar

module Foo
  function f()
    Bar.q()
  end
end

module Bar
  export q
end
</code></pre></div>
<p dir="auto">So <code class="notranslate">Foo.f()</code> can work b/c we modified Bar to allow it. This of course should be done with clear understanding of what one is doing. If it is being done to a module from another package b/c the author probably didn't export the function for a reason. But it is important to be able to have this option b/c it makes it possible to fix overlooked limitations and bugs, and improves potential code reuse.</p>
<p dir="auto">The advantages of this design are essentially all the advantages of modular programming since that is what it is designed to provide. One nice thing that stands out is that all loads can go at the top of a file, as order of loads is not significant. That makes it much easier to see what a file requires. It also means the each file can have it's own loads even if they are the same as another files that loads it. They are only ever loaded once.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# Foo.jl
load Bar.jl
load Baz.jl

# Bar.jl
load Quaz.jl

# Baz.jl
load Quaz.jl"><pre class="notranslate"><code class="notranslate"># Foo.jl
load Bar.jl
load Baz.jl

# Bar.jl
load Quaz.jl

# Baz.jl
load Quaz.jl
</code></pre></div>
<p dir="auto">It would be nice if the long form anonymous functions would support return type declarations akin to named functions.  That is, it'd be nice if the following converted and asserted the return type is <code class="notranslate">T</code>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="julia&gt; function(x, y)::T
           return x*y
       end
ERROR: syntax: expected &quot;(&quot; in function definition"><pre class="notranslate"><code class="notranslate">julia&gt; function(x, y)::T
           return x*y
       end
ERROR: syntax: expected "(" in function definition
</code></pre></div>
<p dir="auto">Note that this appears to work with one argument, but it's bugged and actually applying the type declaration to the <em>argument</em> instead of the function's return:</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="julia&gt; f = function(x)::Int
           x/2
       end
#11 (generic function with 1 method)

julia&gt; f(2.0)
ERROR: MethodError: no method matching (::getfield(Main, Symbol(&quot;##11#12&quot;)))(::Float64)
Closest candidates are:
  #11(::Int64) at REPL[41]:2
Stacktrace:
 [1] top-level scope at none:0

julia&gt; :(function(x)::Int
                  x/2
              end)
:(function (x::Int,)
      #= REPL[47]:2 =#
      x / 2
  end)"><pre class="notranslate">julia<span class="pl-k">&gt;</span> f <span class="pl-k">=</span> <span class="pl-k">function</span>(x)<span class="pl-k">::</span><span class="pl-c1">Int</span>
           x<span class="pl-k">/</span><span class="pl-c1">2</span>
       <span class="pl-k">end</span>
<span class="pl-c"><span class="pl-c">#</span>11 (generic function with 1 method)</span>

julia<span class="pl-k">&gt;</span> <span class="pl-c1">f</span>(<span class="pl-c1">2.0</span>)
ERROR<span class="pl-k">:</span> MethodError<span class="pl-k">:</span> no method matching (<span class="pl-k">::</span><span class="pl-c1">getfield</span>(Main, <span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">"</span>##11#12<span class="pl-pds">"</span></span>)))(<span class="pl-k">::</span><span class="pl-c1">Float64</span>)
Closest candidates are<span class="pl-k">:</span>
  <span class="pl-c"><span class="pl-c">#</span>11(::Int64) at REPL[41]:2</span>
Stacktrace<span class="pl-k">:</span>
 [<span class="pl-c1">1</span>] top<span class="pl-k">-</span>level scope at none<span class="pl-k">:</span><span class="pl-c1">0</span>

julia<span class="pl-k">&gt;</span> :(<span class="pl-k">function</span>(x)<span class="pl-k">::</span><span class="pl-c1">Int</span>
                  x<span class="pl-k">/</span><span class="pl-c1">2</span>
              <span class="pl-k">end</span>)
:(<span class="pl-k">function</span> (x<span class="pl-k">::</span><span class="pl-c1">Int</span>,)
      <span class="pl-c"><span class="pl-c">#=</span> REPL[47]:2 <span class="pl-c">=#</span></span>
      x <span class="pl-k">/</span> <span class="pl-c1">2</span>
  <span class="pl-k">end</span>)</pre></div>
<p dir="auto">(from <a href="https://discourse.julialang.org/t/controlling-the-type-of-anonymous-functions/26264" rel="nofollow">https://discourse.julialang.org/t/controlling-the-type-of-anonymous-functions/26264</a>).</p>