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

...在斯卡拉?我的想法是这样的:

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

这是第二次尝试,以防万一。它以隐式解析失败:

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 回答
0

我也有一个或多或少的 Miles Sabin 的 CH 想法的工作实现(上面链接)。不确定这是否直接解决了您的问题,但也许这是有用的思考食物。

源代码在 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])
于 2013-11-14T21:59:32.810 回答
0

作为记录,Dotty 有联合类型,所以这个例子是

def test(x: String | Int): String = x match {
  case _: String => "found string"
  case _: Int    => "found int"
}

println(test("foo bar"))

斯卡斯蒂链接

于 2017-12-26T15:37:32.327 回答