Quais são as equivalências mais interessantes decorrentes do isomorfismo de Curry-Howard?


97

Eu descobri o isomorfismo de Curry-Howard relativamente tarde em minha vida de programação e talvez isso contribua para que eu fique totalmente fascinado por ele. Isso implica que para cada conceito de programação existe um análogo preciso na lógica formal e vice-versa. Aqui está uma lista "básica" dessas analogias, tirada do topo da minha cabeça:

program/definition        | proof
type/declaration          | proposition
inhabited type            | theorem/lemma
function                  | implication
function argument         | hypothesis/antecedent
function result           | conclusion/consequent
function application      | modus ponens
recursion                 | induction
identity function         | tautology
non-terminating function  | absurdity/contradiction
tuple                     | conjunction (and)
disjoint union            | disjunction (or)          -- corrected by Antal S-Z
parametric polymorphism   | universal quantification

Então, à minha pergunta: quais são algumas das implicações mais interessantes / obscuras desse isomorfismo? Não sou um lógico, então tenho certeza de que apenas arranhei a superfície com esta lista.

Por exemplo, aqui estão algumas noções de programação para as quais não tenho conhecimento de nomes expressivos na lógica:

currying                  | "((a & b) => c) iff (a => (b => c))"
scope                     | "known theory + hypotheses"

E aqui estão alguns conceitos lógicos que ainda não defini em termos de programação:

primitive type?           | axiom
set of valid programs?    | theory

Editar:

Aqui estão mais algumas equivalências coletadas das respostas:

function composition      | syllogism                -- from Apocalisp
continuation-passing      | double negation          -- from camccann

encerramento ~ = conjunto de axiomas
Apocalisp

+1 Esta pergunta e todas as respostas e comentários de qualidade me ensinaram mais sobre a CHI do que eu poderia aprender através da internet.
Alexandre C.

24
@Paul Nathan:goto | jumping to conclusions
Joey Adams

Acho que o conjunto de todos os programas válidos seria um modelo
Daniil

1
fst / snd | eliminação da conjunção, esquerda / direita | introdução da disjunção
Tony Morris,

Respostas:


33

Já que você pediu explicitamente pelos mais interessantes e obscuros:

Você pode estender o CH a muitas lógicas e formulações lógicas interessantes para obter uma variedade realmente ampla de correspondências. Aqui, tentei me concentrar em alguns dos mais interessantes em vez de nos obscuros, além de alguns outros fundamentais que ainda não surgiram.

evaluation             | proof normalisation/cut-elimination
variable               | assumption
S K combinators        | axiomatic formulation of logic   
pattern matching       | left-sequent rules 
subtyping              | implicit entailment (not reflected in expressions)
intersection types     | implicit conjunction
union types            | implicit disjunction
open code              | temporal next
closed code            | necessity
effects                | possibility
reachable state        | possible world
monadic metalanguage   | lax logic
non-termination        | truth in an unobservable possible world
distributed programs   | modal logic S5/Hybrid logic
meta variables         | modal assumptions
explicit substitutions | contextual modal necessity
pi-calculus            | linear logic

EDIT: Uma referência que eu recomendo para qualquer pessoa interessada em aprender mais sobre extensões de CH:

"A Judgmental Reconstruction of Modal Logic" http://www.cs.cmu.edu/~fp/papers/mscs00.pdf - este é um ótimo lugar para começar porque começa a partir dos primeiros princípios e muitos deles se destinam a ser acessível a não lógicos / teóricos da linguagem. (Eu sou o segundo autor, então sou tendencioso.)


obrigado por fornecer alguns exemplos menos triviais (esse era realmente o espírito da pergunta original), embora eu admita que vários deles estão além da minha cabeça ... os termos "necessidade" e "possibilidade" são precisamente definidos na lógica? como eles se traduzem em seus equivalentes computacionais?
Tom Crockett

2
Posso apontar artigos publicados para cada um deles, então eles são precisamente definidos. A lógica modal é muito estudada (desde Aristóteles) e relaciona diferentes modos de verdade - "A é necessariamente verdadeiro" significa "em todo mundo possível A é verdadeiro", enquanto "A é possivelmente verdadeiro" significa "A é verdadeiro em um mundo possível" . Você pode provar coisas como "(necessariamente (A -> B) e possivelmente A) -> possivelmente B". As regras de inferência modal geram diretamente as construções de expressão, digitação e regras de redução, como de costume em CH. Consulte: en.wikipedia.org/wiki/Modal_logic e cs.cmu.edu/~fp/papers/mscs00.pdf
RD1

