Bem, algo conhecido como parametridade nos diz que, se considerarmos o subconjunto puro de ML (ou seja, nenhuma recursão infinita ref
e todas essas coisas estranhas), não há como definir uma função com esse tipo além da que retorna o valor vazio. Lista.
Tudo isso começou com o artigo de Wadler, “ Teoremas de graça! ”. Este artigo, basicamente, nos diz duas coisas:
- Se considerarmos linguagens de programação que satisfazem certas condições, podemos deduzir alguns teoremas interessantes apenas observando a assinatura de tipo de uma função polimórfica (isso é chamado de Teorema da Parametricidade).
- ML (sem recursão infinita
ref
e todas essas coisas estranhas) satisfaz essas condições.
Do Parametricity Teorema nós sabemos que se nós temos uma função f : 'a list -> 'b list
, em seguida, para todos 'a
, 'b
, 'c
, 'd
e para todas as funções g : 'a -> 'c
, h : 'b -> 'd
temos:
map h ∘ f = f ∘ map g
(Observe, f
à esquerda tem o tipo 'a list -> 'b list
e f
à direita é 'c list -> 'd list
.)
Somos livres para escolher o que g
quisermos, então vamos 'a = 'c
e g = id
. Agora desde map id = id
(fácil de provar por indução na definição de map
), temos:
map h ∘ f = f
Agora vamos 'b = 'd = bool
e h = not
. Vamos supor que, para alguns, zs : bool list
isso acontece f zs ≠ [] : bool list
. É claro que map not ∘ f = f
não se sustenta, porque
(map not ∘ f) zs ≠ f zs
Se o primeiro elemento da lista à direita for true
, à esquerda o primeiro elemento é false
e vice-versa!
Isso significa que nossa suposição está errada e f zs = []
. Nós terminamos? Não.
Assumimos que 'b
é bool
. Mostramos que quando f
é chamado com type f : 'a list -> bool list
para any 'a
, f
sempre deve retornar a lista vazia. Pode ser que quando chamamos f
, f : 'a list -> unit list
pois retorna algo diferente? Nossa intuição nos diz que isso não faz sentido: simplesmente não podemos escrever em ML pura uma função que sempre retorna a lista vazia quando queremos que ela nos forneça uma lista de booleanos e, caso contrário, pode retornar uma lista não vazia! Mas isso não é uma prova.
O que queremos dizer é que f
é uniforme : se ela sempre retorna a lista vazia para bool list
, deve retornar a lista vazia para unit list
e, em geral, qualquer 'a list
. É exatamente sobre isso que trata o segundo ponto da lista de marcadores no início da minha resposta.
O documento diz-nos que no ML f
deve tomar relacionados valores relacionados queridos. Não vou entrar em detalhes sobre relações, basta dizer que as listas estão relacionadas se, e somente se, tiverem comprimentos iguais e seus elementos estiverem relacionados em pares (isto é, [x_1, x_2, ..., x_m]
e [y_1, y_2, ..., y_n]
estão relacionados se e somente se m = n
e x_1
estiverem relacionados a y_1
e x_2
está relacionado y_2
e assim por diante). E a parte divertida é, no nosso caso, uma vez que f
é polimórfico, podemos definir qualquer relação nos elementos das listas!
Vamos escolher um 'a
, 'b
e olhar f : 'a list -> 'b list
. Agora olhe f : 'a list -> bool list
; já mostramos que, nesse caso, f
sempre retorna a lista vazia. Agora postulamos que todos os elementos de 'a
estão relacionados a si mesmos (lembre-se, podemos escolher qualquer relação que desejamos), isso implica que qualquer um zs : 'a list
esteja relacionado a si mesmo. Como sabemos, f
leva valores relacionados aos relacionados, isso significa que f zs : 'b list
está relacionado f zs : bool list
, mas a segunda lista tem comprimento igual a zero e, como a primeira está relacionada a ela, ela também está vazia.
Para completar, mencionarei que há uma seção sobre o impacto da recursão geral (possível não terminação) no artigo original de Wadler, e também há um artigo explorando teoremas livres na presença de seq
.