Estou aprendendo programação em ML (OCaml), e anteriormente perguntei sobre as funções de ML do tipo'a -> 'b
. Agora eu tenho experimentado um pouco com funções do tipo 'a list -> 'b list
. Existem alguns exemplos simples óbvios:
let rec loop l = loop l
let return_empty l = []
let rec loop_if_not_empty = function [] -> []
| l -> loop_if_not_empty l
O que não consigo descobrir é como criar uma função que faça algo diferente de retornar a lista ou loop vazio (sem usar nenhuma função de biblioteca). Isso pode ser feito? Existe uma maneira de retornar listas não vazias?
Edit: Sim, se eu tiver uma função do tipo 'a -> 'b
, então eu posso fazer outra, ou uma função do tipo 'a list -> 'b list
, mas o que eu quero saber aqui é como fazer a primeira.
programming-languages
typing
functional-programming
Gilles 'SO- parar de ser mau'
fonte
fonte
Respostas:
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:
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çõesg : 'a -> 'c
,h : 'b -> 'd
temos:(Observe,
f
à esquerda tem o tipo'a list -> 'b list
ef
à direita é'c list -> 'd list
.)Somos livres para escolher o que
g
quisermos, então vamos'a = 'c
eg = id
. Agora desdemap id = id
(fácil de provar por indução na definição demap
), temos:Agora vamos
'b = 'd = bool
eh = not
. Vamos supor que, para alguns,zs : bool list
isso acontecef zs ≠ [] : bool list
. É claro quemap not ∘ f = f
não se sustenta, porqueSe 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 quandof
é chamado com typef : 'a list -> bool list
para any'a
,f
sempre deve retornar a lista vazia. Pode ser que quando chamamosf
,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 parabool list
, deve retornar a lista vazia paraunit 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 sem = n
ex_1
estiverem relacionados ay_1
ex_2
está relacionadoy_2
e assim por diante). E a parte divertida é, no nosso caso, uma vez quef
é polimórfico, podemos definir qualquer relação nos elementos das listas!Vamos escolher um
'a
,'b
e olharf : 'a list -> 'b list
. Agora olhef : '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 umzs : 'a list
esteja relacionado a si mesmo. Como sabemos,f
leva valores relacionados aos relacionados, isso significa quef zs : 'b list
está relacionadof 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
.fonte
g
eh
, neste caso) ir direto com as relações gerais feitos sob medida ...Vamos voltar aos objetos mais simples: você não pode construir um objeto apropriado do tipo,
'a
porque isso significa que esse objetox
pode ser usado onde quer'a
que caiba. E isso significa em toda parte: como um número inteiro, uma matriz e até uma função. Por exemplo, isso significa que você pode fazer coisas comox+2
,x.(1)
e(x 5)
. Existem tipos exatamente para evitar isso.É a mesma idéia que se aplica a uma função do tipo
'a -> 'b
, mas há alguns casos em que esse tipo pode existir: quando a função nunca retorna um objeto do tipo'b
: ao repetir ou gerar uma exceção.Isso também se aplica a funções que retornam uma lista. Se sua função é do tipo
t -> 'b list
e você cria um objeto do tipot
e o aplica a essa função, isso significa que, se você acessar com êxito um elemento desta lista, terá acesso a um objeto que possui todos os tipos. Portanto, você não pode acessar nenhum elemento da lista: a lista está vazia ou ... não há lista.No entanto, o tipo
'a list -> 'b list
aparece nos exercícios habituais, mas é apenas quando você já tem uma função do tipo'a -> 'b
:Mas você provavelmente conhece este.
fonte
'a -> 'b
ou'a list -> 'b list
, mas isso não é uma observação tão interessante. Na verdade, vou editar a pergunta para deixar claro que não é disso que o aluno mais jovem estava aprendendo a programar.f : 'a list -> 'b list
et
tal quef t <> []
, em seguida, este programa irá escrever-check, mas pode fazer maneira pior do que levantar uma exceção:let y = List.hd (f t) in (y y) (y + y.(0) + y.(0).(0))
.Teorema da Parametricidade dos "Teoremas de graça!" O artigo nos diz que os termos de ML têm uma propriedade muito especial: se considerarmos o tipo de um termo como uma relação em valores desse tipo, o valor desse termo será relacionado a ele mesmo. Aqui está como visualizar tipos como relações:
'a -> 'b
corresponde à relação definida dizendo que duas funções estão relacionadas se elas levarem valores relacionados a valores relacionados (assumindo'a
e'b
correspondendo a algumas relações).'a list
corresponde à relação definida dizendo que duas listas são relacionadas se tiverem o mesmo comprimento e seus elementos correspondentes estiverem relacionados (supondo que'a
corresponda a alguma relação).Aqui está um exemplo. Suponha que tenhamos um termoUMA1 e UMA2 , escolha absolutamente qualquer relação UMA entre elementos desse tipo e, se tomarmos alguma uma1:UMA1 e uma2:UMA2 , de modo que estejam relacionados de acordo com UMA , então foouma1 e foouma2 também será relacionado de acordo com UMA :
foo : 'a -> 'a
. O Teorema da Parametricidade diz quefoo
está relacionado a si mesmo. O que isso significa é que podemos escolher dois tipos, digamos,Agora, se tomarmos a relaçãoUMA não ser uma relação arbitrária, mas uma função f:UMA1→UMA2 , o acima se torna:
ou, em outras palavras:
que é exatamente o teorema livre para a
id
função:f . id = id . f
.Se você executar etapas semelhantes para sua funçãoUMA1 e UMA2 qualquer relação UMA entre seus elementos, dois tipos B1 e B2 qualquer relação B entre seus elementos e, em seguida, faça duas listas, primeiro feitas de elementos de UMA1 , segundo feito de elementos de UMA2 , aplique sua função às duas listas (obtendo uma lista de B1 no primeiro caso e uma lista de B2 no segundo), e os resultados serão relacionados, se as entradas foram relacionadas.
foo : 'a list -> 'b list
, poderá escolher dois tiposAgora vamos usar isso para provar que para quaisquer dois tipos
A
eB
a funçãofoo
retorna uma lista vazia para qualquer entradaas : A list
.A
e deixarØ
,B
eas
está relacionado a si mesmo (como escolhemos a relação de identidadeA
), portanto,foo as : Ø list
está relacionado afoo as : B list
.Ø
tipo.Portanto, para qualquer
A
,B
eas : A list
temos quefoo as : B list
tem que ser uma lista vazia.fonte