Intersection Types - More Details

Syntax

Syntactically, the type S & T is an infix type, where the infix operator is &. The operator & is a normal identifier with the usual precedence and subject to usual resolving rules. Unless shadowed by another definition, it resolves to the type scala.&, which acts as a type alias to an internal representation of intersection types.

Type              ::=  ...| InfixType
InfixType         ::=  RefinedType {id [nl] RefinedType}

Subtyping Rules

T <: A    T <: B
----------------
  T <: A & B

    A <: T
----------------
    A & B <: T

    B <: T
----------------
    A & B <: T

From the rules above, we can show that & is commutative: A & B <: B & A for any type A and B.

   B <: B           A <: A
----------       -----------
A & B <: B       A & B <: A
---------------------------
       A & B  <:  B & A

In another word, A & B is the same type as B & A, in the sense that the two types have the same values and are subtypes of each other.

If C is a type constructor, then C[A] & C[B] can be simplified using the following three rules:

  • If C is covariant, C[A] & C[B] ~> C[A & B]
  • If C is contravariant, C[A] & C[B] ~> C[A | B]
  • If C is non-variant, emit a compile error

When C is covariant, C[A & B] <: C[A] & C[B] can be derived:

    A <: A                  B <: B
  ----------               ---------
  A & B <: A               A & B <: B
---------------         -----------------
C[A & B] <: C[A]          C[A & B] <: C[B]
------------------------------------------
      C[A & B] <: C[A] & C[B]

When C is contravariant, C[A | B] <: C[A] & C[B] can be derived:

    A <: A                        B <: B
  ----------                     ---------
  A <: A | B                     B <: A | B
-------------------           ----------------
C[A | B] <: C[A]              C[A | B] <: C[B]
--------------------------------------------------
            C[A | B] <: C[A] & C[B]

Erasure

The erased type for S & T is the erased glb (greatest lower bound) of the erased type of S and T. The rules for erasure of intersection types are given below in pseudocode:

|S & T| = glb(|S|, |T|)

glb(JArray(A), JArray(B)) = JArray(glb(A, B))
glb(JArray(T), _)         = JArray(T)
glb(_, JArray(T))         = JArray(T)
glb(A, B)                 = A                     if A extends B
glb(A, B)                 = B                     if B extends A
glb(A, _)                 = A                     if A is not a trait
glb(_, B)                 = B                     if B is not a trait
glb(A, _)                 = A                     // use first

In the above, |T| means the erased type of T, JArray refers to the type of Java Array.

See also: TypeErasure#erasedGlb.

Relationship with Compound Type (with)

Intersection types A & B replace compound types A with B in Scala 2. For the moment, the syntax A with B is still allowed and interpreted as A & B, but its usage as a type (as opposed to in a new or extends clause) will be deprecated and removed in the future.