我正在尝试在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)
类型检查器
答案 0 :(得分:1)
我注意到了这行
$memo = $iterable->firstValue();
将T
类型的值指定给$memo
。这似乎是错的;声明中$memo
的类型为?Tresult
,并在此处指定了Tresult
类型的值:
$memo = $fn($memo, $value);
您能解释一下为什么$memo
在第一个实例中被分配了T
类型的值吗?您怎么知道T
和Tresult
是一样的?我没有看到任何证据表明这两种类型被限制为同一件事。类型检查器在这里给你一个错误,因为这个程序不是类型安全的;如果T是Animal而Tresult是Fruit,并且有人通过无效果实,则无法从序列中获得水果。
另外,我发现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,它们都是类型安全的。