Ответ 1
Тип объединения Scala.js(источник и tests) поддерживает подтип A | B
A | B | C
. Он даже поддерживает подстановки, такие как A | B
подтип B | C | A
.
Я хотел бы иметь тип A|B
для подтипа A|B|C
. Возможно ли кодирование в Scala? Если да, то как?
Я надеялся, что я смогу сделать implicitly[¬¬[IF] <:< T]
компиляцию ниже (исходный код здесь), но это не так. Есть ли способ исправить этот код, чтобы разрешить подтипирование?
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
}
Я также пробовал это (от здесь):
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
}
но здесь я не могу сделать тип объединения "первоклассным", они могут существовать только как типы аргументов, а не как типы возврата.
Такая же проблема с этим:
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.js(источник и tests) поддерживает подтип A | B
A | B | C
. Он даже поддерживает подстановки, такие как A | B
подтип B | C | A
.
Ваш первый подход работает нормально для меня. Он также работает с перестановками типов.
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
не является типом объединения в этом подходе.