Haskell-类似 Raku 中的模式匹配
Haskell-like pattern matching in Raku
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 中与此最接近的似乎是 multimethods 多功能(术语在下面的答案中固定,但 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; }
但是,如果我想要一个“本地”模式匹配表达式(如上面 haskell 片段中的 case
表达式),这将无济于事。像这样:
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" }
}
您尝试的语法实际上非常接近。这就是你想要的:
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
的类型,但您不是 [=25] =]解构 Hold
。相反,您利用了 given
块在块内设置主题变量 ($_
) 的事实。
其次(看起来你已经意识到了这一点,但我将其添加以供将来参考),重要的是要注意 given
块 而不是 保证详尽的模式匹配。您可以使用 default { die }
块(或者更语义化地使用 fatal stub operator: default {!!! 'unreachable'}
)来模拟该保证,但当然这是运行时检查而不是编译时检查.
我同意在 Raku 中使用优雅的语法会很好。但是 功能上 来说,我认为 Raku 比你想象的更接近你所描述的。
The closest thing to this in Raku seems to be multimethods.
Raku 确实支持多种方法。但是你展示的是多功能的。
我也认为多功能是 Raku 目前拥有的最接近您所描述的东西。但我也认为它们比你目前认为的更接近。
I want a "local" pattern matching expression
多功能是局部的(词法作用域)。
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" }
其中 match
实现了我上面显示的 multi
代码,但是:
避免需要封闭块;
避免需要显式编写函数调用;
用相对简洁的 ->
.
替换 multi match(...)
样板
使 else
子句成为强制性的(以避免语法歧义,但具有强制显式处理失败以否则匹配的良好副作用)。
我注意到你为你的例子介绍了 OO。 (并称为多功能多方法。)明确地说,您不需要使用对象来使用 Raku 中的签名进行模式匹配。
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 中与此最接近的似乎是 multimethods 多功能(术语在下面的答案中固定,但 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; }
但是,如果我想要一个“本地”模式匹配表达式(如上面 haskell 片段中的 case
表达式),这将无济于事。像这样:
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" }
}
您尝试的语法实际上非常接近。这就是你想要的:
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
的类型,但您不是 [=25] =]解构 Hold
。相反,您利用了 given
块在块内设置主题变量 ($_
) 的事实。
其次(看起来你已经意识到了这一点,但我将其添加以供将来参考),重要的是要注意 given
块 而不是 保证详尽的模式匹配。您可以使用 default { die }
块(或者更语义化地使用 fatal stub operator: default {!!! 'unreachable'}
)来模拟该保证,但当然这是运行时检查而不是编译时检查.
我同意在 Raku 中使用优雅的语法会很好。但是 功能上 来说,我认为 Raku 比你想象的更接近你所描述的。
The closest thing to this in Raku seems to be multimethods.
Raku 确实支持多种方法。但是你展示的是多功能的。
我也认为多功能是 Raku 目前拥有的最接近您所描述的东西。但我也认为它们比你目前认为的更接近。
I want a "local" pattern matching expression
多功能是局部的(词法作用域)。
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" }
其中 match
实现了我上面显示的 multi
代码,但是:
避免需要封闭块;
避免需要显式编写函数调用;
用相对简洁的
替换->
.multi match(...)
样板使
else
子句成为强制性的(以避免语法歧义,但具有强制显式处理失败以否则匹配的良好副作用)。
我注意到你为你的例子介绍了 OO。 (并称为多功能多方法。)明确地说,您不需要使用对象来使用 Raku 中的签名进行模式匹配。