在 hacklang 中组合多个泛型类型

Combine multiple generic types in hacklang

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

If no memo is passed to the initial invocation of reduce, the iteratee is not invoked on the first element of the list. The first element is instead passed as the memo in the invocation of the iteratee on the next element in the list.

我尝试实现的功能:

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 == Tresultis_null($memo)

我注意到那行

$memo = $iterable->firstValue();

将类型 T 的值分配给 $memo。这似乎是错误的; $memo 在声明中被指定为 ?Tresult 类型,并在此处分配了 Tresult 类型的值:

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

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

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

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

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,它们都是类型安全的。