Gostaria de saber se um tipo universalmente quantificado : é um subtipo ou caso especial, de um tipo quantificado existencialmente com a mesma assinatura:T a = ∀ X : { a ∈ X , f : X → { T , F } } T e T e = ∃ X : { a ∈ X , f : X → { T , F } }
Eu diria "sim": se algo é verdadeiro "para todos os X" ( ), também deve ser verdadeiro "para alguns X" ( ). Ou seja, uma declaração com ' ' é simplesmente uma versão mais restrita da mesma declaração com ' ':∃ X ∀ ∃ ∀ X , P ( X ) ?
Estou errado em algum lugar?
Antecedentes: Por que estou perguntando isso?
Estou estudando tipos existenciais para entender por que e como "Tipos abstratos de [dados] têm tipo existencial" . Não consigo entender bem esse conceito apenas com a teoria; Também preciso de exemplos concretos.
Infelizmente, é difícil encontrar bons exemplos de código, porque a maioria das linguagens de programação tem suporte limitado apenas para tipos existenciais. (Por exemplo, curingas de Haskell
forall
ou Java?
.) Por outro lado, tipos quantificados universalmente são suportados por muitas linguagens recentes via "genéricos".O que é pior, os genéricos parecem facilmente se misturar com os tipos existenciais , tornando ainda mais difícil diferenciar tipos existenciais de universais. Estou curioso para saber por que essa confusão ocorre com tanta facilidade. Uma resposta para essa pergunta pode explicar: se tipos universais são de fato apenas um caso especial de tipos existenciais, não é de admirar que tipos genéricos, como Java
List<T>
, possam ser interpretados de qualquer maneira.
fonte
forall x. P(x)
simexists x. P(x)
. Se os sistemas de tipos levam isso em consideração ao verificar os tipos ... Não faço ideia. +1 para uma pergunta interessante.Respostas:
Primeiro, do ponto de vista matemático, não implica . A implicação vale se e somente se não estiver vazio. No entanto, nas linguagens de programação, é incomum lidar com tipos vazios (embora isso aconteça).∀ x : T, P( x) ∃ x : T, P( X ) T
Ainda do ponto de vista matemático, mesmo quando , os dois não são os mesmos. Se você der aos tipos uma semântica definida, será um superconjunto de , não o mesmo tipo. (Na verdade, não é um superconjunto; é quase isomórfico a um superconjunto.)( ∀ x : T, P( x ) ) ⇒ ( ∃ x : T, P( x ) ) Tuma Te
Vamos nos aproximar da teoria da linguagem de programação e ver o que esses tipos realmente significam. é um tipo universal: se você tiver um valor desse tipo, poderá construir para qualquer . Em particular, se você tem e ambos do tipo , você pode construir e . Em essência (e possivelmente em efeito, dependendo do idioma) é uma função dos tipos aos termos. Como tal, o tipo universal fornece parametrização de tipo : um valor trabalhando para todos os tipos. Os tipos universais estão no coração deTuma= ∀ X. { a : X, f: X→ b o o l } UMA A ( M) M: X M1 M2 X A ( M1) A ( M2) Tuma polimorfismo .
O tipo existencial é um animal bem diferente. Dado um valor do tipo , existe apenas um termo tal que e, para este termo . O tipo existencial fornece uma maneira de ocultar a natureza de ; está dizendo: "Existe um tipo! Mas eu não vou te dizer qual! ”. Como tal, o tipo existencial fornece abstração de tipo : um valor oculto específico. Tipos existentes estão no coração dos sistemas de módulos .B T e N : X π 1 ( B ) = N pi 2 ( B ) = { um : N , f : N → b o o l } NTe= ∃ X. { a : X, f: X→ b o o l } B Te N: X π1( B ) = N π2( B ) = { a : N, f: N→ b o o l } N
Não se deixe enganar por Haskell
forall
: apesar do nome, é uma forma de quantificador existencial.Como pano de fundo, recomendo fortemente Tipos e Linguagens de Programação (os capítulos 23 e 24 discutem tipos universais e tipos existenciais, respectivamente). Ele fornecerá informações úteis para entender artigos de pesquisa.
fonte
forall
é de fato um quantificador universal no contexto original da quantificação implícita que torna explícita, a saber: a visualização de tipos polimórficos "de fora" para definições de nível superior. No "interior" dessa definição, ao manipular argumentos, os tipos polimórficos são efetivamente existenciais; cada variável de tipo está vinculada a algum tipo, mas não sabemos (e não podemos) saber qual é esse tipo. Que eu saiba, nenhuma implementação Haskell suporta tipos existenciais verdadeiros (brutos, de nível superior), e não está claro para mim qual objetivo isso serviria.Sua intuição de que devem ser incorporados em geralmente está correta. Só falha se toda a coleção de tipos estiver vazia, o que raramente é o caso.∀ X.P( X) ∃ X.P( X)
No entanto, o exemplo específico que você forneceu não é bom. Um valor desse tipo deve ser capaz de produzir um elemento de cada tipo (para o primeiro componente do par). Se você está pensando em tipos como conjuntos, isso não é possível. Então, esse tipo está vazio. Ele seria incluído em todos os tipos, em particular o tipo existencial , mas isso não é muito interessante. De qualquer forma, como exercício teórico, eis um termo que constrói uma coisa dessas:X ∃ X .∀ X.( X× ( X→ B o o l ) ) X ∃ X.( X× ( X→ B o o l ) )
O artigo que você menciona para tipos existenciais é um pouco teórico. Um artigo mais tutorial é o artigo de Cardelli e Wegner: Sobre a compreensão de tipos, abstração de dados e polimorfismo . Os livros de texto mais avançados sobre linguagens de programação também teriam alguma discussão sobre tipos existenciais. Um bom livro para procurar seria o livro Foundations of Programming Languages, de Mitchell .
Você está certo que a maioria das linguagens de programação não possui tipos existenciais explicitamente. No entanto, muitos têm tipos abstratos (ou por outro nome, como "pacotes" ou "módulos"). Portanto, eles são capazes de expressar valores de tipos existenciais, mesmo que não tratem esses valores como entidades de primeira classe.
Mais importante ainda, é equivalente a . Assim, qualquer linguagem que trata funções polimórficas (de tipos) como entidades de primeira classe também tem a capacidade de tratar tipos abstratos como entidades de primeira classe. ∀ Y .∃ X.P( X) ∀∀ Y.( ∀ X.P( X) → Y) → Y ∀
fonte