2012-02-12 17 views
3

私はScalaのケースクラスを抽象化する方法を模索しています。例えば、ここでは(スカラ座2.10.0-M1と-Yvirtpatmatを使用して)Either[Int, String]ための試みである。この定義を考えるとケースクラスの抽象化

trait ApplyAndUnApply[T, R] extends Function1[T, R] { 
    def unapply(r: R): Option[T] 
} 

trait Module { 
    type EitherIntOrString 
    type Left <: EitherIntOrString 
    type Right <: EitherIntOrString 
    val Left: ApplyAndUnApply[Int, Left] 
    val Right: ApplyAndUnApply[String, Right] 
} 

が、私はそのような何かを書くことができます:ここで

def foo[M <: Module](m: M)(intOrString: m.EitherIntOrString): Unit = { 
    intOrString match { 
    case m.Left(i) => println("it's an int: "+i) 
    case m.Right(s) => println("it's a string: "+s) 
    } 
} 

は最初のものです

object M1 extends Module { 
    type EitherIntOrString = String 
    type Left = String 
    type Right = String 
    object Left extends ApplyAndUnApply[Int, Left] { 
    def apply(i: Int) = i.toString 
    def unapply(l: Left) = try { Some(l.toInt) } catch { case e: NumberFormatException => None } 
    } 
    object Right extends ApplyAndUnApply[String, Right] { 
    def apply(s: String) = s 
    def unapply(r: Right) = try { r.toInt; None } catch { case e: NumberFormatException => Some(r) } 
    } 
} 

Eitherの表現がStringあるモジュールの実装のsが、次の作品そう期待して、LeftRight本当に排他ます

scala> foo(M1)("42") 
it's an int: 42 

scala> foo(M1)("quarante-deux") 
it's a string: quarante-deux 

これまでのところは良いです。予想通り

object M2 extends Module { 
    type EitherIntOrString = Either[Int, String] 
    type Left = scala.Left[Int, String] 
    type Right = scala.Right[Int, String] 
    object Left extends ApplyAndUnApply[Int, Left] { 
    def apply(i: Int) = scala.Left(i) 
    def unapply(l: Left) = scala.Left.unapply(l) 
    } 
    object Right extends ApplyAndUnApply[String, Right] { 
    def apply(s: String) = scala.Right(s) 
    def unapply(r: Right) = scala.Right.unapply(r) 
    } 
} 

は、しかし、これは動作しません:

scala> foo(M2)(Left(42)) 
it's an int: 42 

scala> foo(M2)(Right("quarante-deux")) 
java.lang.ClassCastException: scala.Right cannot be cast to scala.Left 

正しい結果を取得する方法があります私の第二の試みはModule.EitherIntOrStringのための自然な実装としてscala.Either[Int, String]を使用するのですか?

答えて

1

問題は、この正規表現エンジンである:

intOrString match { 
    case m.Left(i) => println("it's an int: "+i) 
    case m.Right(s) => println("it's a string: "+s) 
} 

それは無条件intOrStringm.Left.unapplyを実行します。それがなぜ起こるかについては、以下を参照してください。

  • m.Left.unapplyが実際scala.Left.unapply
  • intOrStringであるM2.Left.unapplyに解決結果Right("quarante-deux")

され、scala.Left.unapplyが原因Right("quarante-deux")に呼び出された:あなたはfoo(M2)(Right("quarante-deux"))を呼び出すと

が、これは何が起こっているかでありますCCE。

これがなぜ起こるのでしょうか。私は通訳を通して、あなたのコードを実行しようとしたとき、私はこれらの警告を得た:

<console>:21: warning: abstract type m.Left in type pattern m.Left is unchecked since it is eliminated by erasure 
      case m.Left(i) => println("it's an int: "+i) 
       ^
<console>:22: warning: abstract type m.Right in type pattern m.Right is unchecked since it is eliminated by erasure 
      case m.Right(s) => println("it's a string: "+s) 
       ^

ApplyAndUnApplyunapply方法がOption unapply(Object)に消去されます。 intOrString instanceof m.Leftのようなものを実行することは不可能です(m.Leftも消去されるため)、コンパイラはこの一致をコンパイルして、すべて消去済みのunapplyを実行します。正しい結果を得るために

一つの方法は、以下の(それはケースクラスを抽象化する、あなたのオリジナルのアイデアと一緒に行くかどうかわからない)です。

trait Module { 
    type EitherIntOrString 
    type Left <: EitherIntOrString 
    type Right <: EitherIntOrString 
    val L: ApplyAndUnApply[Int, EitherIntOrString] 
    val R: ApplyAndUnApply[String, EitherIntOrString] 
} 

object M2 extends Module { 
    type EitherIntOrString = Either[Int, String] 
    type Left = scala.Left[Int, String] 
    type Right = scala.Right[Int, String] 
    object L extends ApplyAndUnApply[Int, EitherIntOrString] { 
     def apply(i: Int) = Left(i) 
     def unapply(l: EitherIntOrString) = if (l.isLeft) Left.unapply(l.asInstanceOf[Left]) else None 
    } 
    object R extends ApplyAndUnApply[String, EitherIntOrString] { 
     def apply(s: String) = Right(s) 
     def unapply(r: EitherIntOrString) = if (r.isRight) Right.unapply(r.asInstanceOf[Right]) else None 
    } 
} 
+0

グレート説明!私は私の問題を解決することに決めました...パターンマッチングを使用しないことによって:-)折り畳み機能を定義する方が簡単で堅牢です。 – betehess