2
@pelotom: Você pode querer ler um pouco sobre outros tipos de lógica . A lógica clássica simples muitas vezes não é útil neste contexto - eu mencionei a lógica intuicionista em minha resposta, mas a lógica modal e linear são ainda mais "estranhas", mas também são realmente impressionantes.
CA McCann

1
Obrigado pelas dicas, parece que tenho algumas leituras para fazer!
Tom Crockett

2
@ RD1: Você acha isso ruim, eu passei tanto tempo pensando em Haskell que preciso traduzir mentalmente fórmulas de lógica de predicados em assinaturas de tipo antes que façam sentido. :( Sem mencionar que a lei do meio excluído e tal começa a parecer muito confusa e talvez suspeita.
CA McCann

26

Você está confundindo um pouco as coisas em relação à não terminação. A falsidade é representada por tipos desabitados , que por definição não podem ser não terminados porque não há nada desse tipo para avaliar em primeiro lugar.

A não terminação representa contradição - uma lógica inconsistente. Uma lógica inconsistente, é claro, permitirá que você prove qualquer coisa , incluindo a falsidade, entretanto.

Ignorando as inconsistências, os sistemas de tipos normalmente correspondem a uma lógica intuicionista e são necessariamente construtivistas , o que significa que certas peças da lógica clássica não podem ser expressas diretamente, se o forem. Por outro lado, isso é útil, porque se um tipo é uma prova construtiva válida, então um termo desse tipo é um meio de construir tudo o que você provou a existência .

Uma característica importante do sabor construtivista é que a negação dupla não é equivalente à não negação. Na verdade, a negação raramente é um primitivo em um sistema de tipos, então, em vez disso, podemos representá-la como implicando falsidade, por exemplo, not Ptorna-se P -> Falsity. A negação dupla seria, portanto, uma função com tipo (P -> Falsity) -> Falsity, que claramente não é equivalente a algo do tipo P.

No entanto, há uma reviravolta interessante nisso! Em uma linguagem com polimorfismo paramétrico, as variáveis ​​de tipo abrangem todos os tipos possíveis, incluindo os inabitados, portanto, um tipo totalmente polimórfico como ∀a. a, em certo sentido, quase falso. E daí se escrevermos quase-negação dupla usando polimorfismo? Ficamos com um tipo que se parece com isso: ∀a. (P -> a) -> a. Isso é equivalente a algo do tipo P? Na verdade, é , apenas aplique-o à função de identidade.

Mas de que adianta? Por que escrever um tipo assim? Isso significa alguma coisa em termos de programação? Bem, você pode pensar nisso como uma função que já tem algo do tipo em Palgum lugar e precisa que você dê a ela uma função que aceita Pcomo um argumento, com a coisa toda sendo polimórfica no tipo de resultado final. Em certo sentido, representa uma computação suspensa , esperando que o resto seja fornecido. Nesse sentido, esses cálculos suspensos podem ser compostos juntos, passados, invocados, o que quer que seja. Isso deve começar a soar familiar para os fãs de algumas linguagens, como Scheme ou Ruby - porque o que significa é que a dupla negação corresponde ao estilo de passagem de continuaçãoe, de fato, o tipo que dei acima é exatamente a continuação da mônada em Haskell.


Obrigado pela correção, removi "falsidade" como sinônimo de não terminação. +1 para negação dupla <=> CPS!
Tom Crockett

Eu não entendo bem a intuição por trás de representar ¬p as P -> Falsity. Eu entendo por que funciona (¬p ≡ p → ⊥), mas não entendi a versão do código. P -> ⊥deve ser habitada justamente quando Pnão é, certo? Mas essa função não deveria ser sempre habitada? Ou possível nunca, na verdade, já que você não pode retornar uma instância de ? Não vejo bem a condicionalidade disso. Qual é a intuição aqui?
Antal Spector-Zabusky

1
@Antal SZ: A intuição é lógica intuicionista, claro! Mas sim, escrever tal função é realmente difícil. Vejo em seu perfil que você conhece Haskell, então talvez você esteja pensando em tipos de dados algébricos e correspondência de padrões? Considere que um tipo desabitado não deve ter construtores e, portanto, nada para comparar com o padrão. Você teria que escrever uma "função" sem corpo, o que não é legal para Haskell. Na verdade, que eu saiba, não há como escrever um termo do tipo negado em Haskell sem usar exceções de tempo de execução ou não terminação.
CA McCann

1
@Antal SZ: Por outro lado, se a lógica equivalente for consistente, todas as funções devem ser totais, por exemplo, todas as correspondências de padrões devem ser exaustivas. Portanto, para escrever uma função sem padrões, o tipo de parâmetro não deve ter construtores, por exemplo, ser inabitado. Portanto, tal função seria legal - e, portanto, seu próprio tipo habitado - precisamente e somente quando seu argumento é desabitado. Portanto, uma função P -> Falsityé equivalente a Pser falsa.
CA McCann

Aha, acho que entendi. A versão que eu estava entretendo era algo como f x = x, que seria instanciado sse P = ⊥, mas isso claramente não era genérico o suficiente. Portanto, a ideia é que para retornar um tipo sem valor, você não precisa de nenhum corpo; mas para que a função seja definível e total, você não precisa de casos , então se Pfor desabitado, tudo dá certo? Isso é um pouco estranho, mas acho que vejo. Isso parece interagir de forma bastante estranha com a minha definição do Xortipo ... Vou ter que pensar sobre isso. Obrigado!
Antal Spector-Zabusky

15

Seu gráfico não está certo; em muitos casos, você confundiu tipos com termos.

function type              implication
function                   proof of implication
function argument          proof of hypothesis
function result            proof of conclusion
function application RULE  modus ponens
recursion                  n/a [1]
structural induction       fold (foldr for lists)
mathematical induction     fold for naturals (data N = Z | S N)
identity function          proof of A -> A, for all A
non-terminating function   n/a [2]
tuple                      normal proof of conjunction
sum                        disjunction
n/a [3]                    first-order universal quantification
parametric polymorphism    second-order universal quantification
currying                   (A,B) -> C -||- A -> (B -> C), for all A,B,C
primitive type             axiom
types of typeable terms    theory
function composition       syllogism
substitution               cut rule
value                      normal proof

[1] A lógica para uma linguagem funcional Turing-completa é inconsistente. A recursão não tem correspondência em teorias consistentes. Em uma teoria de prova lógica / incorreta, você poderia chamá-la de uma regra que causa inconsistência / falta de fundamento.

[2] Novamente, isso é uma consequência da integridade. Isso seria uma prova de um anti-teorema se a lógica fosse consistente - portanto, ela não pode existir.

[3] Não existe em linguagens funcionais, uma vez que elide características lógicas de primeira ordem: toda quantificação e parametrização são feitas sobre fórmulas. Se você tivesse características de primeira ordem, haveria um outro tipo do que *, * -> *, etc .; o tipo de elementos do domínio do discurso. Por exemplo, em Father(X,Y) :- Parent(X,Y), Male(X), Xe Yabrangem o domínio do discurso (chame-o Dom), e Male :: Dom -> *.


[1] - sim, eu deveria ter sido mais específico. Eu quis dizer "recursão estrutural" ao invés de recursão irrestrita, que eu acho que é o mesmo que "dobrar". [3] - existe em linguagens com tipos dependentes
Tom Crockett

[1] O fato é que se uma chamada de função de recursão (modus ponens) não fizer com que o programa não termine, os parâmetros (hipóteses) dados à chamada ou ao ambiente DEVEM ser diferentes entre essas chamadas. Portanto, a recursão é apenas aplicar o mesmo teorema várias vezes. Se houver algo especial, é geralmente números crescentes / decrescentes (passo indutivo) e checando com um caso existente (caso base), que corresponde a - Indução Matemática na lógica.
Earth Engine

Eu realmente gosto deste gráfico, mas não diria "n / a", uma vez que a lógica consistente não é o único tipo de lógica, assim como os programas de encerramento não são o único tipo de programa. Uma função não terminada corresponderia a um "argumento circular" e é uma excelente ilustração do isomorfismo de Curry-Howard: "seguir" um argumento circular coloca você em um loop infinito.
Joey Adams,


13

Eu realmente gosto dessa pergunta. Não sei muito, mas tenho algumas coisas (auxiliado pelo artigo da Wikipedia , que tem algumas tabelas interessantes e tal):

  1. Acho que os tipos de soma / tipos de união ( por exemplo data Either a b = Left a | Right b ) são equivalentes à disjunção inclusiva . E, embora eu não esteja muito familiarizado com Curry-Howard, acho que isso demonstra. Considere a seguinte função:

    andImpliesOr :: (a,b) -> Either a b
    andImpliesOr (a,_) = Left a
    

    Se eu entendi as coisas corretamente, o tipo diz que ( a  ∧  b ) → ( a  ★  b ) e a definição diz que isso é verdade, onde ★ é inclusivo ou exclusivo ou, o que quer que Eitherrepresente. Você tem Eitherrepresentante exclusivo ou, ⊕; entretanto, ( a  ∧  b ) ↛ ( a  ⊕  b ). Por exemplo, ⊤ ∧ ⊤ ≡ ⊤, mas ⊤ ⊕ ⊥ ≡ ⊥ e ⊤ ↛ ⊥. Em outras palavras, se ambos um e b são verdadeiras, então a hipótese é verdadeira, mas a conclusão é falsa, e por isso esta implicação deve ser falsa. No entanto, claramente, ( a  ∧  b ) → ( a  ∨ b ), desde que se ambos um e b forem verdadeiras, em seguida, pelo menos uma é verdadeiro. Assim, se as uniões discriminadas são alguma forma de disjunção, devem ser a variedade inclusiva. Acho que isso é uma prova, mas sinta-se mais do que livre para me desiludir dessa noção.

  2. Da mesma forma, suas definições para tautologia e absurdo como a função de identidade e funções não-terminantes, respectivamente, estão um pouco erradas. A verdadeira fórmula é representada pelo tipo de unidade , que é o tipo que tem apenas um elemento ( data ⊤ = ⊤; frequentemente escrito ()e / ou Unitem linguagens de programação funcionais). Isso faz sentido: como esse tipo tem garantia de ser habitado, e como só existe um habitante possível, deve ser verdade. A função de identidade representa apenas a tautologia particular que a  →  a .

    Seu comentário sobre funções não terminadas é, dependendo do que exatamente você quis dizer, mais errado. Curry-Howard funciona no sistema de tipo, mas a não terminação não é codificada lá. Segundo a Wikipedia , lidar com não-terminação é um problema, como a adição produz lógicas inconsistentes ( por exemplo , eu posso definir wrong :: a -> bpor wrong x = wrong x, e, assim, “provar” que a  →  b para qualquer um e b ). Se isso é o que você quis dizer com “absurdo”, então você está exatamente correto. Se em vez disso você quis dizer a declaração falsa, então o que você quer é qualquer tipo desabitado, por exemplo , algo definido pordata ⊥- isto é, um tipo de dados sem nenhuma maneira de construí-lo. Isso garante que ele não tenha nenhum valor e, portanto, deve ser desabitado, o que é equivalente a falso. Eu acho que você provavelmente também poderia usar a -> b, já que se proibirmos funções que não terminam, então também está desabitado, mas não tenho 100% de certeza.

  3. A Wikipedia diz que os axiomas são codificados de duas maneiras diferentes, dependendo de como você interpreta Curry-Howard: ou nos combinadores ou nas variáveis. Acho que a visão combinadora significa que as funções primitivas que recebemos codificam as coisas que podemos dizer por padrão (semelhante ao modo como o modus ponens é um axioma porque a aplicação da função é primitiva). E eu acho que a visão variável pode realmente significar a mesma coisa - combinadores, afinal, são apenas variáveis ​​globais que são funções particulares. Quanto aos tipos primitivos: se estou pensando sobre isso corretamente, então acho que os tipos primitivos são as entidades - os objetos primitivos sobre os quais estamos tentando provar coisas.

  4. De acordo com minha classe de lógica e semântica, o fato de ( a  ∧  b ) →  c  ≡  a  → ( b  →  c ) (e também de b  → ( a  →  c )) ser chamado de lei de equivalência de exportação, pelo menos na dedução natural provas. Não percebi na hora que estava apenas curry - gostaria de ter, porque isso é legal!

  5. Embora agora tenhamos uma maneira de representar a disjunção inclusiva , não temos uma maneira de representar a variedade exclusiva. Devemos ser capazes de usar a definição de disjunção exclusiva para representá-lo: a  ⊕  b  ≡ ( a  ∨  b ) ∧ ¬ ( a  ∧  b ). Não sei escrever negação, mas sei que ¬ p  ≡  p  → ⊥, e tanto a implicação quanto a falsidade são fáceis. Devemos, portanto, ser capazes de representar disjunção exclusiva por:

    data ⊥
    data Xor a b = Xor (Either a b) ((a,b) -> ⊥)
    

    Isso define ser o tipo vazio sem valores, o que corresponde à falsidade; Xoré então definido para conter ( e ) Eitherum a ou a b ( ou ) e uma função ( implicação ) de (a, b) ( e ) para o tipo inferior ( falso ). No entanto, não tenho ideia do que isso significa . ( Edição 1: agora sim, veja o próximo parágrafo!) Como não há valores de tipo (a,b) -> ⊥(existem?), Não consigo entender o que isso significaria em um programa. Alguém conhece uma maneira melhor de pensar sobre essa definição ou outra? ( Edição 1: Sim, camccann .)

    Edição 1: Graças à resposta do camccann (mais particularmente, os comentários que ele deixou para me ajudar), acho que vejo o que está acontecendo aqui. Para construir um valor de tipo Xor a b, você precisa fornecer duas coisas. Primeiro, uma testemunha da existência de um elemento de um aou bcomo o primeiro argumento; ou seja, a Left aou a Right b. E em segundo lugar, uma prova de que não existem elementos de ambos os tipos ae b- em outras palavras, uma prova que (a,b)é inabitada - como o segundo argumento. Já que você só poderá escrever uma função (a,b) -> ⊥se (a,b)estiver desabitada, o que significa ser esse o caso? Isso significaria que alguma parte de um objeto do tipo(a,b)não poderia ser construído; em outras palavras, que pelo menos um, e possivelmente ambos, ae bsão desabitadas bem! Nesse caso, se estivermos pensando em casamento de padrões, não é possível que haja correspondência de padrões em tal tupla: supondo que ela bseja desabitada, o que escreveríamos que pudesse corresponder à segunda parte dessa tupla? Portanto, não podemos corresponder a um padrão, o que pode ajudá-lo a ver por que isso o torna desabitado. Agora, a única maneira de ter uma função total que não aceita argumentos (como esta deve, uma vez que (a,b)é inabitada) é o resultado ser de um tipo desabitado também - se estivermos pensando nisso de uma perspectiva de correspondência de padrões, isso significa que mesmo que a função não tenha casos, não há corpo possível poderia ter qualquer um, e então está tudo OK.

Muito disso sou eu pensando em voz alta / provando (espero) coisas na hora, mas espero que seja útil. Eu realmente recomendo o artigo da Wikipedia ; Eu não li nenhum tipo de detalhe, mas suas tabelas são um resumo muito bom e muito completo.


1
1 para indicar que Either é inclusivo ou. Observe que (Ou aa) é um teorema (para todo a).
Apocalisp

Pergunta re. 2 (b): qual é a diferença entre um tipo de função cujo único habitante é não terminante e um tipo de função não habitada? Por exemplo, se eu declarasse um tipo B sem construtores, então definia uma função A-> B assim: fun (a: A): B: = f (a) isso seria typecheck em muitas linguagens, mesmo que seja impossível retornar um B. Portanto, a função é "habitada" em um sentido, mas seu "habitante" é absurdo ... portanto, não é realmente habitada. Espero que faça algum sentido :)
Tom Crockett

