別の質問(後で編集されました)をきっかけに、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
理解のために結果を求める必要があります。
何か案は?理解にはTry
非常に適していませんか?Either
このタイプのスレッド化を可能にするために、どちらかTry
を「修正」するべきではありませんか(たとえばTry
、の直接結果タイプとして許可するunapply
)?