Archive

Archive for January, 2016

Now they made Array a container!

January 29, 2016 Leave a comment

What means it comes with a default. The default default happens to be Any.

my @a;
put @a[0].perl;

# OUTPUT«Any␤»

The default type constraint is also Any. So we can happily assign any value to any slot in an Array. When reading values from a Positional we will get Any for unassingned slots too, what can be dangerous or cumbersome if we need to check for definedness by hand. In fact, we will have problems to tell if some undefined value was returned (unless we give the Array a type).

my @a is default(Nil); 
put @a[0]; 
say "alive";

# OUTPUT«Use of Nil in string context  in block  at /tmp/_9ikYUP8Qj line 1␤␤alive␤»

Now we get a warning if we turn it into a string and we can test against it. Still it may slip through a crack and corrupt some data.

my @a is default(Failure.new);
put @a[0]; 
say "alive";

# OUTPUT«===SORRY!===␤Failed␤»

Now it dies properly. The lack of a stacktrace is a bug that we can work around.

my @a is default(Failure.new('index out of range'));
put @a[0];
say "alive";
CATCH { default { put .message; put .backtrace.Str } }

# OUTPUT «index out of range␤  in any  at src/Perl6/World.nqp line 2004␤  in any trait_mod:sym at gen/moar/m-Perl6-Actions.nqp line 5060␤  in any trait_mod:sym at src/Perl6/Grammar.nqp line 3101␤  in any trait_mod at /home/camelia/rakudo-m-inst-1/sha…»

The same works for any container, including those in signatures.

sub f($s = Failure.new){ say $s };
f;

# OUTPUT«Failed␤  in sub f at /tmp/DBYRCggtFf line 1␤  in block  at /tmp/DBYRCggtFf line 1␤␤Actually thrown at:␤  in sub f at /tmp/DBYRCggtFf line 1␤  in block  at /tmp/DBYRCggtFf line 1␤␤»
Advertisements
Categories: Uncategorized

Keep your types safe and sound

January 24, 2016 1 comment

Typesafety can safe your bum. Both by repelling evildoers who want to inject and by repelling bugs that want to make the users of your code unhappy. Luckily Perl 6 can help here with a fairly nice type-system and operators that don’t make many assumtions about their operands. Since type-checks are done at runtime (mostly) they come with a cost. I wanted to know how big that cost is in a real-life example.

To ease my finger-hurt I wrote a little module that can output XHTML called XHTML::Writer. Having a dropin replacement that prevents the most basic injection woes would be nice. Before I could write that I needed a type for the type-system to work with.

Enter: Typesafe::HTML

class HTML is export {
    has $.the-str is rw;
    method new (Str $s = '') { self.bless(the-str=>$s) }
    method Str () is nodal { $.the-str }
    method perl (){ "HTML.new('{$.the-str.subst(Q{\}, Q{\\}, :g).subst(<'>, <\'>, :g).subst("\n",Q{\\n}, :g)}');"; }
    proto method utf8-to-htmlentity (|) is export {*};
    multi method utf8-to-htmlentity (Str:D \s) is nodal {
        s.subst('&', '&amp;', :g).subst('<', '&lt;', :g)
    }
    multi method utf8-to-htmlentity (Str:D @a) is nodal {
        @a>>.utf8-to-htmlentity()
    }
    multi method utf8-to-htmlentity (HTML:D \h) is nodal {
        h
    }
}

This class can hold a Str that can contain bare HTML or stuff that is ment to look like HTML but should not be rendered as such. We do the latter with quoting with HTML-entities. We can set bare HTML either by accessing $.the-str (I don’t believe in shotguns) or via the constructor .new. The method utf8-to-htmlentity is ment to quote HTML. For now there is no actually typesafety done. For that we need the right operator.

multi sub infix:<~>(HTML:D \l, Str:D \r) is export {
    l.new( l.Str ~ l.utf8-to-htmlentity(r) );
}

multi sub infix:<~>(HTML:D \l, HTML:D \r) is export {
    l.new( l.the-str ~ r.the-str );
}

multi sub infix:<~>(Str:D \l, HTML:D \r) is export {
    r.new( r.utf8-to-htmlentity(l) ~ r.Str );
}

Those three fellow will get any Str quoted that is concatenated with unquoted HTML, if either operand is of type HTML already.

use Typesafe::HTML;

my $quoted = HTML.new ~ '<br>'; # this will be '&lt;br>'

It’s a bit unwieldy to create an emtpy HTML string. Most of the time we don’t want to quote a loose quoted string anyway.

use Typesafe::HTML;
use Typesafe::XHTML::Writer :p, br;

