Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Perl6 decide which version of a module gets loaded?

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

like image 976
ugexe Avatar asked Feb 04 '23 19:02

ugexe


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 contains Foo:ver<0.5>

  • inst#/home/ugexe/.raku contains Foo:ver<0.1> and Foo:ver<1.0>

  • inst#/home/ugexe/.raku contains Foo:ver<2.0> and Foo:ver<0.1>

use Foo; will load:

  • EXAMPLE 1 - Foo:ver<1.0> from inst#/home/ugexe/.raku

  • EXAMPLE 2 - Foo:ver<0.5> from file#/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 contains Foo:api<0>:ver<0.5>

  • inst#/home/ugexe/.raku contains Foo:api<1>:ver<0.1> and Foo:api<0>:ver<1.0>

use Foo; will load:

  • EXAMPLE 1 - Foo:api<1>:ver<0.1> from inst#/home/ugexe/.raku

  • EXAMPLE 2 - Foo:api<0>:ver<0.5> from file#/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 contains Foo:auth<github:ugexe>:ver<0.5>

  • inst#/home/ugexe/.raku contains Foo:ver<0.1> and Foo:auth<github:ugexe>:ver<1.0>

use Foo; will load:

  • EXAMPLE 1 - Foo:auth<github:ugexe>:ver<1.0> from inst#/home/ugexe/.raku

  • EXAMPLE 2 - Foo:auth<github:ugexe>:ver<0.5> from file#/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

like image 167
ugexe Avatar answered Feb 18 '23 11:02

ugexe