modules

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

modules

fschmidt
Administrator
I will work on implement Lua modules.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
The Lua approach to modules isn't optimal.  See:

http://lua-users.org/wiki/ModuleDefinition
http://lua-users.org/wiki/LuaModuleFunctionCritiqued

It seems that the current standard approach to import a module is like this:

local MM = require 'mymodule'
MM.bar()

But this is ugly.  I suggest that the "require" function return nothing and always assign the module to a global variabe with its name.  But I would add a "package.module" command that returns the module when this is needed.  So then the 2 ways of importing would be:

require 'mymodule'
mymodule.bar()

local MM = package.module 'mymodule'
MM.bar()
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

Hugo <Nabble>
I don't think this
local MM = package.module 'mymodule' 
MM.bar() 
is much better than this
local MM = require 'mymodule' 
MM.bar() 
The advantage of the second case is that this also works in Lua:
require 'mymodule' 
mymodule.bar() 
So I prefer to memorize "require" as the only keyword to load a module instead of memorizing two keywords, even if the "require" may sometimes look ugly. Remember that we also have the "import" keyword for java classes, and this worries me much more than the require/package.module discussion. We will certainly load several java classes from Luan and they will get mixed with Luan modules, so we will have a mix of imports and requires that will confuse users. Maybe "import" should be "require.binary" or "require.java" to keep things in just one direction. We can discuss this if you want to.
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
In reply to this post by fschmidt
done in rev 76.  includes ability to load from classpath.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
In reply to this post by fschmidt
I am not happy with this approach anymore.  Having to precede function definitions with "M." is ugly.  It should be natural like Python.  I will think about this more.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
For this example:

http://lua-users.org/wiki/ModuleDefinition

I am thinking about this:
-- mymodule.lua

-- private
local x = 1
local function baz() print 'test' end

function mymodule.foo() print("foo", x) end

function mymodule.bar()
  mymodule.foo()
  baz()
  print "bar"
end

-- Example usage:
require 'mymodule'
mymodule.bar()
This is similar to the first example in ModuleDefinition but I think it is less cryptic.  It leaves out the first and last line of the module.  I think using the module name is clearer than using "M".  The implementation also becomes very simple.  I just set _ENV.mymodule to an empty table before running the module and then I get it after running the module as the module value.  I ignore the return value.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

Hugo <Nabble>
We talked. We can have a "module" function like this:
module "framework"
and what it does is this:
local framework = {}
package.loaded.framework = framework
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
done in rev 87
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

Hugo <Nabble>
It works fine.
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
closing thread
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
In reply to this post by Hugo <Nabble>
I think this can be improved.  There is no need for the "module" function because the name of the module should just be the name that is passed to "require".  My previous example should look like this:
-- mymodule.lua

-- private
local x = 1
local function baz() print 'test' end

function foo() print("foo", x) end

function bar()
  foo()
  baz()
  print "bar"
end

-- Example usage:
require 'mymodule'
mymodule.bar()
When a module is loaded, it should be run.  If it returns anything, then package.loaded[module] should be set to that value.  If it doesn't return anything, then package.loaded[module] should be set to a copy of _ENV.  When a file is loaded, its _ENV should start empty but have a metatable with __index containing the basic functions.  The "require" function should add packages to the __index, not to _ENV.  That way _ENV stays clean and can be copied to produce the module.

The point of this change is to make modules absolutely trivial as it is in Python.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
One problem with this is that it isn't so easy to remove and rename imported methods and modules.  Another approach is to add modules directly into _ENV as now, but to add a _ENV.loaded variable to tracks what was imported, and then use this to remove these items from the _ENV copy used as the module.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
In reply to this post by fschmidt
I have a better approach.  The basic functions should just be local variables.  This means _ENV can start completely clean as an empty table.  And it means that _ENV simplify defines the module, no need to copy anything.  So if loading/running a module returns nil, then package.loaded[module] is set to _ENV.  If it returns a non-nil value, that value is used.

In this model, I want imported modules to be local variables, basically like this:
local mymodule = require 'mymodule'
And this is what the latest Lua book recommends.  But I think it is annoying to write the module name twice, so I would add a special "import" command to do the same thing like this:
import 'mymodule'
This would be properly handled in the compiler.  Actually I would import as a local variable normally, but into _ENV when in interactive mode.

With this system, I would bring back _G.  But of course unlike Lua, I wouldn't initialize _ENV = _G .  Rather I would automatically generate local variables for everything in _G in the compiler.

With these changes, the longer example code looks like this:
-- mymodule.lua

-- private
local x = 1
local function baz() print 'test' end

function foo() print("foo", x) end

function bar()
  foo()
  baz()
  print "bar"
end

-- Example usage:
import 'mymodule'
mymodule.bar()
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
done in rev 109
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
In reply to this post by fschmidt
fschmidt wrote
In this model, I want imported modules to be local variables, basically like this:
local mymodule = require 'mymodule'
And this is what the latest Lua book recommends.  But I think it is annoying to write the module name twice, so I would add a special "import" command to do the same thing like this:
import 'mymodule'
Now I want to remove "import" and just use "require".  Since removing the global namespace, we have code like this:
import "luan:Io"
local print = Io.print

print "hi"
Both "Io" and "print" are being declared, but in different ways.  This is more consistent:
local Io = require "luan:Io"
local print = Io.print

print "hi"
And "require" is more flexible than an "import" statement, allowing things like this:
local print = require("luan:Io").print

print "hi"
The "import" idea is an innovation moving away from Lua and is an extra idea.  Removing it makes code slightly longer but makes Luan conceptually simpler and closer the Lua.

In the latest release, I removed the global namespace and added a "java" statement to replace the "java" function that was in the global namespace.  What I suggest now is to remove the "java" and "import" statements and instead initialize every local namespace with 2 local variables; "require" and "java".  With these, everything else can be imported.
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture
Reply | Threaded
Open this post in threaded view
|

Re: modules

Hugo <Nabble>
It looks good. Go ahead.
Reply | Threaded
Open this post in threaded view
|

Re: modules

fschmidt
Administrator
done in rev 322, closing
Woe to those who call bad good and good bad -- Isaiah 5:20
Following the Old Testament, not evil modern culture