Sparametryzowane typy w Raku, jak używać wartości czasu wykonywania jako parametrów

Nov 28 2020

Chciałbym stworzyć sparametryzowane typy dla Raku; w zasadzie chciałbym stworzyć kilka różnych klas, których główną różnicą byłby zakres wartości jednego z jego atrybutów; na przykład klasy reprezentują typy budynków, chciałbym mieć różne klasy dla budynków z 3 lub dowolną inną liczbą pięter. Więc to najlepsze, o czym mogłem pomyśleć:

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;

Jest to oczywiście niepraktyczne, gdy trzeba zadbać o wiele różnych ilości pięter (nie tutaj, w Granadzie, gdzie mają co najwyżej 10, jak sądzę, ale cóż ...). Problem polega na tym, że po prostu musisz mieć informacje o podzbiorach w czasie kompilacji, więc jeśli nie używasz makr (nadal eksperymentalnych), nie ma możliwości użycia jakiejkolwiek zmiennej. Czy możesz więc pomyśleć o praktycznym sposobie zdefiniowania tego rodzaju curried ról dla dowolnej wartości parametru?

Odpowiedzi

8 user0721090601 Nov 28 2020 at 15:51

Właściwie, w przeciwieństwie do tego, co powiedziałem w moim poprzednim, możesz używać warunków, w których klauzule bez problemu, wystarczy je zamknąć w nawiasach:

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

Powinno to obejmować wszystkie typy przydziałów

5 raiph Nov 28 2020 at 04:15

Mam bardzo ograniczone kotlety MOP, a poniższe wydaje się brzydkie, ale działa i może być krokiem we właściwym kierunku.

Co ja zrobiłem:

  • Dynamicznie skonstruowano tablicę 10 000 subsetsekund za pomocą MOP.

  • Czas przesunął ich konstrukcję do czasu kompilacji za pośrednictwem BEGIN.

  • Użyto odpowiedniego elementu z tablicy do sparametryzowania roli.

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 
3 user0721090601 Nov 28 2020 at 01:40

Próbowałem użyć anonimowych whereklauzul, ale podobnie bezskutecznie, ale wyśledziłem problem: whereklauzula jest najwyraźniej ignorowana przez BUILDmetodę. Nie jestem pewien, czy to dlatego, że ma bezpośredni dostęp (przez $!floor), który omija whereklauzulę, czy też dzieje się coś innego dziwnego (prawdopodobnie to drugie, generalnie otrzymałem, Nilgdy próbowałem użyć wartości sparametryzowanej w whereklauzuli).

Niemniej powinno to działać dobrze, w tym podając pomocny komunikat o błędzie:

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

Możesz zobaczyć, jak łatwo byłoby to zmodyfikować, jeśli możesz założyć, że podłogi są zawsze 0 .. x, lub możesz x .. ypodać jeszcze bardziej pomocny komunikat o błędzie.

1 raiph Nov 29 2020 at 21:38

Odpowiedź dotycząca sprawy: czytelnik zna Javę, ale nie zna Raku.

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

sparametryzowane typy dla Raku

Powiązany przykład Java to:

Wystąpienie typu ogólnego z rzeczywistymi argumentami typu jest nazywane typem sparametryzowanym. Przykład (typu sparametryzowanego):

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

Rozsądnym analogiem Raku jest:

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

PositionalTyp to parametryzowalny rola . Rola określa interfejs i / lub częściową implementację typu. Uważam, że Raku Positionaljest wystarczająco analogiczny do Java, Collectionże służy do celów tej odpowiedzi.

ArrayTyp to parametryzowalny klasa . Określa strukturę danych, która jest zgodna z Positionalrolą. To nie jest lista z linkami, ale wystarczy do tego, by odpowiedzieć na nie.