随着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"
}
但是模式提取器不能编译。有什么想法吗?
我知道that a similar question exists有一些有用的答案,但我特别想知道是否可以使用Either
和提取器的类型别名。即使定义了Either
以外的新类型,解决方案也应该允许穷举模式匹配。
答案 0 :(得分:0)
这是第二次尝试,以防万一。它以隐式解决方式失败:
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")
答案 1 :(得分: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"))
答案 2 :(得分:0)
我也有一个或多或少的工作实现Miles Sabin的C-H想法(上面链接)。不确定这是否直接解决了你的问题,但也许这是有用的思考。
源代码为in this project on GitHub。
以下是一个快速使用示例:
type ISB = union [Int] #or [String] #or [Boolean]
def unionFunction[T: prove [ISB] #containsType](t: T) {}
unionFunction(55)
unionFunction("hello")
unionFunction(false)
// unionFunction(math.Pi) // fails to compile (correct behavior)
还提供了一个用于装箱这些类型的类,因为这通常很方便:
val wrapped = new Union[union [Int] #or [String]]
wrapped.contains[Int] // true
wrapped.contains[String] // true
wrapped.contains[Double] // false
wrapped assign 55
wrapped.value[Int] // Some(55)
wrapped.value[String] // None
wrapped assign "hi, union!"
wrapped.value[String] // Some("hi, union!")
wrapped.value[Int] // None
def unionFunction[T: wrapped.containsType] {}
unionFunction[Int] // compiles :)
unionFunction[String] // compiles :)
另一个有趣的事情是API允许联合类型一次构建一个成员类型,如下所示:
val unary = new Union[union [Int] #apply]
val binary = new Union[unary.underlying #or [String]]
val ternary = new Union[binary.underlying #or [Float]]
ternary.typeMembers // Seq(typeOf[Int], typeOf[String], typeOf[Float])
答案 3 :(得分:0)
对于记录,Dotty具有联合类型,因此示例为
def test(x: String | Int): String = x match {
case _: String => "found string"
case _: Int => "found int"
}
println(test("foo bar"))