modulepackageversioningrakuapi-versioning

How does Raku 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;?


Solution

  • 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:

    use Foo; will load:

    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:

    use Foo; will load:

    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:

    use Foo; will load:

    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