【问题标题】:A better way to introspect a capture内省捕获的更好方法
【发布时间】:2020-07-01 09:55:20
【问题描述】:

我想测试签名中第一个对象的类型。以下显示了我发现该工作的一些方法。但是为什么对类型(以下 3 个测试中的第 2 个)的智能匹配不起作用? 有没有比对 Type 等价的字符串进行字符串化和测试更好的方法? (以下是我正在处理的用例)

raku -e "sub a( |c ) { say so |c[0].WHAT.raku ~~ /'Rat'/, so |c[0].WHAT ~~ Rat, so |c[0].^name ~~ /'Rat'/ };a(3/2);a(2)"
TrueFalseTrue
FalseFalseFalse
# OUTPUT:
#TrueFalseTrue
#FalseFalseFalse

我正在写一个proto sub handle,大多数潜艇都有类似的签名,例如。 multi sub handle( Pod $node, MyObj $p, Int $level --> Str)

所以大多数多潜艇根据 $node 中的内容做不同的事情。但是,当使用Nil 或纯字符串调用handle 时,如何处理这些情况。我正在考虑类似的事情

proto handle(|c) {
    if |c[0].^name ~~ /'Str'/ { # code for string }
    else { {*} }
}

【问题讨论】:

  • proto foo(| [$first, |]) { say $first.perl; {*}; },或者更清楚的是没有匿名捕获为proto foo(|all [$first, |rest]) { say $first.perl; {*}; }

标签: raku introspection


【解决方案1】:

一种更好的自省方式......

一般来说,在任何编程语言中做任何事情的更好方法是内省,如果可以避免的话。

通常,在 Raku 中,您可以避免手动自省。请参阅此答案末尾的 Introspection 部分以进行进一步讨论。

...捕获

获取捕获自省提供的功能的最佳工具是使用签名。这是他们生活的主要目的。

我想测试签名中第一个对象的类型

使用签名:

proto handle(|) {*}
multi handle( Pod $node )   { ... }
multi handle( Str $string ) { ... }
multi handle( Nil )         { ... }

以下显示了我发现的一些方法。

虽然他们做你想做的事,但他们基本上忽略了 Raku 的所有标志性功能。它们将签名简化为与捕获的绑定作为单个结构;然后在例程的主体中使用该捕获的手动内省。

使用签名几乎总是有一种更简单、更好的方法来做这些事情。

为什么 [|c[0].WHAT ~~ Rat, with c[0] == 3/2] 不起作用?

我会先简化,然后是你的代码在做什么:

say    3/2        ~~ Rat;  # True
say   (3/2)       ~~ Rat;  # True
say   (3/2).WHAT  ~~ Rat;  # True
say |((3/2).WHAT  ~~ Rat); # True
say (|(3/2).WHAT) ~~ Rat;  # False
say  |(3/2).WHAT  ~~ Rat;  # False

最后一种情况是因为|precedence~~ 高。

有没有比对 Type 等价的字符串进行字符串化和测试更好的方法?

天哪,是的。

使用类型,卢克。

(在您的用例中,请使用签名。)

自省

与在例程主体中手动内省传入数据的代码相比,适当使用签名通常会:

  • 阅读更好;

  • 生成更好的低级代码;

  • 编译阶段进行部分或全部评估。

如果一种语言及其编译器通过提供特定功能(例如签名)来解决用例,那么使用该功能而不是自省通常会带来上述三个好处。

语言/编译器可以分为四类,即:

  1. 不要进行或允许任何自省;

  2. 允许编译器进行自省,但不允许devs

  3. 允许编译器和开发人员进行自省,但目标是使其成为最后的手段,至少对于开发人员而言;

  4. 允许并鼓励开发者进行自省。

Raku(do) 属于第三类。在此 SO 的上下文中,签名是主要功能,几乎消除了开发人员手动自省的任何需要。

【讨论】:

  • 我猜你的意思是明确的内省;签名进行隐式内省。但是你愿意解释为什么内省不好吗?当检查类型的隐式方法更好时,也许太多的权力落到了坏人手中?
  • 感谢您的评论;我已经更新了我的答案。请注意,虽然我同意你的观点,但从某种意义上说,这与你写的关于显式/隐式的内容相反。就 Rakudo 而言,签名显式地 做签名所做的事情,而进行自省的用户代码只是隐式地 做签名所做的事情。此外,签名是否总是做“自省”也是有争议的。这种隐式/显式区别和“内省”值得商榷的一个(弱)示例是 Rakudo 可以在编译(优化)期间由于签名而拒绝某些程序。
【解决方案2】:

您可以简单地智能匹配类型:

raku -e "sub a( *@c ) { say @c[0] ~~ Rat };a(3/2);a(2)" 
True
False

另外我在这里使用slurpy 而不是捕获,这是另一种选择。无论如何,只有一个参数,你可能最好使用type captures

raku -e "sub a( ::T $ ) { say ::T ~~ Rat };a(3/2);a(2)" 
True
False

【讨论】:

    【解决方案3】:

    您可以从签名中的 Capture 中提取内容。

    # ( |C ( ::Type $a, +@b ) )
    proto handle( | ( ::Type, +@ ) ) {
        if Type ~~ Str {
            …
        } else {
            {*}
        }
    }
    

    基本上,参数前的::Foo(或代替它)类似于该参数上的.WHAT

    它也可以用作类型描述符。

    sub foo ( ::Type $a ) {
        my Type $b = $a;
    }
    

    根据名称比较类型是一个非常糟糕的主意。

    my $a = anon class Foo { has $.a }
    my $b = anon class Foo { has $.b }
    
    say $a.WHAT =:= $b.WHAT; # False
    
    say $a.^name eq $b.^name; # True
    

    就 Raku 而言,两种类型碰巧同名完全是巧合。
    如果您确实使用了这些名称,您的代码将会对实际情况感到困惑。

    【讨论】:

    • 这里的额外信息非常有趣。它在文档中吗?如果没有,也许可以添加
    猜你喜欢
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多