Raku中类似Haskell的模式匹配
Haskell 和 Rust(以及我不知道的其他语言)有一个他们称之为“模式匹配”的发展。下面是 Haskell 中的一个例子:
data Event = HoldKey Char | PressKey Char | Err String
someFunc = let
someEvent <- doSomeStuff
-- What follows is a case expression using pattern matching
thingINeed <- case someEvent of
HoldKey keySym -> process keySym
PressKey keySym -> process keySym
Err err -> exit err
in keepDoingStuff
在 Raku 中与此最接近的似乎是 多方法 multifunctions(术语在下面的答案中固定,但 multimethods 也可以工作)。
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
multi process(Hold (:$key)) { say $key; }
multi process(Press (:$key)) { say $key; }
multi process(Err (:$msg)) { say $msg; }
但是,如果我想要一个“本地”模式匹配表达式(如case上面 haskell 片段中的表达式),这无济于事。像这样的东西:
given Hold.new(:key<a>) {
when Hold (:$key) { say $key }
when Press (:$key) { say $key }
when Err (:$msg) { say $msg }
default { say "Unsupported" }
}
唉,不能编译。那么我是不是遗漏了什么,或者 Raku 可以用其他方式表达吗?
回答
您正在尝试使用不需要签名的签名。
签名更有可能是块的一部分,所以它看起来更像这样:
given Hold.new(:key<a>) {
when Hold -> (:$key) { say $key }
when Press -> (:$key) { say $key }
when Err -> (:$msg) { say $msg }
default { say "Unsupported" }
}
这目前不起作用,因为块没有任何参数。
可以说这将是一个有用的添加功能。
请注意,只有两件事可以given做。
- 就像一个块来完成
proceed并succeed最终确定。 - 设置
$_为您提供的值。
所以这意味着它$_已经设置为您正在智能匹配的值。
given Hold.new(:key<a>) {
when Hold { say .key }
when Press { say .key }
when Err { say .msg }
default { say "Unsupported" }
}
- Such a feature has been speculated in the past. Between the ongoing work on dispatch and the new compiler frontend coming up during the next year, it'll probably be rather easier to implement and to have it perform decently that it is today.
回答
您尝试的语法实际上非常接近。这是你想要的:
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
given Hold.new(:key<a>) {
when Hold { say .key }
when Press { say .key }
when Err { say .msg }
default { say "Unsupported" }
}
需要注意的几件事:正如语法中的变化所示,您正在匹配的类型Hold,但您不是在解构 Hold。相反,您正在利用when块将块内的主题变量 ( $_)设置为等于匹配这一事实。
其次(看起来您已经意识到这一点,但我正在添加它以供将来参考),重要的是要注意given块并不能保证详尽的模式匹配。您可以使用default { die }块模拟该保证,但这当然是运行时检查而不是编译时检查。
- Your lack of whitespace alignment in the class definitions is killing me, ditto for the default block. ^_^
回答
我同意在 Raku 中有优雅的语法会很好。但从功能上讲,我认为 Raku 比您想象的更接近您所描述的内容。
Raku 中与此最接近的似乎是多方法。
Raku 确实支持多种方法。但是你展示的是多功能的。
我也认为多功能是 Raku 目前最接近你描述的东西。但我也认为它们比你目前认为的更接近。
我想要一个“本地”模式匹配表达式
多功能是本地的(词法范围)。
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
{
match Hold.new: :key<a> ;
multi match (Hold (:$key)) { say $key }
multi match (Press (:$key)) { say $key }
multi match (Err (:$msg)) { say $msg }
multi match ($) { say "unsupported" }
}
# match; <--- would be a compile-time fail if not commented out
是的,上面的代码在语法上是“关闭的”。假设 RakuAST 登陆它可能会特别直接地实现更好的语法。也许:
match Hold.new: :key<a>
-> Hold (:$key) { say $key }
-> Press (:$key) { say $key }
-> Err (:$msg) { say $msg }
else { say "unsupported" }
wherematch实现了multi我上面显示的代码,但是:
-
避免需要封闭块;
-
避免需要显式编写函数调用;
-
替代对象
multi match(...)样板相对简洁->。 -
让
else条款强制性的(为了避免歧义的语法,但有执行失败的显式处理以其他方式匹配的很好的副作用)。
我注意到你为你的例子引入了 OO。(并称为多功能 multimethods。) 需要明确的是,您不需要使用对象来使用 Raku 中的签名进行模式匹配。