0

Does sum type have short denotation in Scala?

For example:

Product type -(Integer, Double)

Funtion type -(Integer=>Double)

Sum type -?

alex94
  • 13
  • 1
  • Possible duplicate of [How to define "type disjunction" (union types)?](https://stackoverflow.com/questions/3508077/how-to-define-type-disjunction-union-types) – Dmytro Mitin Mar 23 '19 at 22:03
  • @DmytroMitin I somehow got the impression that *sum types* are specifically **tagged** union types (`Either`, scalaz's `\/`), both of your links seem to be about untagged union types, which are more about subtyping, not about disjoint unions? The "default category" (types, functions) and the subtyping poset are slightly different things... Maybe OP could clarify what exactly they want to know (in particular, what's the problem with `Either`? It's not *that* long, given that it's not used all *that* often.) – Andrey Tyukin Mar 23 '19 at 22:10
  • @AndreyTyukin https://ncatlab.org/nlab/show/sum+type – Dmytro Mitin Mar 23 '19 at 22:17
  • @DmytroMitin Well, yes... Now, you first proposed the duplicate mentioning [union types](https://stackoverflow.com/a/29979396/2707792), which are essentially more like a join in the `<:`-poset, whereas the nlab link speaks specifically about the disjoint union / the coproduct (with separate `inl`, `inr` inclusions). It holds `implicitly[Int | Int =:= Int]`, but not `implicitly[Either[Int, Int] =:= Int]` (at least in Dotty). – Andrey Tyukin Mar 23 '19 at 22:24

2 Answers2

4

No, there is no syntactic sugar for defining sum types in Scala. In fact, Scala does not have sum types in its type system as a distinct feature. They need to be encoded somehow, the most widely-used encoding is as closed subtyping hierarchies, i.e. the sum type

A = B + C

is encoded as

sealed trait A

final class B extends A
final class C extends A

Note that Scala also does not have syntactic sugar for product types either. The syntax you mentioned is for tuple types, not product types. Tuple types are merely one of many different classes of product types. E.g. case classes are another class of product types.

Note also that Scala does not have a notion of product types in its type system either. They are encoded as parametric types that are subtypes of one of the ProductN types. I.e. the tuple type (A, B) is actually syntactic sugar for

Tuple2[A, B]

which is defined as

final case class Tuple2[+A, +B](_1: A, _2: B) extends Product2[A, B] with Product with Serializable

Of course, classes and traits can be seen as variants of records, so in some sense, every type in Scala can be seen as a product type, which is why it is so easy to encode them.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
3

In Scala, the sum type constructor is given by scala.util.Either. If you need tagged sum of more than two types (e.g. A, B, C), then it usually makes sense to create a custom sealed trait:

sealed trait AorBorC

case class CaseA(a: A) extends AorBorC
case class CaseB(b: B) extends AorBorC
case class CaseC(c: C) extends AorBorC

and then work with pattern matching.

You can of course define shortcuts, if you want:

type +[A, B] = Either[A, B]
type \/[A, B] = Either[A, B]

and then use them in infix notation A + B or A \/ B.

You can also use \/ from ScalaZ.


The util.Either is indeed the coproduct as explained here:

  • Formation: for any two types A, B, you can form the type Either[A, B] (there are no restrictions)
  • Introduction: if a: A and b: B, then both Left(a) and Right(b) are values of type Either[A, B].
  • Elimination: if e: Either[A, B], then the usual pattern matching can be used to collapse both cases to a single type C:

    e match {
      case Left(a) => c1(a) // assuming `c1(a): C`
      case Right(b) => c2(b) // assuming `c2(b): C`
    }
    

    There is also fold:

    e.fold(c1, c2)
    

    which does essentially the same, and also produces a value of type C. The meaning should be clear:

    Left(a).fold(c1, c2) == c1(a)
    Right(b).fold(c1, c2) == c2(b)
    

This is indeed the sum type of A and B, because for any other type C and any two functions c1: A => C, c2: B => C there is the function (e: Either[A, B]) => e.fold(c1, c2), for which the above two identities hold, which is exactly the universal property required by the definition of a coproduct.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93