Uma maneira de interpretar tipos como lógica é como as condições de existência para valores do tipo de retorno. Então f :: a -> [a]
, o teorema de que, se existe um valor de tipo a
, existe um valor de tipo [a]
. A implementação da função é a prova da proposição.
Aqui está uma explicação mais detalhada:
Basicamente, os construtores de dados permitem criar coisas semelhantes a somas e produtos (OR e AND), mas podemos ter várias variantes e podemos "marcar" especialmente o tipo com um nome para distingui-lo (como o nome List
).
Eles também nos permitem construí-los recursivamente: para uma proposição uma, a proposição [ a ] pode ser visto como uma solução para a equação
x ( a )⟺⊤ ∨ ( um ∧ x ( um ) )
As coisas ficam um pouco mais claras quando você escreve a definição de Lista usando o estilo GADT, pseudocódigo semelhante ao que você vê no Agda:
data List : Type -> Type where
Nil : ∀ a . List a
Cons : ∀ a . a -> List a -> List a
Isso nos dá duas coisas: os construtores (ou funções), que atuam como axiomas para as proposições de List
, e axiomas para correspondê-los ou desconstruí-los.
Grosso modo, ele introduz os seguintes axiomas na lógica:
- Para qualquer proposição uma, [ a ] detém.
- E se uma detém e [ a ] segura, então [ a ] detém
- E se [ a ] mantém, então ⊤ detém, ou a∧[a] detém.
Estes são bastante inúteis quando interpretados como lógicos, pois sempre sabemos ⊤ desconstruir não nos fornece muita informação útil
Sem quantificadores ou extensões de tipo mais poderosas (GADTs, famílias de tipos, tipos dependentes etc.), você pode ver que não podemos realmente provar coisas interessantes, e é por isso que muitas vezes você não vê muito sobre a interpretação de tipos padrão Haskell como lógica .