为什么我们需要Free monad将行动解释为未来

时间:2015-12-13 09:33:13

标签: scala scalaz free-monad

我写了一个例子来使用scalaz.Free将Action映射到Future,它看起来很酷。但是,我试图了解它的好处。我希望我能在这里得到答案。这是我的代码片段

首先,我创建一个Action,即AST。

trait Action[A]

case class GetNumberAction(x: Int) extends Action[Int]
case class GetStringAction(x: String) extends Action[String]
case class ConvertToIntAction(x: String) extends Action[Int]
case class AddAction(x: Int, y: Int) extends Action[Int]

然后,我创建了一个类,通过使用Scalaz Free和Coyonda将Action映射到ASTMonad。

type Functor[A] = Coyoneda[Action, A]
type ASTMonad[A]= Free[Functor, A]

def toMonad[A](action: Action[A]): ASTMonad[A] = Free.liftFC[Action, A](action) 

object ADTMonad {
    def getNumber(x: Int): ASTMonad[Int] = toMonad(GetNumberAction(x))
    def getString(x: String): ASTMonad[String] = toMonad(GetStringAction(x))
    def converToInt(x: String): ASTMonad[Int] = toMonad(ConvertToIntAction(x))
    def add(x: Int, y: Int): ASTMonad[Int] = toMonad(AddAction(x, y))

}

最后,我创建了一个解释器来解释Action to Future

object Interpreter extends (Action ~> Future) {
    def apply[A](action: Action[A]): Future[A] = {
        action match {
            case GetNumberAction(x) => Future(x)
            case GetStringAction(x) => Future(x)
            case ConvertToIntAction(x) => Future(x.toInt)
            case AddAction(x, y) => Future(x + y)
        }
    }
}

当我运行它时,我可以使用

val chain =  for {
    number <- ASTMonad.getNumber(x)
    str <- ASTMonad.getString(y)
    convertedNumber <- ASTMonad.converToInt(str)
    total <- ASTMonad.add(number, convertedNumber)
    } yield total

chain.runWith(Interpreter)

它似乎有效,我想我理解这个monad和解释事物。但是,如果我直接使用 Future.flatmap map ,我正在考虑与解决方案相比有什么好处?

for {
    number <- Future(x)
    str <- Future(y)
    convertedNumber <- Future(str.toInt)
    total <- Future(number + convertedNumber)
} yield total

使用Future flatmap和map的代码看起来更简单。回到我的问题,我们是否需要使用Free monad将业务逻辑解释为Future,因为Future已经提供了flatMap和map。如果确实如此,有人可以给我更具体的例子,那么我可以看到它的好处吗?

提前致谢

1 个答案:

答案 0 :(得分:0)

使用免费应用程序的一个好的和有动力的示例是命令行解析器,让我们调用类型CLI[A]

类型CLI[A]的值意味着如果提供命令行参数(A),您将获得Array[String],并且可以成功解析它们。现在,当使用Array[String] -> Either[String,A]进行错误处理时,此功能与Either同构。

由于您使CLI适用,因此您可以mapapply(合并)值。例如,您可以创建Int参数count,另一个Int参数count2,并将它们合并到保存其总和的最终sum: CLI[Int]

假设您直接应用计算,这会产生“仅”等同于Array[String] -> Either[String,Int]的内容。但是,如果要创建帮助文本,则必须知道两个初始参数,并且此信息将丢失。

Free救援。使用Free,您可以保留计算图,您可以使用该图提取从参数中直接解析的所有初始CLI值。然后,您可以通过为所有初始参数提供解析结果来运行计算,从而产生sum的最终值。

当然,您可以实现一个特殊的CLI来跟踪计算中的所有初始值,但Free让您避免这些额外的工作。