Archive

Archive for August, 2020

Tripping over variables

August 25, 2020 Leave a comment

I was wondering where lizmat gets the info for changed modules from. She kindly answered with a link. I learned that updates to modules only show up, when we put them on CPAN. Since most modules are hosted on github, changing a module there does not mean that the world will be informed. I believe a better way to do that would be to fetch the ecosystems (we got two) once a week and check if version in any META6.json has changed.

Anyway, the reason I started this post is the documentation for FixedInt. It reads:

One major caveat to be aware of when using this module. The class instance may not be instantiated in a $ sigiled variable.

Raku $ sigiled scalar variables do not implement a STORE method, but instead do direct assignment; and there doesn’t seem to be any easy way to override that behaviour.

An implication of that is that classes that do implement a STORE method can not be held in a $ sigiled variable. (Well, they can, they just won’t work correctly. The first time you try to store a new value, the entire class instance will be overwritten and disappear.)

That is not true.

class Changing {
    has $!var handles <Str gist raku> is default(Nil);
    method STORE(\v) { note 'storing'; $!var = v }
    method FETCH { note 'fetching'; $!var }
}

constant term:<$a> := Changing.new;

$a = 42;
put $a;
# OUTPUT: storing
          42

The problem here is that the docs talk about variables while Raku don’t got any. It got containers with mutable content and values which are immutable. The language also got symbols that we can actually point at in source code. (Values we can point at in source code are called literals.) In the example above I created a symbol that looks like a variable but is a “reference” to a value of type Changing. The assignment operator can not be overloaded so we can protect immutable values. We can implement the method STORE instead. In fact we must, because there is no container in-between the symbol $a and the instance of Changing. (We get X::Assignment::RO if we try to assign without a STORE.) Since Rakudo does not recognise Changing as a container, it will refuse to call FETCH.

Thundergnat wrote a neat module with very little effort. Quite useful to do calculations with integers of fixed bit size.

my \fixedint = FixedInt.new(:8bit);

say fixedint; # 0

say fixedint -= 12;   # 244
say fixedint.signed;  # -12
say fixedint.bin;     # 0b11110100
say fixedint.hex;     # 0xF4

He achieved all that in just 36 lines of code. The trick is to force the user to bind and thus avoid the creation of a container while using STORE and FETCH to change the object in place. I doubt this is thread safe. Also the user of the module loses the ability to use some forms of quote interpolation and data dumper functions/modules will have less to display.

my \i = Int.new(10);
my $i = Int.new(10);

dd i;
dd $i;
# OUTPUT: 10
          Int $i = 10

We don’t have to define many operators to make custom types work because of plenty of meta-programming that is done in CORE. Many of those constructs assume immutable values. Autothreading is planned and will make the use of ». “interesting”. Thundergnat did not specify a language version for his module. The module itself is not hard to make safe. But – acutally BUT – this will change the interface for the user.

The flexibility of the language bites us here. Even thought the docs explain the difference between different sigils nobody is forced to read it. Also, nobody is forced to stick use v6.d at the beginning of a module. Please do so or the compiler wont be able to help you in the future. While naming immutable constructs quite often, the docs don’t explain why we use them. Concurrency and thus threading is very easy to add to a program. Testing it is hard.

I don’t have a solution to those problems but I’m pretty sure we need one or they will haunt us the next 100 years.

Categories: Raku

Exceptionally colourful

August 23, 2020 4 comments

STDERR is often (ab)used for printing debug or status information. This can create clutter which in turn hides the important stuff. I want to print the essential stuff in exceptions in red unless a dynvar or environment variable is set.

class Explode is Exception {
    method message {
        put "$*dynvar is bad‼";
    }
}

sub e() {
    await start {
        Explode.new.throw;
    }
    CATCH { default { put .message } }
}

my $*dynvar = 'foo';
e();

# OUTPUT: foo is bad‼

We can access a dynvar inside the method of an exception from within an exception handler. In Shell::Piping error handling is a bit more involved. The biggest issue is fail because the enclosed exception is thrown by some routine in CORE about two steps down the call tree seen from the implicit or explicit MAIN sub. The dynvar is simply not there at this point in time. Luckily instances of Exception tend not to be long lived so we can get away with capturing the state of a dynvar at object creation. A good place to do so is TWEAK.

sub infix:<///>(\a, \b) is raw {
    my $dyn-name = a.VAR.name;
    my $has-outer-dynvar = CALLER::CALLERS::{$dyn-name}:exists;
    CALLER::{$dyn-name} = $has-outer-dynvar ?? CALLER::CALLERS::{$dyn-name} !! b
}

