A =>的组合器; Scala中的选项[A]

时间:2016-03-01 10:11:45

标签: scala scalaz combinators

假设我有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函数仅对副作用有用,因此它不是一个很好的例子。

3 个答案:

答案 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(_))
   }
}

对于一个真正的程序员来说不够硬吗?