Union and Intersection Types in Scala 3

Photo by Denys Nevozhai on Unsplash

Scala 3 aka dotty comes with a lot of new types. Union, Intersection , Opaque etc. Let’s understand union and intersection types in detail.

Union types

A union type A | B represents a type that has values of type A or B at a given time. A pattern match must be performed at the point of usage to extract the exact type.

This would not have been possible in Scala 2. We would have to create a super type for our types and then create the method with the super type.

Rules for Union types

  • Union types are commutative i.e A | B is same as B | A .
  • A and B are sub types of A | B for all values of A and B
  • Co- Variance rules for Union types : Given a co-variant type C then C[A] | C[B] <: C[A | B]

Basically we can pass a List[Int] and a List[String] to a method expecting a List[Int|String]. This makes C[A] and C[B] a sub type of C[A|B].

Also we can pass List[String]|List[Int] type where a List[String|Int] is expected. This makes C[A] | C[B] a sub type of C[A|B]. However the reverse is not possible as seen in line 24.

  • Contra-variance rules for Union types : Given a contra variant type C then C[A | B] <: C[A] and C[A | B] <: C[B]

Here, we see that we could pass a Printer[String|Int] where a Printer[Int] was required.

Intersection types

A intersection type A & B represents a type that has values of type A andB at the same time.

At line 8 we are able to use both methods from intersection type i.e monoid contains the functionality of both Semigroup and Indentity types. The intersection type will also act as a type restriction, so we need to mix in both traits if we are to use this method properly.

Rules for Intersection types

  • Intersection types are commutative i.e A & B is same as B & A .
  • Given any types A and B , A & B is a sub type for both A and B
  • Co- Variance rules for Intersection types : Given a co-variant type C then C[A & B] <: C[A] & C[B]

Here, we see that we can provide an Option[TwitterPost & InstagramPost]when we need a Option[TwitterPost] or a Option[InstagramPost]. This also leads us to the fact that when we need an Option[TwitterPost] & Option[InstagramPost] we can provide a Option[TwitterPost & InstagramPost] .

  • Given a contra-variant type C a C[A |B] is a sub type of C[A] & C[B]

What happens here is that a Printer instance which knows how to print Int|String can be passed when we need a Printer[Int] & Printer[String]. This is exactly what is done is line 15.

  • what happens the two intersected types share a method signature except the returned types?

Here both Singer and Dancer types have a method called share with the same signature but different return types. An intersection type of these two upon calling the share method must return both an InstagramPost and a TwitterPost. The only type that can do that is an intersection i.e TwitterPost & InstagramPost.

Conclusion

We explored Union and Intersection types in Scala 3 and checked how these types work with variance rules in Scala. Checkout the official dotty docs for further reading