I a trying to translate this F# code to Raku for learning purposes:
type Meat = Chicken | Beef | Pork | Fish | Veggie
type Ingredient =
| Cheese | Rice | Beans | Salsa | Guacamole | SourCream | Lettuce
| Tomato | Onion | Cilantro | PicoDeGallo
type Burrito = Meat option * Ingredient list
let (>>=) burrito f =
match burrito with
| Some meat, ingredients -> f (Some meat, ingredients)
| None, _ -> None, []
let returnBurrito (meat, ingredients) = meat, ingredients
let tortilla = returnBurrito (Some Veggie, [])
let addMeat meat (m, ingredients) = Some meat, ingredients
let addIngredient ingredient (meat, ingredients) =
meat, ingredient :: ingredients
let addMissionBurritoIngredients (meat, ingredients) =
meat, Cheese :: Rice :: Beans :: ingredients
let holdThe ingredient (meat, ingredients) =
meat, List.filter (fun i -> i <> ingredient) ingredients
let burrito =
tortilla
>>= addMeat Chicken
>>= addMissionBurritoIngredients
>>= holdThe Cheese
>>= addIngredient PicoDeGallo
>>= addIngredient Salsa
>>= addIngredient Guacamole
>>= addIngredient SourCream
printfn "%A" burrito
So far, in Raku, I have this working code (but it is not generally chainable):
use Monad::Result;
enum Meat <Chicken Beef Pork Fish Veggie>;
enum Ingredient <Cheese Rice Beans Salsa Guacamole SourCream Lettuce Tomato Onion Cilantro PicoDeGallo>;
sub returnBurrito($meat, @ingredients) { $meat, @ingredients }
sub tortilla { returnBurrito( Monad::Result.ok(Veggie), [] ) }
sub add-meat($meat, ($, @ingredients)) { Monad::Result.ok($meat), @ingredients }
sub add-ingredient($ingredient, ($meat, @ingredients)) {
$meat, [$ingredient, |@ingredients]
}
sub add-mission-burrito-ingredients(($meat, @ingredients)) {
$meat, [Cheese, Rice, Beans, |@ingredients]
}
sub hold-the($ingredient, ($meat, @ingredients)) {
($meat, [@ingredients.grep(* != $ingredient)]);
}
my $burrito = tortilla;
say $burrito;
sub zz( $burrito, &f ) {
-> $meat { &f( $meat, $burrito ) }
}
say zz($burrito, &add-meat);
say zz($burrito, &add-meat)(Beef);
The problem is that I cannot figure out how to make the chaining operation work as an infix.
I have tried this attempt:
sub infix:<xx>( $burrito, &f ) {
-> $meat { &f( $meat, $burrito ) }
}
say ($burrito xx &add-meat);
say ($burrito xx &add-meat)(Beef);
But it gives the error:
(Ok ( Veggie ) [])
-> $meat { #`(Block|5635547097056) ... }
Cannot unpack or Capture `Code.new`.
To create a Capture, add parentheses: \(...)
If unpacking in a signature, perhaps you needlessly used parentheses? -> ($x) {} vs. -> $x {}
or missed `:` in signature unpacking? -> &c:(Int) {}
in sub add-meat at burrito.raku line 11
in block <unit> at burrito.raku line 29
Here is the corrected code that eliminates the use of xx
as a placeholder...
use Monad::Result;
enum Meat <Chicken Beef Pork Fish Veggie>;
enum Ingredient <Cheese Rice Beans Salsa Guacamole SourCream Lettuce Tomato Onion Cilantro PicoDeGallo>;
sub returnBurrito($meat, @ingredients) { $meat, @ingredients }
sub tortilla { returnBurrito( Monad::Result.ok(Veggie), [] ) }
sub add-meat($meat, ($, @ingredients)) { Monad::Result.ok($meat), @ingredients }
sub add-ingredient($ingredient, ($meat, @ingredients)) {
$meat, [$ingredient, |@ingredients]
}
sub add-mission-burrito-ingredients(($meat, @ingredients)) {
$meat, [Cheese, Rice, Beans, |@ingredients]
}
sub hold-the($ingredient, ($meat, @ingredients)) {
($meat, [@ingredients.grep(* != $ingredient)]);
}
sub infix:«>>=»( $burrito, &f ) {
given $burrito {
when *.is-ok, $ {
-> $arg=Empty { &f( |($arg, $burrito) ) }
}
when *.is-error, $ {
(Monad::Result.error('None'), [])
}
}
}
my \burrito =
(((((((tortilla()
>>= &add-meat)(Chicken)
>>= &add-mission-burrito-ingredients)()
>>= &hold-the)(Cheese)
>>= &add-ingredient)(PicoDeGallo)
>>= &add-ingredient)(Salsa)
>>= &add-ingredient)(Guacamole)
>>= &add-ingredient)(SourCream)
;
say burrito;
#(Ok ( Chicken ) [SourCream Guacamole Salsa PicoDeGallo Rice Beans])
Overall I think that this fairly quick translation of the F# is quite true to the original intent, although the use of Empty
and the LISP style brackets in the summary are probably areas that could be done better with some more thought.