role Exception::Colored is Exception is export {
    has $.color;
    submethod TWEAK {
        my $*colored-exceptions /// on;
        $!color = $*colored-exceptions ~~ on && $env-color ?? 31 !! 0;
    }
    method RED($str) {
        $*ERR.t ?? ("\e[" ~ $.color ~ 'm' ~ $str ~ "\e[0m") !! $str
    }
}

Now I can use $.RED in .message of any exception that is Exception::Colored.

To have a look at the full stack was very helpful to figure out why the dynvar wasn’t there in some cases. For such cases I have a context sensitive binding in my .vimrc.

nmap <F1> :w<CR>:!raku -I ./lib %<CR>
imap <F1> <esc>:w<CR>:!raku --ll-exception -I ./lib %<CR>

In insert mode F1 will write the file and run Rakudo with an additional parameter. This results in a full stack trace.

foo failed
   at SETTING::src/core.c/Exception.pm6:62  (/usr/local/src/rakudo/install/share/perl6/runtime/CORE.c.setting.moarvm:throw)
 from SETTING::src/core.c/Failure.pm6:56  (/usr/local/src/rakudo/install/share/perl6/runtime/CORE.c.setting.moarvm:throw)
 from SETTING::src/core.c/Failure.pm6:111  (/usr/local/src/rakudo/install/share/perl6/runtime/CORE.c.setting.moarvm:sink)
 from /home/dex/tmp/tmp-2.raku:56  (<ephemeral file>:<unit>)
 from /home/dex/tmp/tmp-2.raku:1  (<ephemeral file>:<unit-outer>)
 from gen/moar/stage2/NQPHLL.nqp:1948  (/usr/local/src/rakudo/install/share/nqp/lib/NQPHLL.moarvm:eval)
 from gen/moar/stage2/NQPHLL.nqp:2153  (/usr/local/src/rakudo/install/share/nqp/lib/NQPHLL.moarvm:evalfiles)
 from gen/moar/stage2/NQPHLL.nqp:2113  (/usr/local/src/rakudo/install/share/nqp/lib/NQPHLL.moarvm:command_eval)
 from gen/moar/Compiler.nqp:60  (/usr/local/src/rakudo/install/share/perl6/lib/Perl6/Compiler.moarvm:command_eval)
 from gen/moar/stage2/NQPHLL.nqp:2038  (/usr/local/src/rakudo/install/share/nqp/lib/NQPHLL.moarvm:command_line)
 from gen/moar/rakudo.nqp:116  (/usr/local/src/rakudo/install/share/perl6/runtime/perl6.moarvm:MAIN)
 from gen/moar/rakudo.nqp:1  (/usr/local/src/rakudo/install/share/perl6/runtime/perl6.moarvm:<mainline>)
 from <unknown>:1  (/usr/local/src/rakudo/install/share/perl6/runtime/perl6.moarvm:<main>)
 from <unknown>:1  (/usr/local/src/rakudo/install/share/perl6/runtime/perl6.moarvm:<entry>)

As you can see there are quite a few things called before your script will be executed. Luckily Rakudo is implementing Raku in Raku so we have a chance to see what is going on.

Categories: Raku

Defined or dynvar

August 17, 2020 1 comment

While adding dynvars to Shell::Piping to reduce the risk of finger injury I made a typo lizmat kindly corrected. She suggested to use the defined-or operator to test if a given dynamic variable is declared.

($*FOO // '') eq 'yes'

This is not equivalent to test if a dynvar was declared down the call tree. For that we need to check CALLERS.

say CALLERS::<$*colored-exceptions>:exists;
dd CALLERS::<$*colored-exceptions>;
# OUTPUT: False
#         Nil

In case the dynvar is declared we get a different result.

sub dyn-receiver {
    say CALLERS::<$*colored-exceptions>:exists;
    dd CALLERS::<$*colored-exceptions>;
}
my $*colored-exceptions;
dyn-receiver();
# OUTPUT : True
#          Any $*colored-exceptions = Any

For a module author that means we can have somebody sneak some undefined value into a dynvar we use, that has a type we don’t expect. Composebility is not the same thing as correctness. If we want do deal with this situation properly we need to check if the caller declared the dynvar and use a proper default value if they don’t.

class Switch {
    has $.name;
    method gist { $.name }
    method Str { die('invalid coersion') }
    method Bool { die('invalid coersion') }
}

constant on is export := Switch.new: :name<on>;
constant off is export := Switch.new: :name<off>;

sub dyn-receiver {
    my $*colored-exceptions = CALLERS::<$*colored-exceptions>:exists 
        ?? CALLERS::<$*colored-exceptions>
        !! off
}

In this example there are just two possible values but if there are more and they can be undefined we need to be more careful. However, this is quite a bit of typing. Can we use a deboilerplater here?

sub infix:<///>(\a, \b) is raw {
    my $dyn-name = a.VAR.name;
    my $has-outer-dynvar = CALLER::CALLERS::{$dyn-name}:exists;
    CALLER::{$dyn-name} = $has-outer-dynvar ?? CALLER::CALLERS::{$dyn-name} !! b
}

