web-dev-qa-db-fra.com

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

Propositions (P -> Q) -> Q et P \/ Q sont équivalents.

Existe-t-il un moyen d'assister à 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 = id et to . from = id?

18
user12333506

Les propositions (P -> Q) -> Q Et P \/ Q Sont équivalentes.

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

Dans la logique constructive, nous n'avons pas loi du milieu excl , 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)), renvoyer Left x.
  • si P est faux, alors dans Haskell, nous aurons la fonction nx :: P -> Void. Puis absurd . nx :: P -> Q (Nous pouvons atteindre n'importe quel type, nous prenons Q) et appelons f :: (P -> Q) -> Q) avec absurd . nx Pour obtenir la valeur de 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 existe, par exemple Bool est habité pour 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.

14
phadej

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

Either P Q est 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 en classique logique n'est pas pertinent.)

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

9
chi

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

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

a et b sont 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 b tout en ignorant l'argument f. Mais l'utilisation de f est également impossible. Sans connaître les types a et b, vous ne pouvez pas créer une valeur de type a -> b pour 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.

4
Carl

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 cela un peu plus explicitement. Supposons que nous ayons

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

et nous définissons 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

Maintenant, prouvons 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

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


Vérifions que lem 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)
2
dfeuer

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'une valeur a ou b. Nous n'avons aucun moyen de construire une valeur a, mais nous avons une fonction qui renvoie un b que nous pourrions appeler. Pour ce faire, nous devons fournir une fonction qui convertit un a en 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 valeur b, nous ne pouvons pas la construire autrement, 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
0
Bergi