chainRec的基本思想是什么?

时间:2018-05-27 11:36:50

标签: javascript recursion functional-programming monads

[编辑]

这是How to implement a stack-safe chainRec operator for the continuation monad?

的后续问题

鉴于是chainRec的类型

chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b

通常,chainRec与trampoline一起实现,以允许在monad中进行堆栈安全递归。但是,如果我们放弃蹦床,我们可以为正常函数实现chainRec类型,如下所示:

const chainRec = f => x => join(f(chainRec(f), of, x));

接下来,我想将它应用于递归操作:

const map = f => g => x => f(g(x));
const join = f => x => f(x) (x);
const of = x => y => x;

const chainRec = f => x => join(f(chainRec(f), of, x));

const repeat = n => f => x => 
  chainRec((loop, done, args) =>
    args[0] === 0
      ? done(args[1])
      : loop([args[0] - 1, map(f) (args[1])])) ([n, of(x)]);

const inc = x => of(x + 1);

repeat(10) (inc) (0) (); // error

我认为,由于join的定义中有chainRec,因此map的实现必须有repeat,因此有两个嵌套功能上下文崩溃。然而它不起作用,我不知道如何解决它。

1 个答案:

答案 0 :(得分:1)

不知道您的repeat函数的作用,我猜您的调用repeat(10)(inc)(0)应该扩展为

map(inc)(
 map(inc)(
  map(inc)(
   map(inc)(
    map(inc)(
     map(inc)(
      map(inc)(
       map(inc)(
        map(inc)(
         map(inc)(
          of(0)
         )
        )
       )
      )
     )
    )
   )
  )
 )
)

由于您的inc因某种原因而返回功能_ => Int而非普通Int,因此会在功能x + 1上调用x到该函数的字符串化(y => x变为"y => x1"),这将在尝试调用时抛出异常。

修复const inc = x => x + 1;后,您的repeat功能仍无效。它需要是简单的递归,

const id = x => x
// rec :: ((a -> c, b -> c, a) -> c) -> a -> b
// here with c == b, no trampoline
const rec = f => x => f(rec(f), id, x) // a bit like the y combinator

const repeat = n => f => x => 
  rec((loop, done, [m, g]) =>
    m === 0
      ? done(g)
      : loop([m - 1, map(f)(g)])
  )([n, of(x)]);

repeat(10)(inc)(0)() // 10 - works!

根本没有涉及monad!

如果我们想使用chainRec,我们需要引入一些任意monad(这里:函数monad),f回调到chainRec需要返回一个实例那个monad类型而不仅仅是loop / done

chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b
//                                                ^

我们可以通过简单地将返回值包装在of

中来实现
const repeat = n => f => x => 
  chainRec((loop, done, [m, g]) =>
    of(m === 0
//  ^^
      ? done(g)
      : loop([m - 1, map(f)(g)])
     )
  )([n, of(x)]);

当然现在得到一个m b,即包含在另一个函数中的所有内容:

repeat(10)(inc)(0)()() // 10
//                  ^^

// repeat(1)(inc)(0) expands to `of(map(inc)(of(0)))

但我怀疑这是你想要的。