When to use scala triple caret (^^^) vs double caret (^^) and the into method (>>)

烂漫一生 提交于 2019-12-30 08:51:48

问题


Can someone explain how and when to use the triple caret ^^^ (vs the double caret ^^) when designing scala parser combinators? And also when / how to use the parser.into() method (>>).


回答1:


I'll begin with an example using Scala's Option type, which is similar in some important ways to Parser, but can be easier to reason about. Suppose we have the following two values:

val fullBox: Option[String] = Some("13")
val emptyBox: Option[String] = None

Option is monadic, which means (in part) that we can map a function over its contents:

scala> fullBox.map(_.length)
res0: Option[Int] = Some(2)

scala> emptyBox.map(_.length)
res1: Option[Int] = None

It's not uncommon to care only about whether the Option is full or not, in which case we can use map with a function that ignores its argument:

scala> fullBox.map(_ => "Has a value!")
res2: Option[String] = Some(Has a value!)

scala> emptyBox.map(_ => "Has a value!")
res3: Option[String] = None

The fact that Option is monadic also means that we can apply to an Option[A] a function that takes an A and returns an Option[B] and get an Option[B]. For this example I'll use a function that attempts to parse a string into an integer:

def parseIntString(s: String): Option[Int] = try Some(s.toInt) catch {
  case _: Throwable => None
}

Now we can write the following:

scala> fullBox.flatMap(parseIntString)
res4: Option[Int] = Some(13)

scala> emptyBox.flatMap(parseIntString)
res5: Option[Int] = None

scala> Some("not an integer").flatMap(parseIntString)
res6: Option[Int] = None

This is all relevant to your question because Parser is also monadic, and it has map and flatMap methods that work in very similar ways to the ones on Option. It also has a bunch of confusing operators (which I've ranted about before), including the ones you mention, and these operators are just aliases for map and flatMap:

(parser ^^ transformation) == parser.map(transformation)
(parser ^^^ replacement) == parser.map(_ => replacement)
(parser >> nextStep) == parser.flatMap(nextStep)

So for example you could write the following:

object MyParser extends RegexParsers {
  def parseIntString(s: String) = try success(s.toInt) catch {
    case t: Throwable => err(t.getMessage)
  }

  val digits: Parser[String] = """\d+""".r
  val numberOfDigits: Parser[Int] = digits ^^ (_.length)
  val ifDigitsMessage: Parser[String] = digits ^^^ "Has a value!"
  val integer: Parser[Int] = digits >> parseIntString
}

Where each parser behaves in a way that's equivalent to one of the Option examples above.



来源:https://stackoverflow.com/questions/21259470/when-to-use-scala-triple-caret-vs-double-caret-and-the-into-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!