9

Ceylon 1.0 のリリースに伴い、一部の人々は共用体型の有用性について議論しています。次のコードをどれだけ簡潔に記述できるか疑問に思っていました。

String test(String | Integer x) {
  if (x is String) {
     return "found string";
  } else if (x is Integer) {
     return "found int";
  }
  return "why is this line needed?";
}

print(test("foo bar"));  // generates 'timeout'... well, whatever

...Scalaで?私の考えは次のようなものでした:

type | [+A, +B] = Either[A, B]

object is {
  def unapply[A](or: Or[A]): Option[A] = or.toOption

  object Or {
    implicit def left[A](either: Either[A, Any]): Or[A] = new Or[A] {
      def toOption = either.left.toOption
    }
    implicit def right[A](either: Either[Any, A]): Or[A] = new Or[A] {
      def toOption = either.right.toOption
    }
  }
  sealed trait Or[A] { def toOption: Option[A] }
}

def test(x: String | Int) = x match {
  case is[String](s) => "found string"   // doesn't compile
  case is[Int   ](i) => "found int"
}

しかし、パターン エクストラクタはコンパイルされません。何か案は?

同様の質問がいくつかの有効な回答とともに存在することは知っていますが、特に、Eitherおよび抽出子の型エイリアスを使用できるかどうかは疑問です。以外の新しい型クラスを定義したとしてもEither、ソリューションは徹底的なパターン マッチを可能にする必要があります。

4

4 に答える 4

0

私の試み。汎用エクストラクターなし。入手方法は後で考えようと思います。

sealed trait | [+A, +B]

case class Left[+A](left: A) extends |[A, Nothing]

case class Right[+B](right: B) extends |[Nothing, B]

implicit def toLeft[A, B](a: A): |[A, B] = Left(a)
implicit def toRight[A, B](b: B): |[A, B] = Right(b)

object IsString {
    def unapply(x: |[_, _]): Option[String] = {
        x match {
            case Left(s: String) => Some(s)
            case Right(s: String) => Some(s)
            case _ => None
        }
    }
}

object IsInt {
    def unapply(x: |[_, _]): Option[Int] = {
        x match {
            case Left(i: Int) => Some(i)
            case Right(i: Int) => Some(i)
            case _ => None
        }
    }
}

def test(x: String | Int) = x match {
  case IsString(s) => s"found string: $s"
  case IsInt(i)    => s"found int: $i"
}

println(test(10))
println(test("str"))
于 2013-11-14T18:21:43.103 に答える
0

役立つ場合に備えて、ここで 2 回目の試行を行います。暗黙の解決で失敗します。

trait OrLike {
  type A
  type B

  def left : Option[A]
  def right: Option[B]
}

object | {
  implicit def left[A, B](value: A): | [A, B] = new | [A, B] {
    def left  = Some(value)
    def right = None
  }

  implicit def right[A, B](value: B): | [A, B] = new | [A, B] {
    def left  = None
    def right = Some(value)
  }
}
sealed trait | [A1, B1] extends OrLike {
  type A = A1
  type B = B1
}

 

object is {
  def unapply[A](or: OrLike)(implicit ev: Or[A]): Option[A] = ev.toOption

  object Or {
    implicit def left[A1](peer: OrLike { type A = A1 }): Or[A1] = new Or[A1] {
      def toOption = peer.left
    }
    implicit def right[B1](peer: OrLike { type B = B1 }): Or[B1] = new Or[B1] {
      def toOption = peer.right
    }
  }
  sealed trait Or[A] { def toOption: Option[A] }
}

def test(x: String | Int) = x match {
  case s is String => "found string"  // no evidence of `Or[A]`
  case i is Int    => "found int"
}

test("foo")
于 2013-11-14T13:27:58.140 に答える