rakulang, dartlang, nimlang, golang, rustlang, lang lang no see

如果集合如我所想

焉知非鱼

If Sets Would DWIM

If Sets Would DWIM

每当我在 Raku 中使用集合的时候,它们经常无法 DWIM。这是一个简短的探索,看看是否可以改进 DWIMminess。

我最近重新审视了我前段时间写的一个利用 (-) 集差运算符的脚本。这段代码有一个 bug 潜伏在那里,显而易见,因为下面的代码并没有按照我的直觉去做。

  my @allowed = <m c i p l o t>;
  my @chars = 'impolitic'.comb;

  my @remainder = @allowed (-) @chars;

  if +@remainder == 0 {
     say 'pangram';
  } else {
     say "unused: [{@remainder.join(' ')}]";
  }
unused: []

错误的原因是 (-) 产生了一个 Set,而赋值给 @remainder 会产生1项的 Array。总是这样。但不方便的是,当它是一个空集合时,它就会字符串化为一个空字符串,这只是帮助掩盖了这个潜伏的错误。

my @items = <a b c d e> (-) <a b c d e>;
say @items.raku;
say +@items;
[Set.new()]
1

解决方法比较简单。只要不赋值给数组就可以了。使用一个标量容器来代替。

my $items = <a b c d e> (-) <a b d>;
say $items.raku;
say +$items;
Set.new("e","c")
2

甚至是关联容器也可以。

my %items = <a b c d e> (-) <a b d>;
say %items.raku;
say +%items;
{:c(Bool::True), :e(Bool::True)}
2

或在赋值前明确地取出键的列表。

my @items = (<a b c d e> (-) <a b d>).keys;
say @items.raku;
say +@items;
["e", "c"]
2

很好,起作用了。只是不要用数组容器来处理 Setty 这样的东西。只是这并不能阻止我的直觉时不时地碰上这个错误。同一类的 bug 在我的代码中出现过好几次,因为它实在是太容易犯错了。Raku 不会告诉我,我做错了什么,因为也许是故意的。但重要的是, Raku 没有设法 DWIM。

我可以采取的另一个方法是养成添加类型信息的习惯。这样确实可以让 Raku 在我掉进这个陷阱的时候告诉我。

my Str @a = <a b c d e> (-) <a b d>;
Type check failed in assignment to @a; expected Str but got Set (Set.new("e","c"))
  in sub  at EVAL_0 line 3
  in block <unit> at EVAL_0 line 5
  in block <unit> at -e line 1

这是一个明显的例子,添加类型信息有助于 Raku 编译器帮助我避免引入这种 bug。

实验 - 为 Set 自定义数组存储 #

我开始研究核心设置(core setting),看看可以做什么。我惊喜地发现,我可以在 Array.STORE 的多重分派中添加我正在寻找的语义。

use MONKEY;

augment class Array {
   multi method STORE(Array:D: Set \item --> Array:D) {
       self.STORE(item.keys)
   }
}

my @a = <a b c d e> (-) <a b d>;
say @a.raku;
say +@a;
["c", "e"]
2

分享这个似乎是谨慎的,看看我的小 DWIM 是否有任何我没有考虑到的问题或缺点。一个可能的缺点是,如果你需要这样做的话,你需要使用 , 来强制将一个集合变成一个数组。

my @a = <a b c d e> (-) <a b d> , ;
say @a.raku;
[Set.new("e","c")]

下一步是什么 #

我希望这能引发关于这个问题以及其他我们的直觉和 Raku 的行为不太一致的情况的讨论。也许还有其他相关的语言边缘可以被磨平,以消除这种危害。

后续 #

Reddit 上有一些非常有启发性的讨论,涵盖了语言语义和各种替代方法。公平地说,我建议的方法引入了更多的不一致性,而不是价值,但讨论可能会导致一个语言一致的解决方案。