When I do use Foo:ver<1.0>;
it loads version 1.0 of module Foo
. But what happens when I do use Foo;
?

- 5,297
- 1
- 28
- 49
1 Answers
TL;DR: When given no specific version a default Raku install will load the latest version from the first CompUnit::Repository
it encounters that matches any version of that module (and not neccesarily the highest version out of all CompUnit::Repository
).
It is possible to create and load a non-core CompUnit::Repository
that itself would only load random versions of a module unless otherwise specified. This answer does not apply to these and will focus on how the various core CompUnit::Repository
behave and is specced.
The first thing that determines what module will be loaded is which CompUnit::Repository
matches the requested identity first. The default repository chain will look something like this:
# EXAMPLE 1
$ raku -e '.say for $*REPO.repo-chain'
inst#/home/ugexe/.raku
inst#/home/ugexe/raku/install/share/perl6/site
inst#/home/ugexe/raku/install/share/perl6/vendor
inst#/home/ugexe/raku/install/share/perl6
The inst#
prefix tells us this is a CompUnit::Repository::Installation
. This is relevant because such a repo can contain multiple distributions -- including multiple versions of the same distribution -- which is not true of the single-distribution CompUnit::Repository::FileSystem
used for -I.
or -Ilib
(which are really -Ifile#/home/ugexe/repos/Foo
and -Ifile#/home/ugexe/repos/Foo/lib
).
# EXAMPLE 2
$ raku -I. -e '.say for $*REPO.repo-chain'
file#/home/ugexe/repos/Foo
inst#/home/ugexe/.raku
inst#/home/ugexe/raku/install/share/perl6/site
inst#/home/ugexe/raku/install/share/perl6/vendor
inst#/home/ugexe/raku/install/share/perl6
Lets assume the following:
file#/home/ugexe/repos/Foo
containsFoo:ver<0.5>
inst#/home/ugexe/.raku
containsFoo:ver<0.1>
andFoo:ver<1.0>
inst#/home/ugexe/.raku
containsFoo:ver<2.0>
andFoo:ver<0.1>
use Foo;
will load:
EXAMPLE 1 -
Foo:ver<1.0>
frominst#/home/ugexe/.raku
EXAMPLE 2 -
Foo:ver<0.5>
fromfile#/home/ugexe/repos/Foo
Even though the highest version out of all the repositories is Foo:ver<2.0>
the first repository in the chain that matches any version of Foo (i.e. use Foo
) wins, so Foo:ver<2.0>
is never chosen. You might guess this makes "highest version" the second thing that determines which version of a module gets loaded, but its really the 4th! However I've mentioned it here because for typical usage this is sufficient enough.
The 2nd thing that determines which version of a module get loaded is the api
field. This essentially is another version field that, when combined with the version itself, gives a basic way of pinning major versions.
Lets assume the following:
file#/home/ugexe/repos/Foo
containsFoo:api<0>:ver<0.5>
inst#/home/ugexe/.raku
containsFoo:api<1>:ver<0.1>
andFoo:api<0>:ver<1.0>
use Foo;
will load:
EXAMPLE 1 -
Foo:api<1>:ver<0.1>
frominst#/home/ugexe/.raku
EXAMPLE 2 -
Foo:api<0>:ver<0.5>
fromfile#/home/ugexe/repos/Foo
Even though in EXAMPLE 1 the highest version is Foo:api<0>:ver<1.0>
, the highest api version is Foo:api<1>:ver<0.1>
and thus is chosen.
The 3rd thing that determines which version of a module gets loaded is the auth
field. Unlike api
and ver
it does not imply any sorting. And also unlike api
and ver
field you probably shouldn't be using it in your e.g. use Foo
-- it is policy focused and will serve to be a power-tool/escape hatch most developers should hopefully never have to worry about (ab)using.
Lets assume the following:
file#/home/ugexe/repos/Foo
containsFoo:auth<github:ugexe>:ver<0.5>
inst#/home/ugexe/.raku
containsFoo:ver<0.1>
andFoo:auth<github:ugexe>:ver<1.0>
use Foo;
will load:
EXAMPLE 1 -
Foo:auth<github:ugexe>:ver<1.0>
frominst#/home/ugexe/.raku
EXAMPLE 2 -
Foo:auth<github:ugexe>:ver<0.5>
fromfile#/home/ugexe/repos/Foo
In both examples use Foo;
is the same as use Foo:auth(*):ver(*)
, so even though one of the repo assumptions contains a module with no auth
this does not mean it is an exact match for use Foo;
. Instead the :auth(*)
includes any auth
value as a match (effectively meaning auth
is ignored altogether).
For more examples the spec tests are a good source

- 5,297
- 1
- 28
- 49
-
1Thumbs up for these nice SO Q/A pairs. Are you planning/hoping to post another pair covering `auth` in the next few weeks? In case not, am I right when I assume that a `use` statement with*out* an `auth` field *always* ignores the `auth` metainfo? (I'm assuming this due to what would be logical to me; and one interpretation of what you've written; and the lines in the test file with `.candidates($cuspec-any-auth).elems` and `.installed.grep(*.defined).elems` in them; and your decision not to explain this further in this SO because it's "outside the scope of this post".) TIA for a reply. – raiph Apr 14 '19 at 18:13
-
2@raiph that is correct, and I added an example for `auth`. What I was trying to avoid is explaining how `auth` is intended to be used. It is more policy focused and will serve to be a power-tool/escape hatch most developers should hopefully never have to worry about (ab)using. – ugexe Apr 14 '19 at 18:56
-
"you probably shouldn't be using [auth] in your e.g. use Foo" Is this still correct/best practice? My understanding is that `auth` is very important to prevent loading a package uploaded by a different (possibly malicious) person. See https://github.com/Raku/problem-solving/issues/340#issuecomment-1205401266. Is that incorrect? – codesections Sep 18 '22 at 13:26
-
It sort of depends. If you are only consuming modules from a trusedt source then the malicious aspect could be ignored. In that scenario one can then start using `auth` to provide an alternative implementation of something (perhaps some patched variant a company needs a different dependency to use). If the `auth` is hard coded then they can't do that. `emulates` or `supercedes` could likely provide similar functionality though – ugexe Sep 18 '22 at 20:25