0

我正在尝试在hack中实现下划线的reduce功能。在下划线中,reduce 函数具有以下行为:

如果没有 memo 传递给 reduce 的初始调用,则不会在列表的第一个元素上调用 iteratee。相反,第一个元素在列表中的下一个元素上的 iteratee 调用中作为备忘录传递。

我尝试实现该功能:

function reduce<T, Tresult>(
  Iterable<T> $iterable,
  (function(?Tresult, T):Tresult) $fn,
  ?Tresult $memo=null):?Tresult {
    if (is_null($memo)) {
      $memo = $iterable->firstValue();
      $iterable = $iterable->skip(1);
    }

    foreach ($iterable as $value) {
      $memo = $fn($memo, $value);
    }

    return $memo;
}

这会导致错误:

Invalid return type (Typing[4110])  
  This is a value of generic type Tresult  
  It is incompatible with a value of generic type T  
    via this generic Tv

我如何告诉类型检查器T == Tresult何时is_null($memo)

4

1 回答 1

1

我注意到这条线

$memo = $iterable->firstValue();

为 分配一个类型的T$memo。这似乎是错误的;$memo在声明中被赋予类型?Tresult,并在此处分配一个类型值Tresult

$memo = $fn($memo, $value);

你能解释一下为什么在第一个实例$memo中分配一个类型的值吗?T你怎么知道TTresult是一样的?我没有看到任何证据表明这两种类型曾经被限制为同一事物。类型检查器在这里给你一个错误,因为这个程序不是类型安全的;如果 T 是 Animal 并且 Tresult 是 Fruit,并且有人传入了一个 null 水果,则无法从序列中取出一个水果。

另外,我觉得reduce返回一个可以为空的结果很奇怪;当然它应该返回给定结果类型的结果,不是吗?

如果您希望此函数根据参数的无效性具有两种不同的行为,那么为什么不简单地具有两个函数呢?

function reduce1<T, Tresult>(
  Iterable<T> $iterable,
  (function(Tresult, T):Tresult) $fn,
  Tresult $memo): Tresult {
    foreach ($iterable as $value) {
      $memo = $fn($memo, $value);
    }
    return $memo;
}

function reduce2<T>(
  Iterable<T> $iterable,
  (function(T, T):T) $fn): T {
    return reduce1($iterable->skip(1), $fn, $iterable->firstValue());
}

在那里,现在我们有两种不同形式的 reduce,而且它们都是类型安全的。

于 2016-06-15T16:19:51.337 回答