Existe-t-il un moyen de réaliser une fonction de type ((a -> b) -> b) -> Soit ab?

18

Les propositions (P -> Q) -> Qet P \/ Qsont équivalentes.

Existe-t-il un moyen de témoigner de cette équivalence à 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 = ???

tel que

from . to = idet to . from = id?


la source
Il me semble évident que c'est impossible, mais peut-être que je me trompe. Si c'est le cas, un point de départ utile est qu'une fonction de type entièrement polymorphe ((a -> b) -> b)est isomorphe à a: la seule implémentation possible est g f = f someHardcodedA.
amalloy
1
@amalloy il y a une autre implémentation possible:g = const someHardcodedB
Fyodor Soikin
Ah, bien sûr. C'est soit aou b. Logique.
amalloy
1
Si Haskell avait appelé / cc, to f = callcc (\k -> k (Right (f (\a -> k (Left a)))))cela fonctionnerait. (Ceci est une preuve classique valide de l'implication.)
benrg

Réponses:

14

Les propositions (P -> Q) -> Qet P \/ Qsont équivalentes.

Cela est vrai dans la logique classique, mais pas dans la logique constructive.

Dans la logique constructive, nous n'avons pas de loi du milieu exclu , c'est-à-dire que nous ne pouvons pas commencer notre réflexion avec "soit P est vrai ou P n'est pas vrai".

Classiquement, nous raisonnons comme:

  • si P est vrai (c'est-à-dire que nous avons ( x :: P)), alors retour Left x.
  • si P est faux, alors en Haskell, nous aurions nx :: P -> Void fonction. Ensuite absurd . nx :: P -> Q(nous pouvons piquer n'importe quel type, nous prenons Q) et appelez f :: (P -> Q) -> Q)avec absurd . nxpour obtenir la valeur du type Q.

Le problème, qu'il n'y a pas de fonction générale d'un type:

lem :: forall p. Either p (p -> Void)

Pour certains types de béton, il y a, par exemple, Boolest habité afin que nous puissions écrire

lemBool :: Either Bool (Bool -> Void)
lemBool = Left True -- arbitrary choice

mais encore une fois, en général, nous ne pouvons pas.

phadej
la source
9

Non, c'est impossible. Considérez le cas spécial où Q = Void.

Either P Qest alors Either P Void, qui est isomorphe à 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

Par conséquent, si nous avions un terme de fonction

impossible :: ((P -> Void) -> Void) -> Either P Void

nous pourrions aussi avoir un terme

impossible2 :: ((P -> Void) -> Void) -> P
impossible2 = iso_inv . impossible

Selon la correspondance Curry-Howard, ce serait une tautologie dans la logique intuitionniste :

((P -> False) -> False) -> P

Mais ce qui précède est l'élimination de la double négation, qui est bien connue pour être impossible à prouver dans la logique intuitionniste - d'où une contradiction. (Le fait que nous puissions le prouver dans la logique classique n'est pas pertinent.)

(Remarque finale: cela suppose que le programme Haskell se termine. Bien sûr, en utilisant une récursion infinie undefined, et des moyens similaires pour éviter de retourner un résultat, nous pouvons habiter n'importe quel type dans Haskell.)

chi
la source
4

Non, ce n'est pas possible, mais c'est un peu subtil. Le problème est que les variables de type aet bsont universellement quantifiées.

to :: ((a -> b) -> b) -> Either a b
to f = ...

aet bsont universellement quantifiés. L'appelant choisit de quel type il s'agit, vous ne pouvez donc pas simplement créer une valeur de l'un ou l'autre type. Cela implique que vous ne pouvez pas simplement créer une valeur de type Either a btout en ignorant l'argument f. Mais l'utilisation fest également impossible. Sans savoir quels types aet quels types b, vous ne pouvez pas créer de valeur de type a -> bvers laquelle passer f. Il n'y a tout simplement pas assez d'informations disponibles lorsque les types sont universellement quantifiés.

Quant à savoir pourquoi l'isomorphisme ne fonctionne pas dans Haskell - êtes-vous sûr que ces propositions sont équivalentes dans une logique intuitionniste constructive? Haskell n'implémente pas une logique déductive classique.

Carl
la source
2

Comme d'autres l'ont souligné, cela est impossible car nous n'avons pas la loi du milieu exclu. Permettez-moi de passer en revue un peu plus explicitement. Supposons que nous ayons

bogus :: ((a -> b) -> b) -> Either a b

et nous avons mis b ~ Void. Ensuite, nous obtenons

-- 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

Prouvons maintenant la double négation de la loi du milieu exclu appliquée à une proposition spécifique .

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

Alors maintenant

lem :: Either a (a -> Void)
lem = double_neg_elim nnlem

lemne peut clairement pas exister car apeut coder la proposition selon laquelle toute configuration de machine de Turing que je choisirai s'arrêtera.


Vérifions que lemc'est suffisant:

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)
dfeuer
la source
0

Je n'ai aucune idée de savoir si cela est valable en termes de logique, ou ce que cela signifie pour votre équivalence, mais oui, il est possible d'écrire une telle fonction en Haskell.

Pour construire un Either a b, nous avons besoin d'un aou d'une bvaleur. Nous n'avons aucun moyen de construire une avaleur, mais nous avons une fonction qui renvoie un bque nous pourrions appeler. Pour ce faire, nous devons fournir une fonction qui convertit un aen un b, mais étant donné que les types sont inconnus, nous pourrions au mieux créer une fonction qui renvoie une constante b. Pour obtenir cette bvaleur, nous ne pouvons pas la construire autrement qu'avant, donc cela devient un raisonnement circulaire - et nous pouvons résoudre cela en créant simplement un point fixe :

to :: ((a -> b) -> b) -> Either a b
to f = let x = f (\_ -> x) in Right x
Bergi
la source