在 Crystal 中从生成块中进行类型优化

Type refinement in Crystal from yielding blocks

我正在使用 Enumerable#select(&block) 尝试使用 arr.select { |el| el.is_a?(Bar) } 将数组优化为特定类型的元素,但那没有用。

然后我看到了Enumerable#select(type) method, and that worked: https://play.crystal-lang.org/#/r/7v05

但我注意到这两个定义在我的案例中非常相似。

Enumerable#select(&block):

  def select(&block : T ->)
    ary = [] of T
    each { |e| ary << e if yield e }
    ary
  end

Enumerable#select(type)

  def select(type : U.class) forall U
    ary = [] of U
    each { |e| ary << e if e.is_a?(U) }
    ary
  end

有没有办法让编译器知道 select 块正在细化元素的类型(也许通过某种方式向块添加类型)?还是编译器不知道该块断言了什么?

这里的问题是需要使用正确的类型创建数组。所以这两种方式的核心区别在于:

ary = [] of T

其中 T 是您调用 select 的 Enumerable 的类型参数,而

ary = [] of U

其中 U 是此方法特定的类型参数 (forall U)。

因此,要执行您想要的操作,我们需要知道该块正在过滤元素,但这绝不是在块类型中编码的。它只有一个参数类型列表和一个 return 类型。然而,我们当然可以将这两种方法组合成类似的东西:

module Enumerable(T)
  def select(type : U.class) forall U
    ary = [] of U
    each { |e| ary << e if yield e if e.is_a? U }
    ary
  end
end