【问题标题】:Write Ceylon like union type in Scala, using an alias for Either在 Scala 中编写类似联合类型的 Ceylon,使用 Either 的别名
【发布时间】:2013-11-14 13:05:00
【问题描述】:

随着 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 以外的新类型类,该解决方案也应该允许详尽 模式匹配。

【问题讨论】:

  • @AlexIv 是的,这也包含在链接的问题中。 size 的最终定义的问题是模式匹配器不知道类型。例如,您可以在没有警告的情况下离开 case s: String 案例。您可以添加一个无用的案例,如case b: Boolean 而不会出错。
  • 据我了解,unapply issues.scala-lang.org/browse/SI-884 中不允许使用类型参数
  • @SergeyPassichenko 好的,谢谢!在我的第二次尝试中(下面作为“答案”),我有一个不使用类型应用程序的模式。也许这是一个更好的解决方案起点。

标签: scala types


【解决方案1】:

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

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")

【讨论】:

    【解决方案2】:

    我的尝试。没有通用提取器。以后再想想怎么弄。

    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"))
    

    【讨论】:

      【解决方案3】:

      我也有一个或多或少的 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])
      

      【讨论】:

        【解决方案4】:

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

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

        (Scastie link)

        【讨论】:

          猜你喜欢
          • 2023-04-03
          • 2019-02-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-30
          • 1970-01-01
          相关资源
          最近更新 更多