C'è un modo per realizzare una funzione di tipo ((a -> b) -> b) -> O ab?


18

Le proposte (P -> Q) -> Qe P \/ Qsono equivalenti.

C'è un modo per testimoniare questa equivalenza in 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 = ???

tale che

from . to = ide to . from = id?


Mi sembra ovvio che ciò sia impossibile, ma forse mi sbaglio. In tal caso, un utile punto di partenza è che una funzione con un tipo completamente polimorfico ((a -> b) -> b)è isomorfa a a: l'unica implementazione possibile è g f = f someHardcodedA.
amalloy,

1
@amalloy c'è un'altra possibile implementazione:g = const someHardcodedB
Fyodor Soikin,

Ah certo. È o ao b. Ha senso.
amalloy,

1
Se Haskell avesse chiamato / cc, allora to f = callcc (\k -> k (Right (f (\a -> k (Left a)))))avrebbe funzionato. (Questa è una valida prova classica dell'implicazione.)
ben

Risposte:


14

Le proposte (P -> Q) -> Qe P \/ Qsono equivalenti.

Questo è vero nella logica classica, ma non nella logica costruttiva.

Nella logica costruttiva non abbiamo la legge del mezzo escluso , cioè non possiamo iniziare il nostro pensiero con "o P è vero o P non è vero".

Classicamente ragioniamo come:

  • se P è vero (cioè abbiamo ( x :: P)) quindi ritorna Left x.
  • se P è falso, allora in Haskell parlare avremmo nx :: P -> Voidfunzione. Quindi absurd . nx :: P -> Q(possiamo raggiungere il picco di qualsiasi tipo, prendiamo Q) e chiamare dato f :: (P -> Q) -> Q)con absurd . nxper ottenere il valore del tipo Q.

Il problema è che non esistono funzioni generali di un tipo:

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

Per alcuni tipi concreti ci sono, ad esempio Boolè abitato in modo che possiamo scrivere

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

ma ancora una volta, in generale non possiamo.


9

No, è impossibile Considera il caso speciale in cui Q = Void.

Either P Qè quindi Either P Void, che è isomorfo 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

Quindi, se avessimo un termine di funzione

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

potremmo anche avere un termine

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

Secondo la corrispondenza Curry-Howard, questa sarebbe una tautologia nella logica intuizionista :

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

Ma quanto sopra è l'eliminazione della doppia negazione, che è ben noto per essere impossibile da dimostrare nella logica intuizionista - quindi una contraddizione. (Il fatto che potremmo dimostrarlo nella logica classica non è rilevante.)

(Nota finale: questo presuppone che il programma Haskell sia terminato. Naturalmente, usando la ricorsione infinita undefinede modi simili per evitare effettivamente di restituire un risultato, possiamo abitare qualsiasi tipo in Haskell.)


4

No, non è possibile, ma è un po 'sottile. Il problema è che le variabili di tipo ae bsono universalmente quantificate.

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

ae bsono universalmente quantificati. Il chiamante sceglie di che tipo sono, quindi non puoi semplicemente creare un valore di entrambi i tipi. Ciò implica che non puoi semplicemente creare un valore di tipo Either a bignorando l'argomento f. Ma anche usare fè impossibile. Senza sapere quali tipi ae bquali, non è possibile creare un valore di tipo a -> ba cui passare f. Non ci sono abbastanza informazioni disponibili quando i tipi sono universalmente quantificati.

Per quanto riguarda il motivo per cui l'isomorfismo non funziona in Haskell - sei sicuro che queste proposizioni siano equivalenti in una logica costruttiva intuizionista? Haskell non implementa una logica deduttiva classica.


2

Come altri hanno sottolineato, questo è impossibile perché non abbiamo la legge del mezzo escluso. Lasciami passare un po 'più esplicitamente. Supponiamo di avere

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

e abbiamo impostato b ~ Void. Quindi otteniamo

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

Ora, proviamo la doppia negazione della legge del mezzo escluso applicata a una proposizione specifica .

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

Così ora

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

lemchiaramente non può esistere perché apuò codificare la proposizione che qualsiasi configurazione della macchina di Turing che mi capita di scegliere si fermerà.


Verifichiamo che lemè sufficiente:

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)

0

Non ho idea di cosa sia valido in termini logici o cosa significhi per la tua equivalenza, ma sì è possibile scrivere una tale funzione in Haskell.

Per costruire un Either a b, abbiamo bisogno di un ao di un bvalore. Non abbiamo modo di costruire un avalore, ma abbiamo una funzione che restituisce un valore bche potremmo chiamare. Per fare ciò, dobbiamo fornire una funzione che converte un ain b, ma dato che i tipi sono sconosciuti, nella migliore delle ipotesi potremmo creare una funzione che restituisca una costante b. Per ottenere quel bvalore, non possiamo costruirlo in nessun altro modo rispetto a prima, quindi questo diventa un ragionamento circolare - e possiamo risolverlo semplicemente creando un fixpoint :

to :: ((a -> b) -> b) -> Either a b
to f = let x = f (\_ -> x) in Right x
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.