Isomorfismo de Curry-Howard


He buscado en Internet, y no puedo encontrar ninguna explicación del CHI que no degenere rápidamente en una conferencia sobre la teoría de la lógica que está drásticamente por encima de mi cabeza. (Estas personas hablan como si" cálculo de proposición intuicionista " es una frase que en realidad significa algo para los seres humanos normales!)

Grosso modo, CHI dice que los tipos son teoremas, y los programas son las pruebas de los teoremas. Pero, ¿qué demonios significa eso ??

Hasta ahora, he pensado esto hacia fuera:

  • Considere id :: x -> x. Su tipo dice "dado que X es verdadero, podemos concluir que X es verdadero". Me parece un teorema razonable.

  • Ahora consideremos foo :: x -> y. Como cualquier programador de Haskell te dirá, esto es imposible. No puede escribir esta función. (Bueno, sin hacer trampa de todos modos.) Leído como un teorema, dice "dado que cualquier X es verdadera, podemos concluir que cualquier Y es verdadera". Esto es obviamente una tontería. Y, por supuesto, no puedes escribir esto función.

  • Más generalmente, los argumentos de la función se pueden considerar "esto que se supone que es verdadero", y el tipo de resultado se puede considerar "cosa que es verdadera suponiendo que todas las otras cosas son". Si hay un argumento de función, digamos x -> y, podemos tomar eso como una suposición de que X siendo verdadero implica que Y debe ser verdadero.

  • Por ejemplo, (.) :: (y -> z) -> (x -> y) -> x -> z puede tomarse como " suponiendo que Y implica Z, que X implica Y, y que X es verdadera, podemos concluir que Z es verdadero". Lo cual me parece lógico.

Ahora, ¿qué diablos significa Int -> Int?? o_O

La única respuesta sensata que se me ocurre es esta: Si tienes una función X -> Y -> Z, entonces la firma de tipo dice "suponiendo que es posible construir un valor de tipo X, y otro de tipo Y, entonces es posible construir un valor de tipo Z". Y el cuerpo de la función describe exactamente cómo harías esto.

Eso parece tener sentido, pero no es muy interesante . Así que claramente tiene que haber algo más que esto...

Author: Tom Crockett, 2012-04-18

3 answers

El isomorfismo de Curry-Howard simplemente establece que los tipos corresponden a proposiciones, y los valores corresponden a pruebas.

Int -> Int realmente no significa mucho interesante como una proposición lógica. Al interpretar algo como una proposición lógica, solo le interesa si el tipo está habitado (tiene algún valor) o no. Entonces, Int -> Int solo significa "dado un Int, puedo darte un Int", y es, por supuesto, cierto. Hay muchas pruebas diferentes de ello (correspondientes a varias funciones diferentes de ese tipo), pero cuando lo tomas como una proposición lógica, realmente no te importa.

Eso no significa que las proposiciones interesantes no puedan involucrar tales funciones; solo que ese tipo en particular es bastante aburrido, como una proposición.

Para una instancia de un tipo de función que no es completamente polimórfica y que tiene un significado lógico, considere p -> Void (para algunos p ), donde Void es el tipo deshabitado: el tipo sin valores (excepto ⊥, en Haskell, pero llegaré a eso más tarde). La única manera de obtener un valor de tipo Void es si puedes probar una contradicción (lo cual es, por supuesto, imposible), y como Void significa que has probado una contradicción, puedes obtener cualquier valor de ella (es decir, existe una función absurd :: Void -> a). En consecuencia, p -> Void corresponde a p: significa "p implica la falsedad".

La lógica intuicionista es solo un cierto tipo de lógica a la que corresponden los lenguajes funcionales comunes. Es importante destacar que es constructivo : básicamente, una prueba de a -> b te da un algoritmo para calcular b a partir de a, lo que no es cierto en la lógica clásica regular (debido a la ley del medio excluido, que te dirá que algo es verdadero o falso, pero no por qué).