3
Os fundos não são provas. “É absurdo e impossível supor que o incognoscível e indeterminado deva conter e determinar.” - Aristoteles
Apocalisp

2
@Tom: Apenas para deixar claro o ponto sobre a não terminação, se a lógica for consistente, todos os programas terminam . A não terminação ocorre apenas em sistemas de tipos que representam lógicas inconsistentes ou, de maneira equivalente, sistemas de tipos para linguagens de Turing-completas.
CA McCann

1
Apocalisp: Either a a não deveria ser um teorema: Either ⊥ ⊥ainda é desabitado. Tom: Como camccann disse, consistência implica rescisão. Assim, um sistema de tipos consistente não permitirá que você se expresse f :: a -> be, portanto, o tipo seria desabitado; um sistema de tipo inconsistente teria um habitante para o tipo, mas um que não terminaria. camccann: Existem sistemas de tipos inconsistentes que não são Turing-completos, ocupando algum ponto intermediário na hierarquia? Ou essa última etapa (adicionar recursão geral ou qualquer outra coisa) é precisamente equivalente à inconsistência?
Antal Spector-Zabusky

12

Aqui está um um pouco obscuro que estou surpreso não ter sido mencionado antes: a programação reativa funcional "clássica" corresponde à lógica temporal.

Claro, a menos que você seja um filósofo, matemático ou um programador funcional obsessivo, isso provavelmente levanta várias outras questões.

