【问题标题】:Combinators for A => Option[A] in ScalaScala 中 A => Option[A] 的组合子
【发布时间】:2016-06-13 16:52:40
【问题描述】:

假设我有type X[A] = A => Option[A] 并且需要这样的功能:

def repeat[A](xa: X[A], n: Int): X[A] // call xa n times  
def many[A](xa: X[A]): X[A]           // call xa until it returns None

scalaz(或任何其他库)是否提供此类功能?

附:正如在 cmets 中提到的,many 函数只对副作用有用,所以它不是一个很好的例子。

【问题讨论】:

  • 不确定,因为Scalaz 更喜欢使用纯函数,所以repeat/many 没有任何意义
  • @dk14 当然,many 只能用于它的副作用,但 repeat 有完全合理的纯用例。
  • @dk14 和 Travis,many 是对的。我可能需要smth。喜欢until[A](xa: X[A], pred: A => Boolean): X[A] 调用xa 直到pred 为真
  • @TravisBrown 仅当它被提升到 Kleisli 之类的东西时(或使用某种免费的单子),无论如何 +1 给你的答案
  • @dk14 我的理解是,在repeat 中,该函数将在先前的结果上迭代调用,在这种情况下,它完全是有意义的。

标签: scala scalaz combinators


【解决方案1】:

如果您将您的 X 表示为 Scalaz 的 Endomorphic[Kleisli[Option, ?, ?], A],您将获得一个有用的 monoid 实例:

def repeat[A](
  f: Endomorphic[Kleisli[Option, ?, ?], A],
  n: Int
): Endomorphic[Kleisli[Option, ?, ?], A] = List.fill(n)(f).suml

(请注意,为方便起见,我在这里使用 kind-projector ——你也可以写出类型 lambdas。)

正如 Kenji 在评论中所说,您也可以使用 multiply

def repeat[A](
  f: Endomorphic[Kleisli[Option, ?, ?], A],
  n: Int
): Endomorphic[Kleisli[Option, ?, ?], A] =
  Monoid[Endomorphic[Kleisli[Option, ?, ?], A]].multiply(f, n)

这是同一个幺半群,但效率更高。

不过,这对many 并没有真正的帮助(它只对它的副作用有用)。在我的脑海中,我想不出任何优雅抽象的方式来定义它,但是一个简单的递归实现还不错:

def many[A](f: A => Option[A]): A => Option[A] = new Function[A, Option[A]] {
  @scala.annotation.tailrec
  def apply(a: A): Option[A] = f(a) match {
    case None => None
    case Some(v) => apply(v)
  }
}

如果您不担心堆栈溢出,这当然会简单得多。

【讨论】:

  • Monoid#multiply 比 Foldable#suml 更高效github.com/scalaz/scalaz/pull/988/files
  • @TravisBrown 我们不应该在您更新的代码示例中要求 monoid 的实例,例如 def repeat[A, M <: Endomorphic[Kleisli[Option, ?, ?]: Monoid, A]](f: M, n: Int): M = implicilty[Monoid[M]].multiply(f, n)?虽然没有检查该代码......所以只是一个猜测
【解决方案2】:

只是指出:使用 Scalaz/Cats,您可以编写许多的纯版本

import scalaz._
import scalaz.syntax.monad._

def many[F[_] : Monad,A](xa : A => F[Option[A]])(a : A) : F[None.type] = xa(a) >>= {
  case None    => implicitly[Monad[F]].point(None)
  case Some(y) => many(xa)(y)
}

如果您想要的效果不仅仅是单线程 io,这将非常方便。请注意,返回类型 F[None.type] 反映了规范。

【讨论】:

    【解决方案3】:

    有点像

     def repeat[A](xa: X[A], n: Int): X[A] =  { a => 
       (1 to n).foldLeft(Option(a)) { case(a,_) => 
          a.flatMap(xa(_))
       }
    }
    

    对于真正的程序员来说不够核心?

    【讨论】:

    • 如果不是函数式程序员
    • 副作用不是;如果函数没有副作用(不产生/不依赖 - 引用透明),所有这些循环都没有任何意义 - 结果将是相同的,无论你重复多少次
    • 错了。您应该根据上一次调用的结果调用 xa
    • 你的第二个实现不是有效的 Scala 代码,而你的第一个没有做 OP 打算做的事情(至少在我的阅读中)。
    • @dk14 不,函数的结果可能与其参数不同,所以如果你在前一次调用的结果上调用函数,你会得到不同的结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 2017-03-22
    • 2020-07-16
    相关资源
    最近更新 更多