封装Monad中的一系列步骤

时间:2016-01-18 15:08:00

标签: scala monads

试图将Monads理解为“表示定义为步骤序列的计算的结构”:https://en.wikipedia.org/wiki/Monad_(functional_programming)

这个例子:

def optionAdd(x:Option[Int], y:Option[Int]):Option[Int] = 
  for(xx <- x; yy <- y) yield xx+yy 

取自Scala Monad - Full worked example。这个monad以预定义的顺序连接两个字符串。

我正在尝试将这些步骤包含在monad中:

 1. Get String to be converted to html
 2. Perform series of replace statements on the String to create html compliant String



  def getString = {
    "def test() \n { }"
  }                                               //> getString: => String
  val stringOption = Option(getString);           //> stringOption  : Option[String] = Some(def test() 
                                                  //|  { })
  def createHtmlMonad(x: Option[String]): Option[String] =
    Option(x.get.replace("\n", "<br>").replace("def", "<div style=\"color:red\" </div>"));
                                                  //> createHtmlMonad: (x: Option[String])Option[String]
  val h = createHtmlMonad(stringOption);          //> h  : Option[String] = Some(<div style="color:red" </div> test() <br> { })

createHtmlMonad方法不是Monad吗?这个功能可以封装在Monad中吗?

我可以创建一个封装一系列步骤的新方法,但这不是Monad:

    def getString = {
    "def test() \n { }"
  }                                               //> getString: => String

  def stepsToCreateHtml(stringOption: Option[String]) = {

    val replaceBr = Option(stringOption.get.replace("\n", "<br>"))
    val replaceStyle = Option(replaceBr.get.replace("def", "<div style=\"color:red\" </div>"))

    replaceStyle
  }                                               //> stepsToCreateHtml: (stringOption: Option[String])Option[String]
  val stringOption = Option(getString);           //> stringOption  : Option[String] = Some(def test() 
                                                  //|  { })

  val html = stepsToCreateHtml(stringOption);     //> html  : Option[String] = Some(<div style="color:red" </div> test() <br> { })
                                                  //| 

更新: 我认为这有助于回答这个问题。我没有创建一个新的monad,而是重用现有的Option monad:

object t extends App {

  println(parseService.getHtml("").flatMap(convertBr).flatMap(convertStyle))

  def convertBr = (str: String) => Option(str.replaceAll("\n", "<br>"))
  def convertStyle = (str: String) => Option(str.replaceAll("def", "<div style= color : \"red\"></div>"))

}

object parseService {
  def getHtml(str: String): Some[String] = {
    Some("test \n newline def")
  }
}

这基于https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.xprt8msoc。 monad本身只是一个包装器,其价值在于如何与要实现的功能组合,在这种情况下,我使用Option monad来编译新Strings的附加。我仍然不完全了解monad,但感觉更接近。

1 个答案:

答案 0 :(得分:1)

我发现,当monad用于理解形式时,它更容易理解monad的特殊类比,

for {
  cbr <- convertBr(parseService.getHtml("")) //step 1
  cs <- convertStyle(cbr) //step 2
} yield cs

直接汇编到您在细分中写入的flatmap表单。在这种情况下,每一行都是步骤Option monad提供如何步骤链接在一起并解释的逻辑。

在这种情况下,当前一步未返回存在的值时,Option monad终止后续步骤,即None,但允许计算在返回值时继续,即a Some

def flatMap[B](f: A => Option[B]): A =  this match {
    case Some(a) => f(a)
    case None => None
}

然后monad可以被认为是计算上下文,其中步骤发生在flatmap确定计算如何进行。

值得注意的是,这只是一个类比/隐喻。 monad简单地遵循 monadic law

最重要的法律可以简单地用scala编写,

val ab = for {
 a <- genA
 b <- genB
} yield (a, b)

val ba = for {
 b <- genB
 a <- genA
} yield (a, b)

assert(ab == ba)