TransWikia.com

Parametrized types in Raku, how to use run time values as parameters

Stack Overflow Asked by jjmerelo on February 24, 2021

I’d like to create some parametrized types for Raku; basically, I’d like to create some different classes whose main difference would be the range of values of one of its attributes; for instance, classes represent types of building, I’d like to have different classes for buildings with 3 or any other number of floors.
So this is the best I could think of:

subset Two-Tops of UInt where * <=2;
subset Three-Tops of UInt where * <=3;

role Zipi[ ::Capper ] {
    has Capper $.floor;
}
    
class Capped-at-three does Zipi[Three-Tops] {}


my $capped = Capped-at-three.new( floor => 2 );
say $capped.raku;

This is clearly unpractical as soon as you need to take care of many different numbers of floors (not here in Granada, where they have at most 10, I think, but well… ). The problem here is basically you need to have the information for subsets at compile time, so unless you use macros (still experimental), there’s no way you can use any kind of variable. So can you think of a practical way of defining this kind of curried roles for any value of the parameter?

4 Answers

Actually, unlike I said in my previous you can use conditions in where clauses without problem, you just need to encase them in braces:

role Zipi[$condition] {
    has $.floor is rw where {$_ ~~ $condition}
    method foo($x) { $!floor = $x }
}

class A does Zipi[2 < * < 5] {
    method bar($x) { $.floor = $x }
}

#my $a = A.new( floor => 10); # error
my $a = A.new( floor => 4); # OK

#$a.foo(10); # error
$a.foo(3); # OK

#$a.bar(0); # error
$a.bar(4); # OK

#$a.floor = 9; # error
$a.floor = 3; # OK

That should cover all of the assignment types

Correct answer by user0721090601 on February 24, 2021

A nanswer covering the case a reader knows Java but not Raku.

Collection<String> coll = new LinkedList<String>();

parametrized types for Raku

The linked Java example is:

The instantiation of a generic type with actual type arguments is called a parameterized type. Example (of a parameterized type):

Collection<String> coll = new LinkedList<String>();

A reasonable Raku analog is:

my Positional[Str] coll = Array[Str].new;

The Positional type is a parameterizable role. A role specifies an interface and/or partial implementation of a type. I believe Raku's Positional is sufficiently analogous to Java's Collection that it serves for the purposes of this nanswer.

The Array type is a parameterizable class. It specifies a data structure that adheres to the Positional role. It isn't a linked list but it will suffice for the purposes of this nanswer.

Answered by raiph on February 24, 2021

I have very limited MOP chops, and the following seems ugly, but it works, and might be a step in the right direction.

What I've done:

  • Dynamically constructed an array of 10,000 subsets via the MOP.

  • Time shifted their construction to compile time via BEGIN.

  • Used an appropriate element from the array to parameterize the role.

my @max-floors-checkers; 
BEGIN {
  @max-floors-checkers = do for ^10_000 -> floors {
    Metamodel::SubsetHOW.new_type: 
      refinee => UInt,
      refinement => { $^floors <= floors }
    }
}

role BuildingCategory[ ::MaxFloorsCheck ] { has MaxFloorsCheck $.floors }
    
class Capped-at-three does BuildingCategory[ @max-floors-checkers[3] ] {}

my $capped3 = Capped-at-three.new( floors => 2 );
say $capped3.raku;                                # Capped-at-three.new(floors => 2

my $capped4 = Capped-at-three.new( floors => 4 ); # Type check failed 

Answered by raiph on February 24, 2021

I tried using anonymous where clauses, but similarly to no avail, but I tracked down the issue: the where clause is apparently being ignored by the BUILD method . I'm not sure if it's because it has direct access (via $!floor) which bypasses the where clause, or if something else weird is going on (probably the latter, I general got Nil if I tried to use the paramaterized value in a where clause).

Nonetheless, this should work nicely, including giving a helpful error message:

role Zipi[$condition] {
    has $.floor;

    submethod BUILD(:$floor, |c) {
        die "Invalid floor number."
            unless $floor ~~ $condition;
        $!floor = $floor;
    }
}

You can see how it'd be easy to modify if you can assume floors are always 0 .. x, or x .. y and could provide an even more helpful error message.

Answered by user0721090601 on February 24, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP