4

別の質問(後で編集されました)をきっかけに、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)

今、特に追加flatMapmapインyieldは恐ろしいようです、そしてhead理解のために結果を求める必要があります。

何か案は?理解にはTry非常に適していませんか?Eitherこのタイプのスレッド化を可能にするために、どちらかTryを「修正」するべきではありませんか(たとえばTry、の直接結果タイプとして許可するunapply)?

4

1 に答える 1

4

秘訣は、内部構造にインスタンスを作成せ、代わりに例外をスローして1つの外部構造を構築することです。Try Try

まず、Try[Unit]'sを取り除きましょう:

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`?
}
于 2012-08-16T15:15:35.937 に答える