Então, primeiro: o que é programação reativa funcional? É uma maneira declarativa de trabalhar com valores que variam no tempo . Isso é útil para escrever coisas como interfaces de usuário porque as entradas do usuário são valores que variam com o tempo. O FRP "clássico" tem dois tipos básicos de dados: eventos e comportamentos.

Os eventos representam valores que existem apenas em momentos discretos. As teclas são um ótimo exemplo: você pode pensar nas entradas do teclado como um caractere em um determinado momento. Cada tecla pressionada é então apenas um par com o caractere da tecla e a hora em que foi pressionada.

Os comportamentos são valores que existem constantemente, mas podem mudar continuamente. A posição do mouse é um ótimo exemplo: é apenas um comportamento das coordenadas x, y. Afinal, o mouse sempre tem uma posição e, conceitualmente, essa posição muda continuamente à medida que você move o mouse. Afinal, mover o mouse é uma ação única e prolongada, não um monte de etapas discretas.

E o que é lógica temporal? Apropriadamente, é um conjunto de regras lógicas para lidar com proposições quantificadas ao longo do tempo. Essencialmente, ele estende a lógica normal de primeira ordem com dois quantificadores: □ e ◇. O primeiro significa "sempre": leia □ φ como "φ sempre é válido". A segunda é "eventualmente": ◇ φ significa que "φ eventualmente aguentará". Este é um tipo particular de lógica modal . As duas leis a seguir relacionam os quantificadores:

