Por que precisamos de tipos de soma?
Imagine uma linguagem que não permita vários construtores de valor para um tipo de dados. Em vez de escrever
data Color = White | Black | Blue
Nós teríamos
data White = White
data Black = Black
data Blue = Black
type Color = White :|: Black :|: Blue
Onde:|:
(aqui não é|
para evitar confusão com tipos de soma) é um operador de união de tipo interno. A correspondência de padrões funcionaria da mesma maneira
show :: Color -> String
show White = "white"
show Black = "black"
show Blue = "blue"
Como você pode ver, ao contrário dos coprodutos, resulta em uma estrutura plana, para que você não precise lidar com injeções. E, diferentemente dos tipos de soma, permite combinar tipos aleatoriamente, resultando em maior flexibilidade e granularidade:
type ColorsStartingWithB = Black :|: Blue
Eu acredito que não seria um problema construir tipos de dados recursivos também
data Nil = Nil
data Cons a = Cons a (List a)
type List a = Cons a :|: Nil
Sei que tipos de união estão presentes no TypeScript e provavelmente em outras linguagens, mas por que o comitê Haskell escolheu os ADTs sobre eles?