Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
应用型与一元型组合子及Scalaz中的自由单子_Scala_Stack Overflow_Scalaz_Free Monad_Trampolines - Fatal编程技术网

应用型与一元型组合子及Scalaz中的自由单子

应用型与一元型组合子及Scalaz中的自由单子,scala,stack-overflow,scalaz,free-monad,trampolines,Scala,Stack Overflow,Scalaz,Free Monad,Trampolines,几周前,问到如何在Scalaz 7中使用免费monad来避免这种情况下的堆栈溢出(我对他的代码做了一些修改): 我认为应该这样做: import Free.Trampoline val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) { case (st, i) => st.flatMap(_ => setS(i).lift[Trampoline]) } s(Nil).run 但

几周前,问到如何在Scalaz 7中使用免费monad来避免这种情况下的堆栈溢出(我对他的代码做了一些修改):

我认为应该这样做:

import Free.Trampoline

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) {
  case (st, i) => st.flatMap(_ => setS(i).lift[Trampoline])
}

s(Nil).run
但它仍然会让人大吃一惊,所以我只是把它作为一条评论发布

用applicative
*>
而不是monadic
flatMap
进行排序实际上效果很好:

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) {
  case (st, i) => st *> setS(i).lift[Trampoline]
}

s(Nil).run
(当然,这是非常慢的,因为在Scala中做任何有趣的事情都要付出这样的代价,但至少没有堆栈溢出。)


这是怎么回事?我不认为有什么原则上的原因造成这种差异,但我真的不知道在实现过程中会发生什么,目前没有时间去挖掘。但我很好奇,如果其他人知道,那就太酷了。

对于这种差异,有一种原则性的直觉

应用运算符
*>
仅计算其左参数的副作用,并始终忽略结果。这类似于(在某些情况下相当于)Haskell的单子的
>
函数。以下是
*>
的源代码:

/** Combine `self` and `fb` according to `Apply[F]` with a function that discards the `A`s */
final def *>[B](fb: F[B]): F[B] = F.apply2(self,fb)((_,b) => b)
Apply#apply2

def apply2[A, B, C](fa: => F[A], fb: => F[B])(f: (A, B) => C): F[C] =
  ap(fb)(map(fa)(f.curried))
通常,
flatMap
取决于左参数的结果(它必须,因为它是右参数中函数的输入)。即使在这种特定情况下忽略了左侧结果,
flatMap
也不知道这一点

鉴于您的结果,
*>
的实现似乎针对不需要左参数结果的情况进行了优化。但是
flatMap
无法执行此优化,因此每次调用都会通过保留未使用的剩余结果来增加堆栈


有可能在编译器(scalac)或JIT(热点)级别对其进行优化(Haskell的GHC当然会执行此优化),但目前看来这似乎是一个错过的优化机会。

只是为了补充讨论

StateT
中,您有:

  def flatMap[S3, B](f: A => IndexedStateT[F, S2, S3, B])(implicit F: Bind[F]): IndexedStateT[F, S1, S3, B] = 
  IndexedStateT(s => F.bind(apply(s)) {
    case (s1, a) => f(a)(s1)
  })
apply(s)
在下一个状态中修复当前状态引用

bind
definition急切地解释其捕获引用的参数,因为它需要:

  def bind[A, B](fa: F[A])(f: A => F[B]): F[B]
ap
的差异处,可能不需要解释其参数之一:

  def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B]

有了这段代码,
Trampoline
无法帮助
StateT
flatMap
(还有
map
)…

Mandubian是正确的,StateT的flatMap不允许您绕过堆栈累积,因为在调用包装的monad绑定(这将是一个免费的绑定)之前立即创建了新StateT[Function0]在您的情况下)

所以蹦床帮不上忙,但状态函子上的自由单子是确保堆栈安全的一种方法

我们希望从State[List[Int],Unit]到Free[a[State[List[Int],a],Unit],我们的flatMap调用将是Free的flatMap(除了创建自由数据结构之外,它不做任何事情)

现在我们已经建立了一个自由的数据结构,可以轻松地通过以下方式线程一个状态:

s.foldRun(List[Int]())( (a,b) => b(a) )
给liftF打电话很难看,所以我有一个公共关系,让State和Kleisli monads更容易打电话,希望将来不需要使用lambdas类型

编辑:公关被接受,所以现在我们有

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).liftF) {
      case (st, i) => st.flatMap(_ => setS(i).liftF)
}

+1谢谢,但我的理解是蹦床的
flatMap
将堆上的绑定具体化,这意味着即使我们没有放弃这个结果,我们在这里也是安全的?@cdk我认为这不是答案。选择另一个操作符,它取决于左边的结果,需要
应用
属于
Bind
。例如
@
s.foldRun(List[Int]())( (a,b) => b(a) )
val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).liftF) {
      case (st, i) => st.flatMap(_ => setS(i).liftF)
}