【问题标题】:union types in scala with subtyping: A|B <: A|B|C带有子类型的scala中的联合类型:A | B <:A | B | C
【发布时间】:2017-12-28 13:04:45
【问题描述】:

我希望有一个A|B 类型作为A|B|C 的子类型。这可以在 Scala 中编码吗?如果是,如何?

我希望我可以在下面编译implicitly[¬¬[IF] &lt;:&lt; T](原始代码here),但事实并非如此。有没有办法修复此代码以允许子类型化?

object NUnion{
  type ¬¬[A] = ¬[¬[A]]

  type ¬[A] = A => Nothing
  trait Disj[T] {
    type or[S] = Disj[T with ¬[S]]
    type apply = ¬[T]
  }

  // for convenience
  type disj[T] = { type or[S] = Disj[¬[T]]#or[S] }


  type T = disj[Int]#or[Float]#or[String]#apply
  type IF = disj[Int]#or[Float]#apply
  implicitly[¬¬[Int] <:< T] // works
  // implicitly[¬¬[Double] <:< T] // doesn't work
  // implicitly[¬¬[IF] <:< T] // doesn't work - but it should

}

我也试过这个(来自here):

object Kerr{
  def f[A](a: A)(implicit ev: (Int with String with Boolean) <:< A) = a match {
     case i: Int => i + 1
     case s: String => s.length
  }

  f(1) //works
  f("bla") // works
  def g[R]()(implicit ev: (Int with String with Boolean) <:< R):R = "go" // does not work

}

但在这里我不能将联合类型设为“一流”,它们只能作为参数类型存在,不能作为返回类型存在。

this 方法也有同样的问题:

object Map{
  object Union {
    import scala.language.higherKinds

    sealed trait ¬[-A]

    sealed trait TSet {
      type Compound[A]
      type Map[F[_]] <: TSet
    }

    sealed trait ∅ extends TSet {
      type Compound[A] = A
      type Map[F[_]] = ∅
    }

    // Note that this type is left-associative for the sake of concision.
    sealed trait ∨[T <: TSet, H] extends TSet {
      // Given a type of the form `∅ ∨ A ∨ B ∨ ...` and parameter `X`, we want to produce the type
      // `¬[A] with ¬[B] with ... <:< ¬[X]`.
      type Member[X] = T#Map[¬]#Compound[¬[H]] <:< ¬[X]

      // This could be generalized as a fold, but for concision we leave it as is.
      type Compound[A] = T#Compound[H with A]

      type Map[F[_]] = T#Map[F] ∨ F[H]
    }

    def foo[A : (∅ ∨ String ∨ Int ∨ List[Int])#Member](a: A): String = a match {
      case s: String => "String"
      case i: Int => "Int"
      case l: List[_] => "List[Int]"
    }


    def geza[A : (∅ ∨ String ∨ Int ∨ List[Int])#Member] : A = "45" // does not work 


    foo(geza)

    foo(42)
    foo("bar")
    foo(List(1, 2, 3))
//    foo(42d) // error
//    foo[Any](???) // error
  }

}

【问题讨论】:

标签: scala subtyping union-types


【解决方案1】:

Scala.js 的联合类型(sourcetests)支持 A | B | C 的子类型 A | B。它甚至支持A | B 子类型B | C | A 之类的排列。

【讨论】:

  • 太棒了,我猜 Scala 不能在服务器端支持这个?或者我可以在服务器端使用它们吗?他们使用 Scala.js 特定的东西吗?在我看来,这也可以在服务器端工作,只需稍加修改......对吗?
  • 它主要可以在服务器端运行,但是您必须使用抽象的type |[A, B] 而不是sealed trait,以便它的擦除是Object 并成功转换.这有一个缺点,那就是没有伴随对象可以让它们自动被带入隐式范围。这意味着在您需要“子类型关系”保持的地方,您需要导入。而在 Scala.js 中,您只需要在写下 A | B 时进行导入。
  • 谢谢,需要看看我如何才能完成这项工作,并考虑 Scala.js 和 Scala 之间的擦除差异。
  • 我找到了一种为type | 提供隐式范围的方法(不更改其擦除):gist.github.com/Jasper-M/96ca24e8d2aa6160159abe2c4f745206
【解决方案2】:

您的第一种方法对我来说没问题。它也适用于类型的排列。

type IFS = disj[Int]#or[Float]#or[String]#apply
type IF = disj[Int]#or[Float]#apply
type IS = disj[Int]#or[String]#apply
type IFD = disj[Int]#or[Float]#or[Double]#apply
type FI = disj[Float]#or[Int]#apply

scala> implicitly[IF <:< IFS]
res0: <:<[IF,IFS] = <function1>

scala> implicitly[IS <:< IFS]
res1: <:<[IS,IFS] = <function1>

scala> implicitly[FI <:< IFS]
res2: <:<[FI,IFS] = <function1>

scala> implicitly[IFD <:< IFS]
<console>:18: error: Cannot prove that IFD <:< IFS.
       implicitly[IFD <:< IFS]

当然,您不应该将IF 提升为与¬¬[IF] 的联合类型,因为它已经是联合类型。你需要做¬¬[Int],只是因为Int在这种方法中不是联合类型。

【讨论】:

  • 有趣,谢谢!我可以在函数中使用IF 作为返回类型吗?
猜你喜欢
  • 1970-01-01
  • 2017-04-19
  • 2021-08-25
  • 2020-12-25
  • 1970-01-01
  • 2012-03-18
  • 1970-01-01
  • 2020-03-03
  • 1970-01-01
相关资源
最近更新 更多