Existe uma maneira de realizar uma função do tipo ((a -> b) -> b) -> ou ab?

18

Proposições (P -> Q) -> Qe P \/ Qsão equivalentes.

Existe uma maneira de testemunhar essa equivalência em 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 = ???

de tal modo que

from . to = ide to . from = id?


fonte
Parece-me óbvio que isso é impossível, mas talvez eu esteja errado. Nesse caso, um ponto de partida útil é que uma função com o tipo polimórfico ((a -> b) -> b)é isomórfica a: a única implementação possível é g f = f someHardcodedA.
amalloy
11
@amalloy existe outra implementação possível:g = const someHardcodedB
Fyodor Soikin
Ah, claro. É aou b. Faz sentido.
Amalloy # /
11
Se Haskell tivesse chamado / cc, to f = callcc (\k -> k (Right (f (\a -> k (Left a)))))funcionaria. (Esta é uma prova clássica válida da implicação.)
benrg

Respostas:

14

Proposições (P -> Q) -> Qe P \/ Qsão equivalentes.

Isso é verdade na lógica clássica, mas não na lógica construtiva.

Na lógica construtiva, não temos lei do meio excluído , ou seja, não podemos começar nosso pensamento com "P é verdadeiro ou P não é verdadeiro".

Classicamente, raciocinamos como:

  • se P for verdadeiro (ou seja, temos ( x :: P)), retorne Left x.
  • se P for falso, então, em Haskell, teríamos nx :: P -> Voidfunção. Em seguida, absurd . nx :: P -> Q(que pode pico qualquer tipo, tomamos Q) e chamar dada f :: (P -> Q) -> Q)com absurd . nxpara obter o valor do tipo Q.

O problema é que não há função geral de um tipo:

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

Para alguns tipos de concreto existem, por exemplo, Boolé habitado para que possamos escrever

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

mas, novamente, em geral, não podemos.

phadej
fonte
9

Não, é impossível. Considere o caso especial em que Q = Void.

Either P Qé então Either P Void, o que é isomórfico 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

Portanto, se tivéssemos um termo de função

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

nós também poderíamos ter um termo

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

De acordo com a correspondência de Curry-Howard, isso seria uma tautologia na lógica intuicionista :

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

Mas o exposto acima é a eliminação da dupla negação, que é bem conhecida por ser impossível provar na lógica intuicionista - daí uma contradição. (O fato de podermos provar isso na lógica clássica não é relevante.)

(Nota final: isso pressupõe que o programa Haskell seja encerrado. É claro que, usando recursão infinita undefinede maneiras semelhantes de realmente evitar retornar um resultado, podemos habitar qualquer tipo em Haskell.)

chi
fonte
4

Não, não é possível, mas é um pouco sutil. O problema é que as variáveis ​​de tipo ae bsão universalmente quantificadas.

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

ae bsão universalmente quantificados. O chamador escolhe qual é o tipo, então você não pode simplesmente criar um valor de qualquer tipo. Isso implica que você não pode simplesmente criar um valor do tipo Either a benquanto ignora o argumento f. Mas usar ftambém é impossível. Sem saber quais tipos ae quais bsão, você não pode criar um valor do tipo a -> bpara o qual passar f. Apenas não há informações suficientes disponíveis quando os tipos são universalmente quantificados.

Quanto ao motivo pelo qual o isomorfismo não funciona em Haskell - você tem certeza de que essas proposições são equivalentes em uma lógica intuicionista construtiva? Haskell não implementa uma lógica dedutiva clássica.

Carl
fonte
2

Como outros já apontaram, isso é impossível porque não temos a lei do meio excluído. Deixe-me passar por isso um pouco mais explicitamente. Suponha que tenhamos

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

e nós estabelecemos b ~ Void. Então nós temos

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

Agora, vamos provar a dupla negação da lei do meio excluído , aplicada a uma proposição 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

Então agora

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

lemclaramente não pode existir, porque apode codificar a proposição de que qualquer configuração da máquina de Turing que eu escolher será interrompida.


Vamos verificar se lemé 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)
dfeuer
fonte
0

Não tenho idéia de se isso é válido em termos de lógica ou o que isso significa para sua equivalência, mas sim, é possível escrever essa função em Haskell.

Para construir um Either a b, precisamos de um aou de um bvalor. Não temos como construir um avalor, mas temos uma função que retorna uma bque poderíamos chamar. Para fazer isso, precisamos fornecer uma função que converta an aem a b, mas, como os tipos são desconhecidos, poderíamos, na melhor das hipóteses, criar uma função que retorne uma constante b. Para obter esse bvalor, não podemos construí-lo de outra maneira que antes, portanto isso se torna um raciocínio circular - e podemos resolvê-lo simplesmente criando um ponto de correção :

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