Aunque funciones como Int -> Int no significan mucho como proposiciones, podemos hacer declaraciones sobre ellas con otras proposiciones. Por ejemplo, podemos declarar el tipo de igualdad de dos tipos (usando un GADT):

data Equal a b where
    Refl :: Equal a a

Si tenemos un valor de tipo Equal a b, entonces a es el mismo tipo de b: Equal a b corresponde a la proposición un = b. El problema es que solo podemos hablar de igualdad de tipos de esta manera. Pero si tuviéramos tipos dependientes , podríamos generalizar fácilmente esta definición para trabajar con cualquier valor , y así Equal a b correspondería a la proposición de que el valores un y b son idénticos. Así, por ejemplo, podríamos escribir:

type IsBijection (f :: a -> b) (g :: b -> a) =
    forall x. Equal (f (g x)) (g (f x))

Aquí, f y g son funciones regulares, por lo que f fácilmente podría haber escriba Int -> Int. De nuevo, Haskell no puede hacer esto; necesitas tipos dependientes para hacer cosas como esta.

Los lenguajes funcionales típicos no son realmente adecuados para escribir pruebas, no solo porque carecen de tipos dependientes, sino también porque⊥, que tiene el tipo a para todos a, actúa como una prueba de cualquier proposición. Pero total lenguajes como Coq y Agda explotan la correspondencia para actuar como sistemas de prueba así como lenguajes de programación de tipo dependiente.

 40
Author: ehird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-07 22:20:04

Quizás la mejor manera de entender lo que significa es comenzar (o intentar) usando tipos como proposiciones y programas como pruebas. Es mejor aprender un lenguaje con tipos dependientes, como Agda (está escrito en Haskell y similar a Haskell). Hay varios artículos y cursos sobre ese idioma. Aprender que un Agda está incompleto, pero es tratar de simplificar las cosas, al igual que LYAHFGG libro.

Aquí hay un ejemplo de un simple prueba:

