如何改进使用'Free monad'的方法的代码?

时间:2015-07-07 16:43:40

标签: scala functional-programming refactoring monads scalaz

我正在尝试一些检查this slides about Free Monad in Scala的代码,并用一些稍微更改的代码创建了一个小项目。

项目在这里:https://github.com/freewind/free-the-monads

一切似乎都很好,code is clean and beautiful

def insertAndGet() = for {
    _ <- Script.insert("name", "Freewind")
    value <- Script.get("name")
  } yield value

  def insertAndDelete() = for {
    _ <- Script.insert("name", "Freewind")
    _ <- Script.delete("name")
    value <- Script.get("name")
  } yield value

  def insertAndUpdateAndDelete() = for {
    _ <- Script.insert("name", "Freewind1")
    oriValue <- Script.update("name", "Freewind2")
    _ <- Script.delete("name")
    finalValue <- Script.get("name")
  } yield (oriValue, finalValue)

但是当我的逻辑很复杂时,例如有一些Script[Option[_]],我需要检查选项值以决定做某事,我不能再使用for-comprehensionthe code is like

private def isLongName(name: String): Script[Boolean] = for {
  size <- Script.getLongNameConfig
} yield size.exists(name.length > _)

def upcaseLongName(key: String): Script[Option[String]] = {
  Script.get(key) flatMap {
    case Some(n) => for {
      isLong <- isLongName(n)
    } yield isLong match {
        case true => Some(n.toUpperCase)
        case false => Some(n)
      }
    case _ => Script.pure(None)
  }
}

我发现Free Monad方法非常有趣且很酷,但我不熟悉scalaz,只是开始学习Monad事情,不知道如何改进它。

有没有办法让它变得更好?

PS:您可以克隆项目https://github.com/freewind/free-the-monads并亲自尝试

1 个答案:

答案 0 :(得分:6)

这是OptionT monad变换器的一个很好的用例:

import scalaz.OptionT, scalaz.syntax.monad._

def upcaseLongName(key: String): OptionT[Script, String] = for {
  n <- OptionT.optionT(Script.get(key))
  isLong <- isLongName(n).liftM[OptionT]
} yield if (isLong) n.toUpperCase else n 

此处OptionT.optionTScript[Option[String]]转换为OptionT[Script, String].liftM[OptionT]Script[Boolean]转换为同一个monad。

现在而不是:

println(upcaseLongName("name1").runWith(interpreter))

你写的是:

println(upcaseLongName("name1").run.runWith(interpreter))

你也可以upcaseLongName直接通过调用Script[Option[String]]返回run,但是如果有任何机会你需要用其他选项-y脚本编写它 - y事情最好让它返回OptionT[Script, String]