my $mixed = p(class=>'quotedhtml', '<br/>');
# q{<p class="quotedhtml">&lt;br></p>}

my $also-mixed = br ~ '<br/>' ~ br;
# q{<br/>&lt;br><br/>}

Typesafe::XHTML::Writer comes with a little helper called xhtml-skeleton that will surround it’s arguments with XHTML1.1 boilerplate and will convert anything that isn’t HTML to quoted xhtml.

Next step was to find out how big the performence hit for using all those type-checks would be. A little script provided plenty of HTML generating function calls. With 50000 tags it takes about 284s to get the script compiled and 73.1s to execute. The typesafe variant takes 2s longer to compile (could be noise) and 82.8s to execute, what is a 11.7% performance hit. Given that any real-world script would likely spend most time doing something else then generating HTML we can assume that 11.7% is the upper boundary in any case. I asked jnthn if he expects type checks to get faster with more work on Rakudo. The answer was: “yes“.

Categories: Perl6

A story of export and indirection

January 18, 2016 1 comment

While playing with Typesafe::XHTML::Writer I found myself wanting to allow the user of the module to provide an alternate class to handle the identity of typesafe HTML-fragments. Simply replacing Typesafe::HTML by uninstalling mine and installing their own would work. That sounds wrong and un-Perl6ish. To find a better way to do that I hit The Docs, found them lacking and tried to improve them. The result still wasn’t that nice. I simply lacked the knowledge to do it properly. Any lack of knowledge can be solved by the spec, that does define the whole process nicely but there are not that many test. Since Rakudo is test driven I had the suspicion that the untested stuff wouldn’t work. Three bug reports later I was sure that some parts of the spec are not implemented yet. There is no point in telling you what is missing, the bug reports are in the public domain. Instead I would like to share what I found working. If you are Japanese, Chinese or German, you will love the next part.

EXPORT All The Things

I’m not shouting at you, the function that is used to extend the default, trait driven way to export symbols, is called EXPORT. The capitals come from the fact that you are not ment to call that function. As any Phaser it’s called by Rakudo at the right time. This time, that time is at compile time when a use statement with a positional parameter is called. Any named parameters will not end up in the EXPORT function. Getting hold of the tags inside EXPORT is the first entry on my v6.d-wishlist. The return value of EXPORT must be a hash (that can be anonymous) with pairs of symbols with their respective sigils and a reference to the thing that is exported.

To provide the ability to change the type Typesafe::XHTML::Writer is returning I wanted to use type captures. Type captures work in Roles and Routines quite nicely.

sub foo(::T $t){ # return the same type we got fed
    dd T.^name;
    dd $t.WHAT.name;
}

my Int $i = 42;
foo($i)

# OUTPUT«"Int"␤Int␤»

When it comes to EXPORT things get complicated. Type captures take a type and store it for later use. When used in a thunk like a where clause or a anonymous sub, the later use is respected by Rakudo.

# in some module
sub EXPORT(::T = BaseClass) {
    { # anon Hash to be returned by &EXPORT
        '&consumer' => sub (T $c) {
            dd $c.^name
            T.new
        }
    }
}

#in some script
consumer(Int.new);

# OUTPUT«"Int"␤»

Multi subs can’t be anonymous, so the type capture would not be resolved inside EXPORT. We can store the type in a container and use it inside a where clause though. We lose introspection but typesafe it will be.

sub EXPORT(::T = BaseClass) {
    my $T = T;

    multi sub amulti($t where * ~~ $T) { say "$?FILE:$?LINE: amulti(T): ", $t.^name }
    multi sub amulti($t where * ~~ BaseClass) { say "$?FILE:$?LINE: amulti(BaseClass): ", $t.^name }
    { # anon Hash
        '&amulti' => &amulti,
    }
}

With the type stored in $T we can do any runtime type check we need and use it as an object factory. For my usecase that’s all I need. I can use a user defined class instead of my own until the type capture bugs are fixed. With any luck a :%s/$T/T/g is all I need to get rid of the workaround.

The complete example can be found in a gist.

Categories: Perl6

Empty is considered useful

January 8, 2016 Leave a comment

I found myself having to convert the arguments of a sub with plenty of optional named arguments into a hash to be passed on into another function.

sub f(:a?, :b?){
    my %h = 
        $a ?? a=>$a !! Emtpy, 
        $b ?? b=>$b !! Emtpy;
}

dd f(b=>1); # Hash %h = {:b(1)}

As it turns out, Empty is skipped when the list of Pairs is turned into a Hash. The short form of Emtpy is |(), a Slip of the empty list. Unless you love Lisp, that may be a little hard to read.

Categories: Perl6