□φ ⇔ ¬◇¬φ
◇φ ⇔ ¬□¬φ

Portanto, □ e ◇ são duais entre si da mesma forma que ∀ e ∃.

Esses dois quantificadores correspondem aos dois tipos de FRP. Em particular, □ corresponde a comportamentos e ◇ corresponde a eventos. Se pensarmos em como esses tipos são habitados, isso deveria fazer sentido: um comportamento é habitado em todos os momentos possíveis, enquanto um evento só acontece uma vez.


8

Relacionado à relação entre continuações e dupla negação, o tipo de chamada / cc é a lei de Peirce http://en.wikipedia.org/wiki/Call-with-current-continuation

CH é geralmente declarado como correspondência entre a lógica intuicionista e os programas. No entanto, se adicionarmos o operador call-with-current-continuation (callCC) (cujo tipo corresponde à lei de Peirce), obtemos uma correspondência entre a lógica clássica e os programas com callCC.


4

Embora não seja um isomorfismo simples, esta discussão sobre LEM construtivo é um resultado muito interessante. Em particular, na seção de conclusão, Oleg Kiselyov discute como o uso de mônadas para obter eliminação de dupla negação em uma lógica construtiva é análogo a distinguir proposições computacionalmente decidíveis (para as quais LEM é válido em um ambiente construtivo) de todas as proposições. A noção de que as mônadas capturam efeitos computacionais é antiga, mas esta instância do isomorfismo Curry-Howard ajuda a colocá-la em perspectiva e ajuda a entender o que a dupla negação realmente "significa".



4
2-continuation           | Sheffer stoke
n-continuation language  | Existential graph
Recursion                | Mathematical Induction

Uma coisa que é importante, mas ainda não foi investigada, é a relação de 2 continuação (continuação que leva 2 parâmetros) e AVC de Sheffer . Na lógica clássica, o traço de Sheffer pode formar um sistema lógico completo por si só (mais alguns conceitos de não-operador). O que significa que o familiar and, or, notpodem ser implementados utilizando apenas o Stoke Sheffer ou nand.

Este é um fato importante de sua correspondência de tipo de programação porque avisa que um único combinador de tipo pode ser usado para formar todos os outros tipos.

A assinatura de tipo de uma continuação 2 é (a,b) -> Void. Por esta implementação, podemos definir 1-continuação (continuações normais) como (a,a)-> Vazio, tipo de produto como ((a,b)->Void,(a,b)->Void)->Void, tipo de soma como ((a,a)->Void,(b,b)->Void)->Void. Isso nos dá uma impressionante força de expressividade.

Se cavarmos mais, descobriremos que o grafo existencial de Piece é equivalente a uma linguagem com o único tipo de dados é n-continuação, mas não vi nenhuma linguagem existente nesta forma. Então, inventar um pode ser interessante, eu acho.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.