由另一个问题触发(尽管随后已被编辑掉),我想尝试使用 for-comprehensions将调用链接到 Scala 2.10 的Try
构造(参见此演示文稿)是多么容易。
这个想法是有一个标记列表并将它们与一系列模式匹配,然后返回第一个错误或成功匹配的模式。我得到了以下非常尴尬的版本,我想知道这是否可以变得更简单更好:
import util.Try
trait Token
case class Ident (s: String) extends Token
case class Keyword(s: String) extends Token
case class Punct (s: String) extends Token
case object NoToken extends Token
case class FunctionDef(id: Ident)
case class Expect[A](expectation: String)(pattern: PartialFunction[Token, A]) {
def unapply(tup: (Try[_], Token)) = Some(tup._1.map { _ =>
pattern.lift(tup._2).getOrElse(throw new Exception(expectation))
})
}
现在构建期望Keyword("void") :: Ident(id) :: Punct("(") :: Punct(")") :: tail
val hasVoid = Expect("function def starts with void") { case Keyword("void") => }
val hasIdent = Expect("expected name of the function") { case id: Ident => id }
val hasOpen = Expect("expected opening parenthesis" ) { case Punct("(") => }
val hasClosed = Expect("expected closing parenthesis" ) { case Punct(")") => }
构建一个完整的测试用例:
def test(tokens: List[Token]) = {
val iter = tokens.iterator
def next(p: Try[_]) = Some(p -> (if (iter.hasNext) iter.next else NoToken))
def first() = next(Try())
val sq = for {
hasVoid (vd) <- first()
hasIdent (id) <- next(vd)
hasOpen (op) <- next(id)
hasClosed(cl) <- next(op)
} yield cl.flatMap(_ => id).map(FunctionDef(_))
sq.head
}
下面验证测试方法:
// the following fail with successive errors
test(Nil)
test(Keyword("hallo") :: Nil)
test(Keyword("void" ) :: Nil)
test(Keyword("void" ) :: Ident("name") :: Nil)
test(Keyword("void" ) :: Ident("name") :: Punct("(") :: Nil)
// this completes
test(Keyword("void" ) :: Ident("name") :: Punct("(") :: Punct(")") :: Nil)
现在特别是附加flatMap
和map
输入yield
似乎很可怕,以及需要调用head
for 理解的结果。
有任何想法吗?Try
非常不适合理解吗?不应该Either
或Try
“固定”以允许这种类型的线程(例如,允许Try
作为直接结果类型unapply
)?