Archive

Archive for October, 2020

Planned obsolescence

October 26, 2020 1 comment

Twelve years¹ ago Larry planned the obsolescence of one of my modules. His cunning plan was executed by lizmat a fortnight ago. If you are building Rakudo from source you take another shortcut now.

use v6.e.PREVIEW;

my %detectives;

my @a = <UK London Bakerstreet>;
%detectives{||@a} = "Holms";

say %detectives{||<UK London Bakerstreet>};
dd %detectives;
# OUTPUT: Holms
          Hash %detectives = {:UK(${:London(${:Bakerstreet("Holms")})})}

Slippy semi lists can be quite useful while working with parsed JSON. There are plenty of hashes of hashes of hashes.

Now I got a peculiar problem. One of my modules becomes redundant if — and only if — a Raku version is requested that is younger then v6.d. The module itself can demand v6.d even when used by a program or module that itself demands v6.e. At least it is easy to test for that condition.

use v6.e.PREVIEW;
say $*RAKU.version ~~ v6.e+;
# OUTPUT: True

We can use that to trigger a warning in a module as soon as v6.e becomes the default.

use v6;

CHECK if $*RAKU.version ~~ v6.e+ { warn "Unattended items will be destroy without warning! (This is a warning.)" };

This warning will be displayed once per precompilation so it is easy to miss. Also it will show even thought the program running the use statement might demand an older version of Raku. If only there where a subroutine that is called every time we import a module in the context of the caller!

use v6.d;

sub EXPORT {
    if $*RAKU.version ~~ v6.e+ { warn "Unattended items will be destroy without warning! (This is a warning.)" };
    %()
}

Now use can detect time travel by means of looking up the dynamic variable $*RAKU.

use v6.e.PREVIEW;
use warner;

# OUTPUT: Unattended items will be destroy without warning! (This is a warning.)
          in sub EXPORT at /home/dex/projects/raku/obsolescence/lib/warner.rakumod (warner) line 6

The ability of Raku to fix past mistakes was planned right from the beginning.² I feel ready for the next 100 years.

¹) That’s a lie. The truth was hidden by the move from SVN to git. Please forgive my urge to tell a good story.
²) That’s not a lie.

Categories: Raku

Sneaky Arguments

October 10, 2020 1 comment

While playing with .WHY I stepped onto an ENODOC. Method refers to Signature. Both fail to mention the default signature of a method. The documentation of automatic signatures fails to mention that %_ is always added to methods. Asking greppable6 for mentioning of it revealed plenty of redundant uses of *%_ in methods.

my method m1(:$named) {}
my method m2(:$named, *%_) {}

say &m1.signature;
say &m2.signature;
# OUTPUT: (Mu: :$named, *%_)
          (Mu: :$named, *%_)

The reason why I spotted this is .WHY. In Raku we can add comments that are attached to the language objects that follow them.

class Commented {
    #| this is an attribute
    has $.a-documented-attribute;
    #| this is a method
    method works {
        say ‚working as intended‘;
    }
}
say $c.^can('works')[0].WHY;
say $c.^attributes[0].WHY;
# OUTPUT: this is a method
          this is an attribute

The docs fail to state how to access WHY-nodes for attributes and methods at runtime. We have to go through the MOP here because methods and attributes can be added to a type object at runtime. At least for methods I found a nicer way to handle this.

method works {
    return &?ROUTINE.WHY if %_;
    say ‚working as intended‘;
}
put $c.works(:why);

Oddly, this leads to the awkward situation that I add an undocumented argument to a method that returns the documentation for that method. To be explicit we got the trait is implementation-detail. Being forced to use a multi makes things more cumbersome though.

#| No comment! We are sneaky!
multi method sneaky { say ‚working as intended‘ }
multi method sneaky('why') is implementation-detail {
    return self.^lookup('sneaky').candidates[0].WHY;
}
say $c.sneaky('why');

That leaves the question why I need .WHY on attributes. I do agree with the assessment that JSON is lacking in general. My biggest objection is the lack of comments. For config files being able to tell a co-worker to keep his dirty fingers of a certain line can save lives. Debian is making good use of comments in such files too. It can save a lot of time if you don’t have to read a man page just to change one value. With inline comments on attributes I could express both the structure of data stored in a config file and instructions how to use the config file without another HEREDOC that can get out if sync with the actual code. I really like the structure of NestedText and am playing with adding type information. In NestedText leafs default to Str so that can be omitted. In Raku we get string representations for types for free, we can just add them as text.

I can define the structure of my config file as follows.

use Data::TypedNestedText;

class Phone {
has $.mobile;
has $.home;
has $.office;
}

class Contact {
has $.name;
#| this can be multiline
has $.address;
has $.phone;
has @.additional-roles;
}

For the output I wrote a naive deparser that can produce a NestedText with type annotations.

president[Contact]:
# this can be multiline
address:
> 138 Almond Street
> Topika, Kansas 20697
name: Katheryn McDaniel
phone[Phone]:
home: 1-210-555-8470
mobile: 1-210-555-5297
treasurer[Contact]:
# this can be multiline
address:
> 3636 Buffalo Ave
> Topika, Kansas 20692
name: Fumiko Purvis
phone[Phone]:
office: 1-268-555-0280
additional-roles:
- hug task force
- Christmas party team

This is much easier to read and edit then JSON. Writing the parse might not. I will report back shortly if it turns out to be easier then expected.

Categories: Raku