由another question触发(不过后来被编辑掉了),我想尝试一下将调用链接到Scala2.10的Try结构是多么容易(参见:this presentation),使用for-comprehensions。
其思想是有一个令牌列表,并将它们与一系列模式进行匹配,然后返回第一个错误或成功匹配的模式。我得到了下面这个相当尴尬的版本,我想知道是否可以让它变得更简单和更好:
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)现在尤其是yield中额外的flatMap和map看起来很可怕,以及需要对结果调用head来进行理解。
有什么想法吗?Try非常不适合用于理解吗?Either或Try是否应该被“修复”以允许这种类型的线程(例如,允许Try作为unapply的直接结果类型)?
发布于 2012-08-16 23:15:36
诀窍似乎不是在内部结构中创建Try实例,而是让它抛出异常并构造一个外部Try。
首先,让我们去掉Try[Unit]:
case class Expect(expectation: String)(pattern: PartialFunction[Token, Unit]) {
def unapply(token: Token) =
pattern.isDefinedAt(token) || (throw new Exception(expectation))
}
case class Extract[A](expectation: String)(pattern: PartialFunction[Token, A]) {
def unapply(token: Token) = Some(
pattern.lift(token).getOrElse(throw new Exception(expectation))
)
}那么检查就变成了:
val hasVoid = Expect ("function def starts with void") { case Keyword("void") => }
val getIdent = Extract("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]) = Try {
val iter = tokens.iterator
def next() = Some(if (iter.hasNext) iter.next else NoToken)
(for {
hasVoid() <- next()
getIdent(id) <- next()
hasOpen() <- next()
hasClosed() <- next()
} yield FunctionDef(id)).head // can we get rid of the `head`?
}https://stackoverflow.com/questions/11990017
复制相似问题