出于学习目的,我尝试将此 F# 代码翻译为 Raku:
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
到目前为止,在 Raku 中,我有这个工作代码(但它通常不可链接):
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);
问题是我无法弄清楚如何使链接操作作为中缀工作。
我已经尝试过这个尝试:
sub infix:<xx>( $burrito, &f ) {
-> $meat { &f( $meat, $burrito ) }
}
say ($burrito xx &add-meat);
say ($burrito xx &add-meat)(Beef);
但它给出了错误:
(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
这是更正后的代码,消除了使用
xx
作为占位符...
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])
总体而言,我认为 F# 的这种相当快速的翻译非常忠实于最初的意图,尽管摘要中使用
Empty
和 LISP 风格的括号可能是可以通过更多思考来做得更好的领域。