sub c {
    my $*colored-exceptions /// Int;
    dd $*colored-exceptions;
}

sub d {
    my $*colored-exceptions = Str;
    c();
}

c();
d();
# OUTPUT: Int $*colored-exceptions = Int
          Str $*colored-exceptions = Str

This operator takes two bound arguments. If we call it with a dynvar a contains the container that is the dynvar. We can query the name of that container and us it to check if down the call tree the dynvar was already declared. If so we use its value and assign it directly into the dynvar declared in c. Otherwise we assign b to the dynvar. In both cases we might return something naughty so we better do so raw.

Poking across the stack is risky. This could be done better with proper macros. I am quite sure we can do so after Christmas*.

*) For any value greater then Christmas last year.

Categories: Raku

Guarding dynamics

August 14, 2020 1 comment

Dynamic variables are a nice way to get the benefits of global variables without taking the drawbacks. They pass information up the call tree without forcing a declaration upon the caller. However, dynvars share a burden with exceptions. The callee knows how to do what the caller might not expect.

use Module::By::Author::A;
use Module::By::Author::B;

my $*dynvar = 42;

sub-from-A(); # expects $*dynvar to be Int
sub-from-B(); # expects $*dynvar to be IO(Str)
sub-from-C(); # expects $*dynvar to be IO::Handle

In this example sub-from-B() will silently fail until it tries to open the file named “42”. While sub-from-C() will try to coerce 42 to become a file handle and throw. So there lies a danger in dynvars expected to be set by independent modules. Worse, the behaviour might suddenly pop up after any zef --install. Raku is a dynamic language that will try its best to convert values automatically and fail at runtime. It is good advice to support the compiler by failing early.

I want to use dynvars in a few modules to provide semi-global flags to remove boilerplate. The following seems to provide protection against unintentional dynvar-spill over.

class Switch is Mu {
    has $.name;
    method gist { $.name }
    method Str { die('invalid coersion') }
}

constant on = Switch.new: :name<on>;
constant off = Switch.new: :name<off>;

sub s() {
    # put $*d; # this will die
    say $*d;
    dd $*d;
}
my $*d = off;
s();
# OUTPUT: off
#         Switch $*d = Switch.new(name => "off")

I derive from Mu to get rid of all the coercers from Cool and overload Str to take that one out of the loop as well. Using say for anything but debugging is a bug anyway, so I might support it properly with .gist. Since I create a type with the class I can then go and protect my subs and methods with a whereception.

sub dyn-var-typecheck(Mu:U \T, $name) {
    sub {
        DYNAMIC::($name) ~~ T || die(„$name is set to an unexpected value“)
    }
}

constant &dyn-s-typecheck = dyn-var-typecheck(Switch, ‚$*d‘);

sub s($? where dyn-s-typecheck) { }
# OUTPUT: $*d is set to an unexpected value
#         in sub  at /home/dex/projects/blog/shielded-dynvars.raku line 6
#         in sub s at /home/dex/projects/blog/shielded-dynvars.raku line 22
#         in block <unit> at /home/dex/projects/blog/shielded-dynvars.raku line 29

Using the DYNAMIC::($name) in a where clause will slow down any call on MMD. So pulling the check into the sub might be reasonable.

With this measure I feel better to add $*always-capture-stderr to Shell::Piping to get rid of :stderr(Capture) on pretty much any pipe I start. And I feel a lot better when adding $*debug in everywhere.

Raku was not designed with type checks on dynvars in mind. It was designed to be a Perl. That gives us the flexibility to fix problems as they occur.

Categories: Raku

Whereceptions

August 9, 2020 1 comment

I have a sub that takes a file and tries to guard itself against a file that does not exist. Where clauses don’t make good error messages.

sub run-test(IO() $file where .e & .f) { };
run-test('not-there.txt');
# OUTPUT:
# Constraint type check failed in binding to parameter '$file'; expected anonymous constraint to be met but got IO::Path (IO::Path.new("not-th...)

The signature of that sub is quite expressive. Often we don’t have time to read code to hunt down mistakes. That’s why bad error messages are LTA. We can extend any where clause with a die or fail to provide a better message.

sub run-test(IO() $file where .e && .f || fail("file $_ not found")) { };
run-test('not-there.txt');
# OUTPUT:
# file not-there.txt not found

We can also throw exceptions of cause. With just one parameter the signature has gotten quite long already. Also, when working with many files we will write the same code over and over again. Since we don’t code in Java we would rather not to.

my &it-is-a-file = -> IO() $_ {
    .e && .f || fail (X::IO::FileNotFound.new(:path(.Str)))
}
sub run-test(IO::Path $file where &it-is-a-file) { }

