Объедините несколько общих типов в хакланг

Я пытаюсь реализовать функцию снижения от подчеркивания в хаке. Подчеркивание, функция приведения имеет следующее поведение:

Если никакая памятка не передается при первоначальном вызове Reduce, итератор не вызывается в первом элементе списка. Вместо этого первый элемент передается в качестве памятки при вызове итерируемого следующего элемента в списке.

Моя попытка реализовать функцию:

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)

1 ответ

Решение

Я отмечаю, что линия

$memo = $iterable->firstValue();

присваивает значение типа T в $memo, Это кажется неправильным; $memo дано быть типа ?Tresult в объявлении, и присваивается значение типа Tresult Вот:

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

Можете ли вы объяснить, почему $memo присваивается значение типа T в первую очередь? Откуда ты это знаешь T а также Tresult подобные? Я не вижу никаких доказательств того, что эти два типа всегда должны быть одним и тем же. Проверка типов выдает здесь ошибку, потому что эта программа не безопасна; если T - это животное, а Tresult - это фрукт, а кто-то раздает нулевой фрукт, то нет способа вывести фрукт из последовательности.

Кроме того, я нахожу странным, что 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());
}

Там, теперь у нас есть две разные формы снижения, и обе они безопасны.

Другие вопросы по тегам