Поскольку Haskell ленив, можно начать использовать начало списка, возвращаемого "futu", до того, как остальные будут вычислены.В терминах Javascript это лучше всего смоделировать с помощью генераторов .
Например (для простоты я использовал null
значения для представления None
с):
const arrFutu = coalg => function*(seed) {
while (true) {
const next = coalg(seed);
if (next) {
// Maybe (b, ([b], Maybe a)), using null for None & flattening nested tuple
const [item, items, nextseed] = next;
yield item;
yield* items;
// Maybe a, using null for None
if (nextseed) {
seed = nextseed;
continue; // Continue iterating if the seed isn't null.
}
}
return;
}
}
С примером такой коалгебры, как:
const coalg1 = seed => {
if (seed < 5) {
return ['a', ['a','a'], seed + 1];
} else if (seed < 10) {
return ['b', ['b','b'], seed + 1];
} else if (seed == 10) {
return ['c', ['c'], null];
}
return null;
}
for (const x of arrFutu(coalg1)(0)) {
console.log(x);
}
for (const x of arrFutu(coalg1)(20)) {
console.log(x);
}
Было бы неплохо сделать futu рекурсивным, а не итеративным, но кажется, что Оптимизация хвостового вызова Javascript не работает с генераторами .
Кстати, даже если код на Haskell не является хвостовой рекурсией, рекурсия «охраняется» : рекурсивный вызов происходит внутри одного или нескольких конструкторов данных (здесь, списки конструкторов) и вызов может быть отложен до тех пор, пока конструкторы не будут «очищены» клиентом, когда он использует возвращенный список.