haskell - ¿Hay alguna manera de realizar una función de tipo((a-> b)-> b)-> O ab?
logic (5)
Las proposiciones
(P -> Q) -> Q
y
P // Q
son equivalentes.
¿Hay alguna manera de presenciar esta equivalencia en Haskell?
from :: Either a b -> ((a -> b) -> b)
from x = case x of
Left a -> /f -> f a
Right b -> /f -> b
to :: ((a -> b) -> b) -> Either a b
to = ???
tal que
from . to = id
from . to = id
y
to . from = id
to . from = id
?
Las proposiciones
(P -> Q) -> Q
yP // Q
son equivalentes.
Esto es cierto en la lógica clásica, pero no en la lógica constructiva.
En la lógica constructiva no tenemos la ley del medio excluido , es decir, no podemos comenzar a pensar con "o P es verdadero o P no es verdadero".
Clásicamente razonamos como:
-
si P es verdadero (es decir, tenemos (
x :: P
)), entonces devuelveLeft x
. -
si P es falso, entonces en Haskell hablar tendremos
nx :: P -> Void
Funciónnx :: P -> Void
. Entoncesabsurd . nx :: P -> Q
absurd . nx :: P -> Q
(podemos alcanzar cualquier tipo de pico, tomamosQ
) y llamamos dadof :: (P -> Q) -> Q)
conabsurd . nx
absurd . nx
para obtener el valor de tipoQ
El problema es que no hay una función general de un tipo:
lem :: forall p. Either p (p -> Void)
Para algunos tipos concretos hay, por ejemplo,
Bool
está habitado para que podamos escribir
lemBool :: Either Bool (Bool -> Void)
lemBool = Left True -- arbitrary choice
pero de nuevo, en general no podemos.
Como otros han señalado, esto es imposible porque no tenemos la ley del medio excluido. Déjame pasar por eso un poco más explícitamente. Supongamos que tenemos
bogus :: ((a -> b) -> b) -> Either a b
y establecemos
b ~ Void
.
Entonces tenemos
-- chi calls this `impossible2`.
double_neg_elim :: ((a -> Void) -> Void) -> a
bouble_neg_elim f = case bogus f of
Left a -> a
Right v -> absurd v
Ahora, demostremos la doble negación de la ley del medio excluido aplicada a una proposición específica .
nnlem :: forall a. (Either a (a -> Void) -> Void) -> Void
nnlem f = not_not_a not_a
where
not_a :: a -> Void
not_a = f . Left
not_not_a :: (a -> Void) -> Void
not_not_a = f . Right
Y ahora
lem :: Either a (a -> Void)
lem = double_neg_elim nnlem
claramente no puede existir porque puede codificar la proposición de que cualquier configuración de máquina de Turing que elija se detendrá.
Verifiquemos que
lem
sea suficiente:
bogus :: forall a b. ((a -> b) -> b) -> Either a b
bogus f = case lem @a of
Left a -> Left a
Right na -> Right $ f (absurd . na)
No tengo idea de si esto es válido en términos de lógica, o qué significa para su equivalencia, pero sí, es posible escribir dicha función en Haskell.
Para construir un
Either ab
, necesitamos un valor
a
o a
b
.
No tenemos ninguna forma de construir un valor a, pero tenemos una función que devuelve un
b
que podríamos llamar.
Para hacer eso, necesitamos proporcionar una función que convierta a
a
en a
b
, pero dado que los tipos son desconocidos, en el mejor de los casos podríamos hacer una función que devuelva una constante
b
.
Para obtener ese valor
b
, no podemos construirlo de otra manera que antes, por lo que esto se convierte en un razonamiento circular, y podemos resolverlo simplemente creando un punto de
fijación
:
to :: ((a -> b) -> b) -> Either a b
to f = let x = f (/_ -> x) in Right x
No, es imposible
Considere el caso especial donde
Q = Void
.
Either PQ
es
Either P Void
, que es isomorfo a
P
iso :: P -> Either P Void
iso = Left
iso_inv :: Either P Void -> P
iso_inv (Left p) = p
iso_inv (Right q) = absurd q
Por lo tanto, si tuviéramos un término de función
impossible :: ((P -> Void) -> Void) -> Either P Void
también podríamos tener un término
impossible2 :: ((P -> Void) -> Void) -> P
impossible2 = iso_inv . impossible
Según la correspondencia de Curry-Howard, esto sería una tautología en la lógica intuicionista :
((P -> False) -> False) -> P
Pero lo anterior es la eliminación de la doble negación, que es bien sabido que es imposible de probar en la lógica intuicionista , de ahí una contradicción. (El hecho de que podamos probarlo en la lógica clásica no es relevante).
(Nota final: esto supone que nuestro programa Haskell finalizará. Por supuesto, usando una recursión infinita,
undefined
y formas similares para evitar devolver un resultado, podemos habitar cualquier tipo en Haskell).
No, no es posible, pero es un poco sutil.
El problema es que las variables de tipo
b
están cuantificadas universalmente.
to :: ((a -> b) -> b) -> Either a b
to f = ...
b
están universalmente cuantificados.
La persona que llama elige de qué tipo son, por lo que no puede simplemente crear un valor de cualquier tipo.
Esto implica que no puede simplemente crear un valor de tipo
Either ab
mientras ignora el argumento
f
.
Pero usar
f
también es imposible.
Sin saber qué son los tipos
a
y
b
, no puede crear un valor de tipo
a -> b
para pasar a
f
.
Simplemente no hay suficiente información disponible cuando los tipos se cuantifican universalmente.
En cuanto a por qué el isomorfismo no funciona en Haskell, ¿estás seguro de que esas proposiciones son equivalentes en una lógica constructivista intuicionista? Haskell no implementa una lógica deductiva clásica.