Estou começando a entender como a forall
palavra-chave é usada nos chamados "tipos existenciais" como este:
data ShowBox = forall s. Show s => SB s
Este é apenas um subconjunto, no entanto, de como forall
é usado e eu simplesmente não consigo entender meu uso em coisas como esta:
runST :: forall a. (forall s. ST s a) -> a
Ou explicando por que são diferentes:
foo :: (forall a. a -> a) -> (Char, Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Ou o RankNTypes
material todo ...
Eu tendem a preferir um inglês claro e sem jargões, em vez dos tipos de idioma normais em ambientes acadêmicos. A maioria das explicações que tento ler sobre isso (as que posso encontrar nos mecanismos de pesquisa) tem esses problemas:
- Eles estão incompletos. Eles explicam uma parte do uso desta palavra-chave (como "tipos existenciais") que me faz sentir feliz até que eu ler o código que usa-lo em uma maneira completamente diferente (como
runST
,foo
ebar
acima). - Eles estão densamente repletos de suposições que eu li sobre o que há de mais recente em qualquer ramo da matemática discreta, teoria das categorias ou álgebra abstrata que é popular nesta semana. (Se eu nunca mais ler as palavras "consulte o documento, para maiores detalhes sobre a implementação", será muito cedo.)
- Eles são escritos de maneiras que frequentemente transformam conceitos simples em gramática e semântica tortuosamente distorcidas e fraturadas.
Assim...
Para a questão real. Alguém pode explicar completamente a forall
palavra - chave em inglês claro e claro (ou, se existir em algum lugar, apontar para uma explicação tão clara que eu perdi) que não presuma que eu sou um matemático mergulhado no jargão?
Editado para adicionar:
Havia duas respostas destacadas das de alta qualidade abaixo, mas infelizmente só posso escolher uma como a melhor. A resposta de Norman foi detalhada e útil, explicando as coisas de uma maneira que mostrava algumas das bases teóricas forall
e, ao mesmo tempo, mostrando-me algumas das implicações práticas dela. resposta de yairchucobriu uma área que ninguém mais mencionou (variáveis de tipo com escopo definido) e ilustrou todos os conceitos com código e uma sessão do GHCi. Se fosse possível selecionar os dois da melhor maneira, eu selecionaria. Infelizmente, não posso e, depois de examinar atentamente as duas respostas, decidi que o yairchu fica um pouco mais afastado do Norman por causa do código ilustrativo e da explicação anexa. Isso é um pouco injusto, no entanto, porque realmente eu precisava das duas respostas para entender isso a tal ponto que forall
não me deixa com um leve senso de pavor quando a vejo em uma assinatura de tipo.
Respostas:
Vamos começar com um exemplo de código:
Esse código não é compilado (erro de sintaxe) no Haskell 98 simples. Requer uma extensão para suportar a
forall
palavra - chave.Basicamente, existem 3 diferentes usos comuns para a
forall
palavra-chave (ou pelo menos assim parece ), e cada um tem sua própria extensão Haskell:ScopedTypeVariables
,RankNTypes
/Rank2Types
,ExistentialQuantification
.O código acima não recebe um erro de sintaxe com nenhum dos habilitados, mas apenas verifica o tipo com
ScopedTypeVariables
ativado.Variáveis do tipo de escopo:
Variáveis de tipo com escopo definido ajudam a especificar tipos de código dentro de
where
cláusulas. Faz ob
noval :: b
mesmo que ob
infoob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
.Um ponto confuso : você pode ouvir que, quando omite o
forall
de um tipo, ele ainda está implicitamente presente. ( da resposta de Norman: "normalmente essas línguas omitem o conjunto dos tipos polimórficos" ). Esta afirmação está correta, mas refere-se aos outros usosforall
e não aoScopedTypeVariables
uso.Tipos Rank-N:
Vamos começar com o
mayb :: b -> (a -> b) -> Maybe a -> b
equivalente amayb :: forall a b. b -> (a -> b) -> Maybe a -> b
, exceto quandoScopedTypeVariables
está ativado.Isso significa que ele funciona para todos
a
eb
.Digamos que você queira fazer algo assim.
Qual deve ser o tipo disso
liftTup
? ÉliftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
. Para ver o porquê, vamos tentar codificá-lo:"Hmm .. por que o GHC infere que a tupla deve conter duas do mesmo tipo? Vamos dizer que elas não precisam ser"
Hmm. então aqui GHC não vamos aplicar
liftFunc
emv
causav :: b
eliftFunc
quer umx
. Nós realmente queremos que nossa função obtenha uma função que aceite qualquer possívelx
!Portanto, não é isso
liftTup
que funciona para todosx
, é a função que ele obtém.Quantificação Existencial:
Vamos usar um exemplo:
Como isso é diferente dos Rank-N-Types?
Com Rank-N-Types,
forall a
significa que sua expressão deve atender a todos osa
s possíveis . Por exemplo:Uma lista vazia funciona como uma lista de qualquer tipo.
Portanto, com Quantificação Existencial,
forall
s nasdata
definições significa que, o valor contido pode ser de qualquer tipo adequado, não que deve ser de todos os tipos adequados.fonte
ScopedTypeVariables
parece pior do que é. Se você escrever o tipob -> (a -> b) -> Maybe a -> b
com esta extensão, ainda será exatamente equivalente aforall a b. b -> (a -> b) -> Maybe a -> b
. No entanto, se você quiser se referir ao mesmob
(e não tê-lo quantificado implicitamente) , precisará escrever a versão quantificada explicitamente. Caso contrário,STV
seria uma extensão extremamente intrusiva.ScopedTypeVariables
e não acho que seja ruim. imho, é uma ferramenta muito útil para o processo de programação, e especialmente para iniciantes em Haskell, e sou grato por ele existir.Não. (Bem, talvez Don Stewart possa.)
Aqui estão as barreiras para uma explicação simples e clara ou
forall
:É um quantificador. Você precisa ter pelo menos um pouco de lógica (cálculo predicado) para ver um quantificador universal ou existencial. Se você nunca viu cálculo de predicado ou não se sente à vontade com quantificadores (e eu vi estudantes durante os exames de qualificação para doutorado que não se sentem à vontade), então para você, não há uma explicação fácil
forall
.É um quantificador de tipo . Se você não viu o Sistema F e aprendeu a escrever tipos polimórficos, ficará
forall
confuso. A experiência com Haskell ou ML não é suficiente, porque normalmente esses idiomas omitem osforall
tipos polimórficos. (Na minha opinião, isso é um erro de design de linguagem.)Em Haskell, em particular,
forall
é usado de maneiras que acho confusas. (Eu não sou um teórico de tipos, mas meu trabalho me põe em contato com muita teoria de tipos, e estou bastante à vontade com ela.) Para mim, a principal fonte de confusão é que elaforall
é usada para codificar um tipo que Eu mesmo preferiria escrever comexists
. Isso é justificado por um pouco complicado de isomorfismo de tipo envolvendo quantificadores e setas, e toda vez que quero entender, tenho que procurar as coisas e descobrir o isomorfismo.Se você não se sente à vontade com a idéia de isomorfismo de tipo, ou se não tem prática de pensar em isomorfismos de tipo, esse uso
forall
irá impedi-lo.Embora o conceito geral de
forall
seja sempre o mesmo (obrigatório para introduzir uma variável de tipo), os detalhes de diferentes usos podem variar significativamente. O inglês informal não é uma ferramenta muito boa para explicar as variações. Para realmente entender o que está acontecendo, você precisa de matemática. Nesse caso, a matemática relevante pode ser encontrada no texto introdutório de Benjamin Pierce, Tipos e linguagens de programação , que é um livro muito bom.Quanto aos seus exemplos particulares,
runST
deve fazer sua cabeça doer. Tipos de classificação mais alta (todos à esquerda de uma flecha) raramente são encontrados na natureza. Convido você a ler o artigo que apresentourunST
: "Threads de Estado Funcional Preguiçoso" . Este é um artigo realmente bom, e lhe dará uma intuição muito melhor para o tipo derunST
em particular e para os tipos de classificação mais alta em geral. A explicação leva várias páginas, está muito bem feita e não vou tentar condensá-la aqui.Considerar
Se eu ligar
bar
, posso simplesmente escolher qualquer tipoa
que quiser e transmitir uma função de um tipoa
para outroa
. Por exemplo, eu posso passar a função(+1)
ou a funçãoreverse
. Você pode pensar noforall
que diz: "Preciso escolher o tipo agora". (A palavra técnica para escolher o tipo é instanciação .)As restrições de chamada
foo
são muito mais rigorosas: o argumentofoo
deve ser uma função polimórfica. Com esse tipo, as únicas funções às quais posso passarfoo
sãoid
ou uma função que sempre diverge ou com erros, comoundefined
. O motivo é quefoo
, com ,forall
está à esquerda da seta, para que o responsável pela chamadafoo
não consiga escolher o quea
é - é a implementação defoo
que começa a escolher o quea
é. Comoforall
está à esquerda da seta, e não acima da seta como embar
, a instanciação ocorre no corpo da função e não no site da chamada.Resumo: Um completo explicação da
forall
palavra - chave requer matemática e só pode ser entendida por alguém que estudou a matemática. Mesmo explicações parciais são difíceis de entender sem a matemática. Mas talvez minhas explicações parciais, não matemáticas, ajudem um pouco. Vá ler Launchbury e Peyton Jones emrunST
!Termo aditivo: jargão "acima", "abaixo", "à esquerda de". Isso não tem nada a ver com a maneira como os tipos de texto são escritos e tudo a ver com as árvores de sintaxe abstrata. Na sintaxe abstrata, a
forall
leva o nome de uma variável de tipo e, em seguida, existe um tipo completo "abaixo" do forall. Uma seta pega dois tipos (argumento e tipo de resultado) e forma um novo tipo (o tipo de função). O tipo de argumento é "à esquerda" da seta; é o filho esquerdo da seta na árvore de sintaxe abstrata.Exemplos:
No
forall a . [a] -> [a]
, o forall está acima da seta; o que está à esquerda da seta é[a]
.No
o tipo entre parênteses seria chamado "um forall à esquerda de uma seta". (Estou usando tipos como este em um otimizador em que estou trabalhando.)
fonte
forall a . [a] -> [a]
, o forall está à esquerda da seta.forall
nessas circunstâncias, como efetivamente barulho. Vou dar uma olhada no artigo ao qual você vinculou (obrigado pelo link também!) E ver se ele está no meu domínio da compreensão. Parabéns.exists
nunca foi implementado. (Não faz parte do sistema F!) Em Haskell, parte do sistema F é implícita, masforall
é uma das coisas que não pode ser varrida para debaixo do tapete completamente. É como se eles tivessem começado com Hindley-Milner, o que permitiriaforall
tornar-se implícito, e depois optaram por um sistema de tipos mais poderoso, confundindo aqueles de nós que estudavam o 'tudo' e 'existe' da FOL e paravam ali.Minha resposta original:
Como Norman indica, é muito difícil dar uma explicação clara e clara em inglês de um termo técnico da teoria dos tipos. Estamos todos tentando embora.
Há apenas uma coisa a lembrar sobre 'forall': vincula tipos a algum escopo . Depois de entender isso, tudo fica bem fácil. É o equivalente a 'lambda' (ou uma forma de 'let') no nível de tipo - Norman Ramsey usa a noção de "esquerda" / "acima" para transmitir esse mesmo conceito de escopo em sua excelente resposta .
A maioria dos usos de 'forall' é muito simples e você pode encontrá-los introduzidos no Manual do Usuário do GHC, S7.8 ., Particularmente o excelente S7.8.5 em formas aninhadas de 'forall'.
Em Haskell, geralmente deixamos de lado o fichário para tipos, quando o tipo é universalmente quantificado, da seguinte forma:
é equivalente a:
É isso aí.
Como você pode vincular variáveis de tipo agora a algum escopo, pode ter escopos diferentes do nível superior (" quantificado universalmente "), como seu primeiro exemplo, em que a variável de tipo é visível apenas na estrutura de dados. Isso permite tipos ocultos (" tipos existenciais "). Ou podemos ter um aninhamento arbitrário de ligações ("tipos de classificação N").
Para entender profundamente os sistemas de tipos, você precisará aprender algum jargão. Essa é a natureza da ciência da computação. No entanto, usos simples, como acima, devem poder ser compreendidos intuitivamente, por analogia, com 'let' no nível de valor. Uma ótima introdução é Launchbury e Peyton Jones .
fonte
length :: forall a. [a] -> Int
não é equivalente alength :: [a] -> Int
quandoScopedTypeVariables
está ativado. Quandoforall a.
existe, afetalength
awhere
cláusula (se houver) e altera o significado das variáveis de tipo nomeadasa
nela.E quanto à lógica simples de primeira ordem?
forall
é claramente uma referência à quantificação universal e, nesse contexto, o termo existencial também faz mais sentido, embora fosse menos complicado se houvesse umaexists
palavra - chave. Se a quantificação é efetivamente universal ou existencial depende da localização do quantificador em relação ao local onde as variáveis são usadas em qual lado de uma seta de função e tudo é um pouco confuso.Portanto, se isso não ajudar, ou se você apenas não gostar da lógica simbólica, de uma perspectiva mais funcional da programação, você pode pensar nas variáveis de tipo apenas como parâmetros do tipo (implícitos) para a função. As funções que tomam os parâmetros de tipo nesse sentido são tradicionalmente escritas usando uma lambda maiúscula por qualquer motivo, que vou escrever aqui como
/\
.Portanto, considere a
id
função:Podemos reescrevê-lo como lambdas, movendo o "parâmetro de tipo" para fora da assinatura de tipo e adicionando anotações de tipo em linha:
Aqui está a mesma coisa feita para
const
:Portanto, sua
bar
função pode ser algo como isto:Observe que o tipo de função atribuída
bar
como argumento depende dobar
parâmetro de tipo. Considere se você tivesse algo parecido com isto:Aqui
bar2
está aplicando a função a algo do tipoChar
, portanto, fornecerbar2
qualquer parâmetro de tipo diferente deChar
causará um erro de tipo.Por outro lado, eis o que
foo
pode parecer:Ao contrário
bar
,foo
na verdade não aceita nenhum tipo de parâmetro! É preciso uma função que em si leva um parâmetro de tipo, em seguida, aplica essa função para dois diferentes tipos.Portanto, quando você vir uma
forall
assinatura de tipo, pense nela como uma expressão lambda para assinaturas de tipo . Assim como lambdas regulares, o escopo deforall
se estende o mais para a direita possível, entre parênteses, e assim como variáveis ligadas em um lambda regular, as variáveis de tipo vinculadas por aforall
estão apenas no escopo da expressão quantificada.Post scriptum : Talvez você deva se perguntar - agora que estamos pensando em funções que aceitam parâmetros de tipo, por que não podemos fazer algo mais interessante com esses parâmetros do que colocá-los em uma assinatura de tipo? A resposta é que nós podemos!
Uma função que coloca variáveis de tipo juntas com um rótulo e retorna um novo tipo é um construtor de tipos , que você pode escrever algo como isto:
Mas precisaríamos de uma notação completamente nova, porque a maneira como esse tipo é escrito, como
Either a b
, já é sugestivo de "aplicar a funçãoEither
a esses parâmetros".Por outro lado, uma função que tipo de "correspondência de padrão" em seus parâmetros de tipo, retornando valores diferentes para tipos diferentes, é um método de uma classe de tipo . Uma pequena expansão na minha
/\
sintaxe acima sugere algo como isto:Pessoalmente, acho que prefiro a sintaxe real de Haskell ...
Uma função que "padroniza" seus parâmetros de tipo e retorna um tipo existente arbitrário é uma família de tipos ou uma dependência funcional - no caso anterior, ela ainda se parece muito com uma definição de função.
fonte
λ
, mas a extensão de sintaxe unicode do GHC não suporta isso porque λ é uma carta , um fato infeliz que hipoteticamente também se aplicaria às minhas hipotéticas abstrações big-lambda. Portanto,/\
por analogia com\
. Acho que eu poderia ter utilizado∀
, mas eu estava tentando evitar cálculo de predicados ...Aqui está uma explicação rápida e suja em termos simples, com a qual você provavelmente já deve estar familiarizado.
A
forall
palavra-chave é realmente usada apenas de uma maneira no Haskell. Sempre significa a mesma coisa quando você a vê.Quantificação universal
Um tipo universalmente quantificado é um tipo do formulário
forall a. f a
. Um valor desse tipo pode ser pensado como uma função que aceita um tipoa
como argumento e retorna um valor do tipof a
. Exceto que em Haskell esses argumentos de tipo são transmitidos implicitamente pelo sistema de tipos. Essa "função" deve fornecer o mesmo valor, independentemente do tipo que receber, portanto, o valor é polimórfico .Por exemplo, considere o tipo
forall a. [a]
. Um valor desse tipo pega outro tipoa
e retorna uma lista de elementos desse mesmo tipoa
. Existe apenas uma implementação possível, é claro. Teria de lhe dar a lista vazia, porquea
poderia ser absolutamente qualquer tipo. A lista vazia é o único valor de lista polimórfico em seu tipo de elemento (já que não possui elementos).Ou o tipo
forall a. a -> a
. O chamador de uma função fornece um tipoa
e um valor do tipoa
. A implementação deve retornar um valor desse mesmo tipoa
. Há apenas uma implementação possível novamente. Teria que retornar o mesmo valor que foi dado.Quantificação existencial
Um tipo existencialmente quantificado teria a forma
exists a. f a
, se Haskell suportasse essa notação. Um valor desse tipo pode ser pensado como um par (ou um "produto") que consiste em um tipoa
e um valor do tipof a
.Por exemplo, se você tem um valor do tipo
exists a. [a]
, você tem uma lista de elementos de algum tipo. Pode ser de qualquer tipo, mas mesmo que você não saiba o que é, há muito o que fazer nessa lista. Você pode revertê-lo ou contar o número de elementos ou executar qualquer outra operação de lista que não dependa do tipo dos elementos.OK, então espere um minuto. Por que Haskell usa
forall
para denotar um tipo "existencial" como o seguinte?Pode ser confuso, mas está realmente descrevendo o tipo do construtor de dados
SB
:Uma vez construído, você pode pensar em um valor do tipo
ShowBox
como consistindo em duas coisas. É um tipos
junto com um valor do tipos
. Em outras palavras, é um valor de um tipo quantificado existencialmente.ShowBox
poderia realmente ser escrito comoexists s. Show s => s
se Haskell apoiasse essa notação.runST
e amigosDado isso, como são diferentes?
Vamos primeiro pegar
bar
. Ele pega um tipoa
e uma função do tipoa -> a
e produz um valor do tipo(Char, Bool)
. Podemos escolherInt
como oa
e atribuir a ele uma função do tipo,Int -> Int
por exemplo. Masfoo
é diferente. Requer que a implementação defoo
seja capaz de passar qualquer tipo que desejar para a função que lhe damos. Portanto, a única função que podemos razoavelmente dar a ela éid
.Agora devemos ser capazes de entender o significado do tipo de
runST
:Portanto
runST
, deve ser capaz de produzir um valor do tipoa
, não importa qual o tipo que damosa
. Para fazer isso, ele usa um argumento do tipoforall s. ST s a
que certamente deve produzir oa
. Além disso, ele deve ser capaz de produzir um valor do tipo,a
independentemente do tipo que a implementaçãorunST
decida fornecers
.Tá, e daí? O benefício é que isso impõe uma restrição ao chamador
runST
, pois o tipoa
não pode envolver o tipos
. Você não pode transmitir um valor do tipoST s [s]
, por exemplo. O que isso significa na prática é que a implementação derunST
é livre para realizar mutações com o valor do tipos
. O tipo garante que essa mutação seja local para a implementação derunST
.O tipo de
runST
é um exemplo de um tipo polimórfico de classificação 2 porque o tipo de seu argumento contém umforall
quantificador. O tipofoo
acima também é de classificação 2. Um tipo polimórfico comum, como o debar
, é de classificação 1, mas se torna de classificação 2 se for necessário que os tipos de argumentos sejam polimórficos, com seu próprioforall
quantificador. E se uma função recebe argumentos de classificação 2, seu tipo é classificação 3, e assim por diante. Em geral, um tipo que recebe argumentos polimórficos de classificaçãon
tem classificaçãon + 1
.fonte
Vou tentar explicar apenas o significado e talvez a aplicação
forall
no contexto de Haskell e seus sistemas de tipos.Mas antes que você entenda que eu gostaria de direcioná-lo para uma conversa muito acessível e agradável de Runar Bjarnason intitulada " Restrições Liberam, Liberdades Limitam ". A palestra está cheia de exemplos de casos de uso do mundo real, bem como de exemplos no Scala, para apoiar esta afirmação, embora não mencione
forall
. Vou tentar explicar aforall
perspectiva abaixo.É muito importante digerir e acreditar que esta declaração deve prosseguir com a explicação a seguir, por isso peço que você assista à palestra (pelo menos partes dela).
Agora, um exemplo muito comum, mostrando a expressividade do sistema de tipos Haskell é esta assinatura de tipo:
foo :: a -> a
Diz-se que, dada essa assinatura de tipo, existe apenas uma função que pode satisfazer esse tipo e essa é a
identity
função ou o que é conhecido popularmenteid
.Nos estágios iniciais de aprendizado de Haskell, sempre me perguntei as funções abaixo:
ambos satisfazem a assinatura do tipo acima, então por que o pessoal da Haskell afirma que é
id
somente isso que satisfaz a assinatura de tipo?Isso ocorre porque há um implícito
forall
oculto na assinatura do tipo. O tipo real é:Então, agora vamos voltar à declaração: restrições liberam, liberdades restringem
Traduzindo isso para o sistema de tipos, esta declaração se torna:
Uma restrição no nível do tipo se torna uma liberdade no nível do termo
e
Uma liberdade no nível do tipo se torna uma restrição no nível do termo
Vamos tentar provar a primeira afirmação:
Uma restrição no nível do tipo.
Então, colocando uma restrição em nossa assinatura de tipo
torna-se uma liberdade no nível do termo nos dá a liberdade ou flexibilidade para escrever todos esses
O mesmo pode ser observado restringindo-se
a
a qualquer outra classe, etc.Então agora o que este tipo de assinatura:
foo :: (Num a) => a -> a
traduz é:Isso é conhecido como quantificação existencial, que significa que existem algumas instâncias
a
para as quais uma função é alimentada com algo do tipoa
retorna algo do mesmo tipo, e todas essas instâncias pertencem ao conjunto de Números.Portanto, podemos ver a adição de uma restrição (que
a
deve pertencer ao conjunto de Números), libera o nível de termo para ter várias implementações possíveis.Agora, vamos para a segunda declaração e a que realmente traz a explicação de
forall
:Uma liberdade no nível do tipo se torna uma restrição no nível do termo
Então agora vamos liberar a função no nível de tipo:
Agora isso se traduz em:
o que significa que a implementação dessa assinatura de tipo deve ser tal que seja
a -> a
para todas as circunstâncias.Então agora isso começa a nos restringir no nível do termo. Não podemos mais escrever
porque essa implementação não seria satisfatória se colocarmos
a
como aBool
.a
pode ser umChar
ou um[Char]
ou um tipo de dados personalizado. Sob todas as circunstâncias, ele deve retornar algo do tipo semelhante. Essa liberdade no nível de tipo é conhecida como Quantificação Universal e a única função que pode satisfazer isso éque é comumente conhecida como a
identity
funçãoPortanto,
forall
existe umliberty
no nível de tipo, cujo objetivo real éconstrain
o nível de termo para uma implementação específica.fonte
A razão pela qual existem diferentes usos dessa palavra-chave é que ela é realmente usada em pelo menos duas extensões de sistema de tipos diferentes: tipos de classificação mais alta e existenciais.
Provavelmente, é melhor apenas ler e entender essas duas coisas separadamente, em vez de tentar obter uma explicação do porquê 'forall' é uma parte apropriada da sintaxe das duas ao mesmo tempo.
fonte
Como é existencial existencial?
Uma explicação de por
forall
emdata
definições são isomorfo a(exists a. a)
(pseudo-Haskell) pode ser encontrada em "Haskell / tipos existencialmente quantificadas" das Wikibooks .A seguir, um breve resumo literal:
Ao fazer a correspondência / desconstrução de padrões
MkT x
, qual é o tipox
?x
pode ser de qualquer tipo (conforme declarado emforall
) e, portanto, é do tipo:Portanto, o seguinte é isomórfico:
forall significa forall
Minha simples interpretação de tudo isso é que "
forall
realmente significa 'para todos'". Uma distinção importante a ser feita é o impactoforall
sobre a definição versus aplicação de função .A
forall
significa a definição do valor ou função deve ser polimórfica.Se o que está sendo definido é um valor polimórfico , significa que o valor deve ser válido para todos os adequados
a
, o que é bastante restritivo.Se a coisa que está sendo definida é uma função polimórfica , significa que a função deve ser válida para todos os adequados
a
, o que não é tão restritivo, porque apenas porque a função é polimórfica não significa que o parâmetro a ser aplicado deve ser polimórfico. Ou seja, se a função é válida para todosa
, inversamente, qualquer adequadoa
pode ser aplicado à função. No entanto, o tipo do parâmetro pode ser escolhido apenas uma vez na definição da função.Se a
forall
estiver dentro do tipo de parâmetro da função (ou seja, aRank2Type
), significa que o parâmetro aplicado deve ser verdadeiramente polimórfico, para que seja consistente com a idéia de definição deforall
meios polimórficos. Nesse caso, o tipo do parâmetro pode ser escolhido mais de uma vez na definição da função ( "e é escolhido pela implementação da função", conforme apontado por Norman )Portanto, a razão pela qual existenciais
data
definições permite que qualquera
é porque o construtor de dados é um polimórfico função :tipo de MkT ::
a -> *
O que significa que qualquer um
a
pode ser aplicado à função. Em oposição a, digamos, um valor polimórfico :tipo de valueT ::
a
O que significa que a definição de valueT deve ser polimórfica. Nesse caso,
valueT
pode ser definido como uma lista vazia[]
de todos os tipos.Diferenças
Mesmo que o significado para
forall
seja consistente emExistentialQuantification
eRankNType
, existenciais tenha uma diferença, pois odata
construtor pode ser usado na correspondência de padrões. Conforme documentado no guia do usuário do ghc :fonte