That’s much better. Since exceptions are classes we can reuse code amongst them. Like coloured output and checking for broken symlinks.

class X::Whereception is Exception is export {
    has $.stale-symlink is rw;
    method is-dangling-symlink {
        $.stale-symlink = do with $.path { .IO.l & !.IO.e };
    }
}

class X::IO::FileNotFound is X::Whereception is export {
    has $.path;
    method message {
        RED $.is-dangling-symlink ?? „The file ⟨$.path⟩ is a dangling symlink.“ !! „The file ⟨$.path⟩ was not found.“
    }
}

I have added a few to Shell::Piping. Suggestions what else to check for are very welcome.

Categories: Raku

Dropin replacement

August 1, 2020 1 comment

Today I learned that whereis can take multiple commands to look for in $PATH.

$ whereis not-there raku-test-all zef
not-there:
raku-test-all: /home/dex/bin/raku-test-all
zef: /usr/local/src/rakudo/install/share/perl6/site/bin/zef

The results are always in the requested order. So we can use a shell spell to find a candidate to be used as our tester in a Makefile.

TESTER := $(shell whereis raku-test-all zef | cut -d ' ' -f 2 -s | head -n 1)

install-deps:
        zef --depsonly install .

test: install-deps
        $(TESTER) --verbose test .

install:
        zef install .

all: test

push: test
        git push

I had to do a few changes to raku-test-all so it mimics the interface of zef. The idea behind this Makefile is to be able to hit F6 and have all tests run and then push to github. Our current ecosystem simply distributes links to github repos. As a result pushing without testing can lead to somebody else cloning a broken repo (given the timing is bad enough). As you might imagine any speed up to testing is very welcome. Travis is quite slow. I think I should work in that area next.

Categories: Raku, shell

Wrapping Exceptions

August 1, 2020 1 comment

As stated before I would like to reduce simple error handling in Shell::Piping. The following is just way to long for putting a single line into a terminal.

CATCH {
    when X::Shell::CommandNotFound {
        when .cmd ~~ ‚commonmarker‘ {
            put ‚Please install commonmarker with `gem install commonmarker`.‘;
            exit 2;
        }
        default {
            .rethrow;
        }
    }
}

We have a conditional in line 3 and a print statement in line 4. The rest is boilderplate. The shortest syntax I came up with looks like the following.

X::Shell::CommandNotFound.refine({.cmd eq ‚commonmarker‘}, {„Please install ⟨{.cmd}⟩ in ⟨{.path}⟩.“});
X::Shell::CommandNotFound.refine({.cmd eq ‚raku‘}, {„Please run `apt install rakudo`.“});

I want to modify the type object behind CommandNotFound because the module will create instances of them and throw them before I can intercept them. Of cause I could catch them. But the whole excercise is done to get rid of the CATCH block. Adding a method to any exception both inside a module, and thus at compile time, and at runtime would be nice. We can do so with a trait.

multi sub trait_mod:<is>(Exception:U $ex, :$refineable) {
    $ex.HOW.add_method($ex, ‚refine‘, my method (&cond, &message) {
        $ex.WHO::<@refinements>.append: &cond, &message;
        state $wrapped;
        $ex.HOW.find_method($ex, ‚message‘).wrap(my method {
            my @refinements := self.WHO::<@refinements>;
            for @refinements -> &cond, &message {
                if cond(self) {
                    return message(self);
                }
            }
            nextsame
        }) unless $wrapped;
        $wrapped = True;
    });
}

A trait is a fancy sub that takes type objects or other stat-ish (Raku is a dynamic language without static things) objects and does stuff with it. That can be wrapping – a MOP operation in disguise – or actual MOP-calls. This redefining trait adds a class attribute named @refinements via autovification on the package-part of the type. This is hidden behind the pseudo method .WHO. Then it adds a method that takes a pair of Callables and stores them in @refinements. The type object’s original .message method is wrapped unless already done so. This is controlled by a state variable.

The wrapped .message will call the first callable and expects a Bool in return. If that one is True is will call the 2nd callable. Both are called with an exception instance.

The trait does not .^compose the type object it is called on. This is (for now) needed because of the following case.

class X::Shell::CommandNotFound is Exception is refineable { ... }

The trait is actually called on Exception. That works because the added method .refine is not shadowed by anything. We can therefore use this method to wait for it to be called to finish what we need to do after compile time. The wrapper has a fall through via nextsame if no condition has matched.

I don’t think I got all corner cases covered yet. That will need further testing. The user facing syntax can stay the way it is because traits are deboilerplaters. The trait itself is likely going to get more complex. But that is fine. The burden is supposed to be on the side of the implementer.

Categories: Raku