In Perl 5, a module can declare an AUTOLOAD() subroutine that will be called if an undefined subroutine is called. Similarly in Perl 6 classes there is a FALLBACK() method. But I could not find anything similar to FALLBACK() for modules.
Here is an example of a use case: I would like to write a new module MyModule that exports all subroutines that another module OtherModule exports, and then forwards all subroutine calls to those exported methods (not yet defined) in MyModule to the corresponding subroutine in OtherModule. I think I can get the exported symbols from OtherModule using CompUnit::Util.
Question: How can I implement the Perl 5 autoload mechanism in Perl 6?
Lexical names are resolved statically, so I'm not sure how to implement AUTOLOAD without some deep magic.
Regarding your specific example, I'm also not quite sure what the 'proper' way to do so would be, but here's a 'dirty' solution that seems to work, replacing a sub &foo exported by a module Foo:
# get all subroutines exported by Foo by default
BEGIN my #reex = do {
use Foo;
MY::.pairs.grep(*.key.starts-with('&'));
}
# export our replacement subroutine (also in the default namespace)
sub foo is export {
say "new foo";
}
# re-export subroutines lacking a replacement
# placed at the end of the module so EXPORT will have been populated
BEGIN EXPORT::DEFAULT::{.key} //= .value
for #reex;
I want to use Raku Modules to group some functions, I often use. Because these functions are all loosely coupled, I don't like to add them in a class.
I like the idea of use, where you can select, which functions should be imported, but I don't like it, that the functions, which are imported are then stored in the global namespace.
For example if I have a file my_util.pm6:
#content of my_util.pm6
unit module my_util;
our sub greet($who) is export(:greet) {
say $who;
}
sub greet2($who) is export(:greet2) {
say $who;
}
sub greet3($who) is export(:greet3) {
say $who;
}
and a file test.p6:
#!/usr/bin/perl6
#content of test.p6
use v6.c;
use lib '.';
use my_util :greet2;
greet("Bob"); #should not work (because no namespace given) and also doesn't work
greet2("Bob"); #should not work (because no namespace given) but actually works
greet3("Bob"); #should not work (because no namespace given) and also doesn't work
my_util::greet("Alice"); #works, but should not work (because it is not imported)
my_util::greet2("Alice"); #should work, but doesn't work
my_util::greet3("Alice"); #should not work (because it is not imported) and also doesn't work
I would like to call all functions via my_util::greet() and not via greet() only.
The function greet() defined in my_util.pm6 comes very close to my requirements, but because it is defined as our, it is always imported. What I like is the possibility, to select which functions should be imported and it should be possible to leave it in the namespace defined by the module (i.e. it doesn't pollute the global namespace)
Does anyone know, how I can achieve this?
To clear up some potential confusion...
Lexical scopes and package symbol tables are different things.
my adds a symbol to the current lexical scope.
our adds a symbol to the current lexical scope, and to the public symbol table of the current package.
use copies the requested symbols into the current lexical scope.
That's called "importing".
The :: separator does a package lookup – i.e. foo::greet looks up the symbol greet in the public symbol table of package foo.
This doesn't involve any "importing".
As for what you want to achieve...
The public symbol table of a package is the same no matter where it is referenced from... There is no mechanism for making individual symbols in it visible from different scopes.
You could make the colons part of the actual names of the subroutines...
sub foo::greet($who) is export(:greet) { say "Hello, $who!" }
# This subroutine is now literally called "foo::greet".
...but then you can't call it in the normal way anymore (because the parser would interpret that as rule 4 above), so you would have to use the clunky "indirect lexical lookup" syntax, which is obviously not what you want:
foo::greet "Sam"; # Could not find symbol '&greet'
::<&foo::greet>( "Sam" ); # Hello, Sam!
So, your best bet would be to either...
Declare the subroutines with our, and live with the fact that all of them can be accessed from all scopes that use the module.
Or:
Add the common prefix directly to the subroutine names, but using an unproblematic separator (such as the dash), and then import them normally:
unit module foo;
sub foo-greet($who) is export(:greet) { ... }
sub foo-greet2($who) is export(:greet2) { ... }
sub foo-greet3($who) is export(:greet3) { ... }
There are two packages I want to use: CorpusLoaders.jl, and WordNet.jl
CorpusLoaders.SemCor exports sensekey(::SenseTaggedWord)
WordNet exports sensekey(::DB, ::Synset, ::Lemma)
I want to use both sensekey methods.
Eg
for some mixed list of items: mixedlist::Vector{Union{Tuple{SenseTaggedWord},Tuple{DB, Synset,Lemma}}.
Ie the items in the list are a mixture of 1-tuples of SenseTaggedWord, and3 tuples of DB, Synset, and Lemma.
for item in mixedlist
println(sensekey(item...)
end
should work.
This example is a little facetious, since why would I be mixing them like this.
But, hopefully it serves for illustrating the problem in the general case.
Trying to using CorpusLoaders.SemCor, WordNet to bring in both results in WARNING: both WordNet and Semcor export "sensekey"; uses of it in module Main must be qualified.
Manually importing both: import CorpusLoaders.SemCor.sensekey; import WordNet.sensekey results in WARNING: ignoring conflicting import of Semcor.sensekey into Main
What can be done? I want them both, and they don't really conflict, due to multiple-dispatch.
Given that CorpusLoaders.jl is a package I am writing I do have a few more options, since I could make my CorpusLoaders.jl depend on WordNet.jl.
If I did do than then I could say in CorpusLoaders.jl
import WordNet
function WordNet.sensekey(s::SenseTaggedWord)...
and that would make them both work.
But it would mean requiring WordNet as a dependency of CorpusLoaders.
And I want to know how to solve the problem for a consumer of the packages -- not as the creator of the packages.
tl;dr qualify the functions when using them in your script via their module namespace, i.e. CorpusLoader.sensekey() and WordNet.sensekey()
Explanation
My understanding of your question after the edits (thank you for clarifying) is that:
You have written a package called CorpusLoaders.jl, which exports the function sensekey(::SenseTaggedWord)
There is an external package called WordNet.jl, which exports the function sensekey(::DB, ::Synset, ::Lemma)
You have a script that makes use of both modules.
and you are worried that using the modules or "importing" the functions directly could potentially create ambiguity and / or errors in your script, asking
how can I write my CorpusLoaders package to prevent potential clashes with other packages, and
how can I write my script to clearly disambiguate between the two functions while still allowing their use?
I think this stems from a slight confusion how using and import are different from each other, and how modules create a namespace. This is very nicely explained in the docs here.
In essence, the answers are:
You should not worry about exporting things from your module that will clash with other modules. This is what modules are for: you're creating a namespace, which will "qualify" all exported variables, e.g. CorpusLoaders.sensekey(::SenseTaggedWord).
When you type using CorpusLoaders, what you're saying to julia is "import the module itself, and all the exported variables stripped from their namespace qualifier, and bring them into Main". Note that this means you now have access to sensekey as a function directly from Main without a namespace qualifier, and as CorpusLoaders.sensekey(), since you've also imported the module as a variable you can use.
If you then try using the module WordNet as well, julia very reasonably issues a warning, which essentially says:
"You've imported two functions that have the same name. I can't just strip their namespace off because that could create problems in some scenarios (even though in your case it wouldn't because they have different signatures, but I couldn't possibly know this in general). If you want to use either of these functions, please do so using their appropriate namespace qualifier".
So, the solution for 2. is:
you either do
using CorpusLoaders;
using WordNet;
, disregarding the warning, to import all other exported variables as usual in your Main namespace, and access those particular functions directly via their modules as CorpusLoaders.sensekey() and WordNet.sensekey() each time you need to use them in your script, or
you keep both modules clearly disambiguated at all times by doing
import CorpusLoaders;
import WordNet;
and qualify all variables appropriately, or
in this particular case where the function signatures don't clash, if you'd really like to be able to use the function without a namespace qualifier, relying on multiple dispatch instead, you can do something like what FengYang suggested:
import CorpusLoaders;
import WordNet;
sensekey(a::SenseTaggedWord) = CorpusLoader.sensekey(a);
sensekey(a::DB, b::Synset, c::Lemma) = WordNet.sensekey(a, b, c);
which is essentially a new function, defined on module Main, acting as a wrapper for the two namespace-qualified functions.
In the end, it all comes down to using using vs import and namespaces appropriately for your particular code. :)
As an addendum, code can get very unwieldy with long namespace qualifiers like CorpusLoader and WordNet. julia doesn't have something like python's import numpy as np, but at the same time modules become simple variables on your workspace, so it's trivial to create an alias for them. So you can do:
import CorpusLoaders; const cl = CorpusLoaders;
import Wordnet; const wn = WordNet;
# ... code using both cl.sensekey() and wn.sensekey()
In this case, the functions do not conflict, but in general that is impossible to guarantee. It could be the case that a package loaded later will add methods to one of the functions that will conflict. So to be able to use the sensekey for both packages requires some additional guarantees and restrictions.
One way to do this is to ignore both package's sensekey, and instead provide your own, dispatching to the correct package:
sensekey(x) = CorpusLoaders.sensekey(x)
sensekey(x, y, z) = WordNet.sensekey(x,y,z)
I implemented what #Fengyang Wang said,
as a function:
function importfrom(moduleinstance::Module, functionname::Symbol, argtypes::Tuple)
meths = methods(moduleinstance.(functionname), argtypes)
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol)
meths = methods(moduleinstance.(functionname))
importfrom(moduleinstance, functionname, meths)
end
function importfrom(moduleinstance::Module, functionname::Symbol, meths::Base.MethodList)
for mt in meths
paramnames = collect(mt.lambda_template.slotnames[2:end])
paramtypes = collect(mt.sig.parameters[2:end])
paramsig = ((n,t)->Expr(:(::),n,t)).(paramnames, paramtypes)
funcdec = Expr(:(=),
Expr(:call, functionname, paramsig...),
Expr(:call, :($moduleinstance.$functionname), paramnames...)
)
current_module().eval(funcdec) #Runs at global scope, from calling module
end
end
Call with:
using WordNet
using CorpusLoaders.Semcor
importfrom(CorpusLoaders.Semcor, :sensekey)
importfrom(WordNet, :sensekey)
methods(sensekey)
2 methods for generic function sensekey:
sensekey(db::WordNet.DB, ss::WordNet.Synset, lem::WordNet.Lemma)
sensekey(saword::CorpusLoaders.Semcor.SenseAnnotatedWord
If you wanted to get really flash you could reexport the DocString too.
I created a module called foo in my spreadsheet, and then I added the following to it:
Function foo() As Variant
foo = 5
End Function
When I try to run the function in Excel, by typing =foo() into a cell, I get #NAME. When I look at what #NAME is supposed to mean by clicking on the little icon next to it and then help on this error I get this:
Well, not exactly that, but it was about as useful.
Eventually I discovered that changing the module name to something other than foo seemed to fix it. Have I stumbled upon a bug or a feature? Where is this behavior documented?
Since multiple modules are possible and all can have public functions it is also possible that there are multiple public functions with the same name but in different modules. That's why you can call a UDF with =foo.foo(). This is calling the function named "foo" in the module named "foo". That's why =foo() will fail if there is a module named "foo" because foo is first evaluated as the module name.
I'm having trouble using one Lua lib from inside another. I'm not sure about the best way to do it.
I've got a library that returns a (non-global) table with functions, like this:
-- foo.lua
local foo = {}
function foo:m1(...) ... end
function foo:m2(...) ... end
return foo
This library can be inserted in either the global or local scope, depending on what the user wants:
-- globally
foo = require('foo')
-- or locally
local foo = require('foo')
I'm now trying to create another lib (let's call it bar) that requires/uses this foo lib. Something like this:
-- bar.lua
local bar={}
function bar:m3(...)
...
foo:m1()
...
end
My trouble is - I don't know how to "pass" foo to bar.
Ideally I'd like to send it as a parameter to require:
local foo = require('foo')
local bar = require('bar', foo)
But I don't think that's possible (is it?). The other option I could think about was adding a init method to bar:
local foo = require('foo')
local bar = require('bar')
bar:init(foo)
This works, but doesn't look very clean to me; it's possible to forget adding that third line, leaving bar in an "unsafe" state.
Is there a common Lua idiom/method that I'm missing?
Just call require 'foo' directly in you bar module. This is perfectly legal. The foo module will be loaded only once. In order not to leak it out of the bar module, store it in a local variable.
You can use this idiom also to separate one big module into several parts, and have one module require all the others. The user will have to require only one module.