{-# OPTIONS --without-K #-} -- we are consistent

module Equality where

-- Peano arithmetic.
-- 
--   ℕ-formation:     ℕ is set.
-- 
--   ℕ-introduction:  o ∈ ℕ,
--                    a ∈ ℕ | (1 + a) ∈ ℕ.
-- 
data ℕ : Set where
  o : ℕ
  1+ : ℕ → ℕ

-- Axiom for _+_.
-- 
--   Form of ℕ-elimination.
-- 
infixl 6 _+_
_+_ : ℕ → ℕ → ℕ
o + m = m
1+ n + m = 1+ (n + m)

-- The identity type for ℕ.
-- 
infix 4 _≡_
data _≡_ (m : ℕ) : ℕ → Set where
  refl : m ≡ m

-- Usefull property.
-- 
cong : {m n : ℕ} → m ≡ n → 1+ m ≡ 1+ n
cong refl = refl

-- Proof _of_ mathematical induction:
-- 
--   P 0, ∀ x. P x → P (1 + x) | ∀ x. P x.
-- 
ind : (P : ℕ → Set) → P o → (∀ n → P n → P (1+ n)) → ∀ n → P n
ind P P₀ _ o = P₀
ind P P₀ next (1+ n) = next n (ind P P₀ next n)

-- Associativity of addition using mathematical induction.
-- 
+-associative : (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-associative m n p = ind P P₀ is m
  where
    P : ℕ → Set
    P i = (i + n) + p ≡ i + (n + p)
    P₀ : P o
    P₀ = refl
    is : ∀ i → P i → P (1+ i)
    is i Pi = cong Pi

-- Associativity of addition using (dependent) pattern matching.
-- 
+-associative′ : (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-associative′ o _ _ = refl
+-associative′ (1+ m) n p = cong (+-associative′ m n p)

Allí se puede ver (m + n) + p ≡ m + (n + p) proposición como tipo y su prueba como función. Hay técnicas más avanzadas para tales pruebas (por ejemplo, razonamiento de preorden, combinadores en Agda es como tácticas en Coq).

Qué más se puede probar:

  • head ∘ init ≡ head para vectores, aquí .

  • Su compilador produce un programa cuya ejecución da el mismo valor que el valor obtenido en la interpretación del mismo programa (host) , aquí , para Coq. Este libro también es una buena lectura sobre el tema de modelado de lenguaje y verificación de programas.

  • Cualquier otra cosa que se pueda probar en matemáticas constructivas, ya que la teoría de tipos de Martin-Löf en su poder expresivo es equivalente a ZFC. De hecho, el isomorfismo de Curry-Howard puede extenderse a física y topología y a topología algebraica .

 2
Author: JJJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:53:56

La única respuesta sensata que se me ocurre es esta: Si tienes una función X -> Y -> Z, entonces la firma de tipo dice "suponiendo que es posible construir un valor de tipo X, y otro de tipo Y, entonces es posible construir un valor de tipo Z". Y el cuerpo de la función describe exactamente cómo harías esto. Eso parece tener sentido, pero no es muy interesante. Así que claramente tiene que haber algo más que esto...

Bueno, sí, hay bastante más aún, porque tiene muchas implicaciones y abre muchas preguntas.

En primer lugar, su discusión del CHI se enmarca exclusivamente en términos de tipos de implicación/función (->). Usted no habla de esto, pero probablemente ha visto también cómo la conjunción y la disyunción corresponden a los tipos de producto y suma respectivamente. Pero, ¿qué pasa con otros operadores lógicos como la negación, la cuantificación universal y la cuantificación existencial? Cómo traducimos las pruebas lógicas ¿involucrarlos en programas? Resulta que es más o menos así:

  • La negación corresponde a continuaciones de primera clase. No me pidas que te explique esto.
  • La cuantificación universal sobre variables proposicionales (no individuales) corresponde a polimorfismo paramétrico. Así, por ejemplo, la función polimórfica id realmente tiene el tipo forall a. a -> a
  • La cuantificación existencial sobre variables proposicionales corresponde a un puñado de cosas que tienen que ver con la ocultación de datos o implementación: tipos de datos abstractos, sistemas de módulos y despacho dinámico. Los tipos existenciales de GHC están relacionados con esto.
  • La cuantificación universal y existencial sobre variables individuales conduce a sistemas de tipo dependiente.

Aparte de eso, también significa que todo tipo de pruebas sobre lógicas se traducen instantáneamente en pruebas sobre lenguajes de programación. Por ejemplo, el la decidibilidad de la lógica proposicional intuicionista implica la terminación de todos los programas en el cálculo lambda de tipo simple.

Ahora, ¿qué demonios significa Int - > Int?? o_O

Es un tipo, o alternativamente una proposición. En f :: Int -> Int, (+1) nombra un " programa "(en un sentido específico que admite tanto funciones como constantes como" programas", o alternativamente una prueba. La semántica del lenguaje debe proporcionar f como una regla primitiva de inferencia, o demostrar cómo f es una prueba que puede construirse a partir de tales reglas y premisas.

Estas reglas a menudo se especifican en términos de axiomas ecuacionales que definen los miembros base de un tipo y reglas que le permiten probar qué otros programas habitan ese tipo. Por ejemplo, cambiando de Int a Nat (números naturales desde 0 hacia adelante), podríamos tener estas reglas:

  • Axioma: 0 :: Nat (0 es una prueba primitiva de Nat)
  • Regla: x :: Nat ==> Succ x :: Nat
  • Regla: x :: Nat, y :: Nat ==> x + y :: Nat
  • Regla: x + Zero :: Nat ==> x :: Nat
  • Regla: Succ x + y ==> Succ (x + y)

Estas reglas son suficientes para probar muchos teoremas sobre la adición de números naturales. Estas pruebas también serán programas.

 2
Author: Luis Casillas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-19 17:48:47