2017-07-22 3 views
4

Ich möchte einen A|B Typ haben, der den Untertyp A|B|C darstellt. Ist das in Scala möglich? Wenn ja, wie?union types in scala mit Subtypisierung: A | B <: A | B | C

Ich hatte gehofft, dass ich implicitly[¬¬[IF] <:< T] unten kompilieren kann (Originalcode here), aber es tut es nicht. Gibt es eine Möglichkeit, diesen Code zu reparieren, um Subtyping zu ermöglichen?

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 

}

Ich habe auch versucht, diese (von 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 

} 

aber hier kann ich nicht eine Union Typ "First-Class" machen können sie nur als Argument Typen existieren, nicht als Rückgabetypen.

gleiche Problem mit this Ansatz:

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 
    } 

} 
+0

verwandt: https://contributors.scala-lang.org/t/dotty-style-union-types-in-scala/733/2 – jhegedus

Antwort

1

Die Vereinigung Art von Scala.js (source und tests) unterstützt A | B Subtyp von A | B | C. Es unterstützt sogar Permutationen wie Subtyp B | C | A.

+0

Fantastisch, ich denke, Scala kann dies nicht auf der Server-Seite unterstützen? Oder kann ich sie auch auf der Serverseite verwenden? Verwenden sie etwas Scala.js spezifisch? Es scheint mir, dass dies auch auf der Serverseite funktionieren kann, mit ein bisschen Basteln ... oder? – jhegedus

+1

Es könnte hauptsächlich auf der Serverseite laufen, aber Sie müssten einen abstrakten Typ | [A, B] 'anstelle eines' versiegelten Merkmals' verwenden, so dass dessen Löschung "Objekt" ist und es erfolgreich ausgeführt wird . Das hat einen Nachteil, nämlich dass es kein begleitendes Objekt gibt, wo die Implikate dazu führen können, dass sie automatisch in den impliziten Bereich gebracht werden. Dies bedeutet, dass Sie an Orten, an denen Sie die "Subtyping-Beziehung" halten müssen, einen Import benötigen. Während Sie in Scala.js nur den Import benötigen, wenn Sie 'A | B'. – sjrd

+0

Danke, ich muss sehen, wie ich das schaffen kann und über diesen Unterschied zwischen Scala.js und Scala nachdenken. – jhegedus

1

Ihre erste Annäherung funktioniert OK für mich. Es funktioniert auch mit Permutationen von Typen.

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] 

Natürlich sollten Sie nicht heben IF in einen Union-Typ mit ¬¬[IF], denn es ist schon eine Vereinigung Art. Sie müssen ¬¬[Int] tun, nur weil Int in diesem Ansatz kein Union-Typ ist.

+0

Interessant, danke! Kann ich 'IF' als Rückgabetyp in einer Funktion verwenden? – jhegedus

Verwandte Themen