Tripping over variables
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.