Vamos começar em um idioma total como o Agda. Então, como afirma Gallais, isso só faz sentido se, por "tipo vazio", você quer dizer o tipo de unidade, ou seja, a tupla 0-ária, que tem exatamente um valor. O tipo vazio pode ser pensado como um tipo de soma de maiúsculas e minúsculas e não possui nenhum valor. No Agda, você pode facilmente provar que isso Unit -> A
é isomórfico A
. Nesse sentido, você pode considerá-los iguais, embora ainda não sejam literalmente iguais. Não posso, por exemplo, fazer uma análise de caso Unit -> Bool
nem aplicar True : Bool
a nada como uma função.
A história para Haskell é bem diferente. () -> A
e A
são tipos semanticamente não isomórficos. Em particular, () -> ()
tem quatro valores, enquanto ()
tem apenas 2. Os quatro observacionalmente distintos valores são undefined
, \_ -> undefined
, \x -> x
, \_ -> ()
. Portanto, ()
não é realmente um tipo de unidade, no sentido de que existe exatamente uma função ()
. (Na Agda, por outro lado, podemos provar que se x : Unit
e y : Unit
, então, são iguais [definicionalmente, se definirmos Unit
com a record
sintaxe em oposição à data
sintaxe]. Isso significa que Unit
tem apenas um valor. Além disso, podemos provar que Unit
e A -> Unit
são isomórficos para qualquer um A
.)
De fato, um tipo "vazio", como Void
definido, data Void
está mais próximo de ser um tipo de unidade nesse sentido. Void
tem apenas um valor, mas Void -> Void
ainda tem dois. De fato, todo tipo de função A -> B
tem pelo menos dois valores observacionalmente distintos, a saber, undefined
e \_ -> undefined
. Portanto, Haskell não possui uma unidade verdadeira ou um tipo de vazio.
Muito disso se deve ao fato de Haskell ser uma linguagem não estrita e é exasperada pela existência de seq
(e seus equivalentes). Por exemplo, a distinção entre undefined
e \_ -> undefined
só pode ser vista com seq
. Se eliminássemos seq
e seus equivalentes de Haskell, Void
serviríamos como um tipo de unidade, embora, ironicamente, ainda não fosse um tipo vazio.
Geralmente, quando as pessoas falam sobre essas coisas em Haskell, elas estão fingindo tacitamente que Haskell é uma linguagem mais bem comportada do que é. Ou seja, eles assumem que os fundos não existem para seus propósitos, ou seja, você está trabalhando em um idioma total como o Agda. Para os propósitos de projetar seu código, isso geralmente é adequado; não é comum nos preocuparmos ou esperarmos fundos. Essas distinções podem se tornar importantes se estivermos fazendo algo como programação circular ou se as garantias de segurança de nosso programa dependem dessas propriedades, por exemplo, uma função nunca pode ser chamada se tiver um tipo vazio como domínio.