假设我有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
(或任何其他库)是否提供此类功能?
P.S。正如评论中提到的那样,many
函数仅对副作用有用,因此它不是一个很好的例子。
答案 0 :(得分:4)
如果您将X
代表为Scalaz的Endomorphic[Kleisli[Option, ?, ?], A]
,则会获得一个有用的幺半群实例:
def repeat[A](
f: Endomorphic[Kleisli[Option, ?, ?], A],
n: Int
): Endomorphic[Kleisli[Option, ?, ?], A] = List.fill(n)(f).suml
(请注意,为方便起见,我在这里使用了kind-projector - 你也可以写出lambda类型。)
正如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)
}
}
如果你不担心堆栈溢出,这当然可以简单得多。
答案 1 :(得分: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)
}
如果您想要的效果不仅仅是单线程,那么这可能非常方便。请注意,返回类型F[None.type]
反映了规范。
答案 2 :(得分:-4)
类似于
def repeat[A](xa: X[A], n: Int): X[A] = { a =>
(1 to n).foldLeft(Option(a)) { case(a,_) =>
a.flatMap(xa(_))
}
}
对于一个真正的程序员来说不够硬吗?