L'equivalenza eta per le funzioni è compatibile con l'operazione seq di Haskell?


14

Lemma: Supponendo che eta-equivalenza lo abbiamo (\x -> ⊥) = ⊥ :: A -> B.

Prova: ⊥ = (\x -> ⊥ x)per eta-equivalenza e (\x -> ⊥ x) = (\x -> ⊥)per riduzione sotto la lambda.

Il rapporto Haskell 2010, sezione 6.2 specifica la seqfunzione con due equazioni:

seq :: a -> b -> b
seq ⊥ b = ⊥
seq ab = b, se a ≠ ⊥

Quindi afferma "Di conseguenza, ⊥ non è uguale a \ x -> ⊥, poiché seq può essere usato per distinguerli".

La mia domanda è: è davvero una conseguenza della definizione di seq?

L'argomento implicito sembra essere che seqsarebbe incontestabile se seq (\x -> ⊥) b = ⊥. Tuttavia non sono stato in grado di dimostrare che un simile seqsarebbe impensabile. Mi sembra che seqsia sia monotono, sia continuo, il che lo mette nel regno dell'essere calcolabile.

Un algoritmo che implementa come seq potrebbe funzionare tentando di cercare alcuni xdove f x ≠ ⊥elencando il dominio che finizia con ⊥. Anche se una tale implementazione, anche se possibile, diventa piuttosto pelosa una volta che vogliamo fare seqpolimorfica.

C'è una prova che non v'è calcolabile seqche identifica (\x -> ⊥)con ⊥ :: A -> B? In alternativa, c'è qualche costruzione seqche si identifica (\x -> ⊥)con ⊥ :: A -> B?

Risposte:


6

Innanzitutto, cerchiamo di essere espliciti su come si seqdistingue da λ x . :λx.

bottom :: a
bottom = bottom

eta :: a -> b
eta x = bottom

-- This terminates
fortytwo = seq eta 42

-- This does not terminate
infinity = seq bottom 42

È quindi un fatto sperimentale che in Haskell e λ x . sono distinguibili dal punto di vista operativo. È anche un dato di fatto, e abbastanza ovvio, calcolabile perché Haskell lo calcola. Tanto su Haskell. Stai chiedendo del fraseggio molto particolare della documentazione di Haskell. L'ho letto dicendo che dovrebbe soddisfare le due equazioni date, ma quelle due equazioni non sono sufficienti per la definizione di . Ecco perché: posso darti due modelli di (semplicemente digitati) λλx.seqseqseqλ -calculus in cui seqè calcolabile e soddisfa le equazioni date, ma in uno dei modelli e λ x . λX. d'accordo, mentre nell'altro no.

In un semplice modello teorico-dominio in cui le espressioni sono interpretate nel dominio delle funzioni continue [ D E ] abbiamo = λ x . , ovviamente. Prendi domini Scott efficaci o qualcosa del genere per rendere tutto calcolabile. È facile da definire in un tale modello.λ[DE]=λX.seq

Possiamo anche avere un modello di -calculus in cui distingue e λ x . , e quindi ovviamente η -rule non può contenere. Ad esempio, possiamo farlo interpretando le funzioni nel dominio [ D E ] , vale a dire il dominio dello spazio delle funzioni con un ulteriore fondo collegato. Ora è, beh, il fondo di [ D E ] , mentre λ x . è l'elemento appena sopra di esso. Non possono essere distinti dall'applicazione perché entrambi valutanoλseqλX.η[DE][DE]λX. , indipendentemente da cosa li applichi (sonoestensivamente uguali). Ma abbiamouna mappa tra domini e sempre distingue il fondo da tutti gli altri elementi.seq


1
È un fatto sperimentale che in GHC e / o Hugs ⊥ e λx.⊥. Fortunatamente, Haskell non è definito da un'implementazione. La mia domanda suggerisce che Haskell non è specificato per quanto riguarda il seq.
Russell O'Connor,

Puoi dare un riferimento a ciò che intendi per "domini Scott efficaci" Presumibilmente ciò non implica che l'ordine parziale sia decidibile. Inoltre, lo STLC non è polimorfico, ma lo è Haskell. Di solito Haskell viene interpretato nel Sistema F o in uno dei suoi derivati. In che modo ciò influisce sul tuo argomento?
Russell O'Connor,

Sezione 1.1.4 del mio dottorato dissertation andrej.com/thesis/thesis.pdf ha una breve definizione di domini Scott efficaci, e questo è in realtà il primo hit di Google che è liberamente disponibile.
Andrej Bauer,

2
Se scrivi una prova per me otterrai un'implementazione di Haskell 98 in cui la regola eta sostiene che l'ottimizzazione di (foldr (\ ab -> fab) z xs) su (foldr fz xs) provoca un aumento asintotico delle prestazioni da O (n ^ 2) a O (n) (vedi ghc.haskell.org/trac/ghc/ticket/7436 ). Più convincente consentirà a un NewTypeWrapper in (NewTypeWrapper. F) di essere ottimizzato senza forzare l'espansione di eta f e impedire alcune penalità di prestazioni asintotiche attualmente imposte da newTypes in GHC (ad esempio nell'uso di foldr).
Russell O'Connor,

1
In realtà, dovresti assicurarti che il tuo compilatore implementi sempre come . Cioè, potresti essere tentato di non contrarre sempre e quindi in linea di principio λ x . e λX.λX. sarebbero "a volte distinguibili", una situazione molto pericolosa. Per essere sicuri che non sia così, è necessario implementare seqin modo intelligente che comporta la generazione infinita di processi, ciascuno dei quali applica la propria funzione a un elemento di base. Se uno qualsiasi dei processi termina, seqpuò procedere. Sarebbe interessante vedere se possiamo farlo in sequenza. Hmm.
Andrej Bauer

2

Si noti che la specifica per seqcui si cita è non sua definizione. Per citare il rapporto di Haskell "La funzione seq è definita dalle equazioni : [e quindi dalle equazioni fornite]".

L'argomento suggerito sembra essere che seq sarebbe imputabile se seq (\ x -> ⊥) b = ⊥.

Tale comportamento violerebbe le specifiche di seq.

È importante sottolineare che, da allora seq è polimorfico, seqnon può essere definito in termini di decostruttori (proiezioni / corrispondenza dei modelli, ecc.) Su uno dei due parametri.

Esiste una prova che non esiste un seq calcolabile che identifichi (\ x -> ⊥) con ⊥ :: A -> B?

Se seq' (\x -> ⊥) b, uno potrebbe pensare che potremmo applicare il primo parametro (che è una funzione) ad un certo valore e quindi uscire ⊥. Tuttavia, seqnon può mai identificare il primo parametro con un valore di funzione (anche se sembra essere uno per un certo uso seq) a causa del suo tipo polimorfico parametrico. Parametricità significa che non sappiamo nulla dei parametri. Inoltre, seqnon potrà mai prendere un'espressione e decidere "è questo ⊥?" (cfr. il problema di Halting), seqpuò solo provare a valutarlo e divergere da solo a ⊥.

Che cosa seq fa è valutare il primo parametro (non completamente, ma in "forma normale testa debole" [1], cioè al costruttore più in alto), quindi restituire il secondo parametro. Se il primo parametro sembra essere (cioè un calcolo non terminante), la valutazione causa seqnon terminazione, e quindi seq ⊥ a = ⊥.

[1] Teoremi liberi in presenza di seq - Johann, Voigtlander http://www.iai.uni-bonn.de/~jv/p76-voigtlaender.pdf


La specifica che do per seq è la definizione di seq perché è esattamente ciò che dice il rapporto Haskell 2010 nella Sezione 6.2. La definizione operativa di seq non è supportata dal report Haskell 2010: le parole "head normal form" compaiono solo una volta nel report in un contesto totalmente diverso. Inoltre, non è coerente con la mia comprensione che GHC ridurrà spesso il secondo argomento a seq prima del primo argomento, oppure il primo argomento non sarà affatto ridotto perché l'analizzatore di rigore ha dimostrato che non è staticamente inferiore.
Russell O'Connor,

La parametricità non dice direttamente che non possiamo applicare alcun decostruttore, né dice che non possiamo mai identificare il primo parametro con un valore di funzione. Tutta la parametercità dice per il calcolo polimorfo lambda con punti fissi è che seq può assorbire funzioni rigide, o più in generale certe relazioni strette valgono per termini che contengono seq. Ammetto che è plausibile che la parametricità possa essere usata per dimostrare (\ x -> ⊥) & ne; ⊥, ma vorrei vedere una prova rigorosa.
Russell O'Connor,

Nel caso di una funzione f : forall a . a -> T(dove Tè un altro tipo), quindi fnon può applicare alcun decostruttore al suo primo argomento poiché non sa quali decostruttori applicare. Non possiamo fare un "caso" sui tipi. Ho cercato di migliorare la risposta di cui sopra (tra cui citando le informazioni sulla seqvalutazione per dirigere la forma normale).
Dorchard,

Posso provare a fare la prova rigorosa più tardi se trovo il tempo (usare le relazioni nello stile di Reynolds potrebbe essere un buon approccio).
Dorchard,

@ RussellO'Connor: la descrizione di seq non è "incoerente" con quei comportamenti, è solo una specifica operativa (e i comportamenti sono ottimizzazioni che non cambiano il risultato finale).
Blaisorblade,

2

λX.λX.

Samson Abramsky ha esaminato questo problema molto tempo fa e ha scritto un documento intitolato " The Lazy Lambda Calculus ". Quindi, se vuoi definizioni formali, è qui che potresti guardare.


1
Apparentemente, questi dettagli sono definiti solo desugarando nel "kernel Haskell". Dove è definito è? Il rapporto dice, nel sec. 1.2 : "Sebbene il kernel non sia formalmente specificato, è essenzialmente una variante leggermente zuccherata del calcolo lambda con una semantica denotazionale semplice. La traduzione di ciascuna struttura sintattica nel kernel è data quando viene introdotta la sintassi."
Blaisorblade,

Il rapporto Haskell 2010 dice lo stesso , sorprendentemente.
Blaisorblade,

Grazie per il riferimento ad Abramsky! L'ho scremato per vedere come risponde alla domanda e ho trovato la seguente risposta: cstheory.stackexchange.com/a/21732/989
Blaisorblade

2

Dimostrando che λ x. Ω ‌ ≠ Ω in è uno degli obiettivi che Abramsky stabilisce per la sua pigra teoria del calcolo lambda (pagina 2 di suo articolo , già citato da Uday Reddy), perché entrambi sono in forma normale testa debole. A partire dalla definizione 2.7, discute esplicitamente che la riduzione dell'et λ x. M x → M non è generalmente valido, ma è possibile se M termina in ogni ambiente. Ciò non significa che M debba essere una funzione totale, ma solo che la valutazione di M deve terminare (riducendo ad esempio una lambda).

La tua domanda sembra essere motivata da preoccupazioni pratiche (prestazioni). Tuttavia, anche se il Rapporto Haskell potrebbe essere meno del tutto chiaro, dubito che equiparare λ x. ‌ ‌con ⊥ produrrebbe un'utile attuazione di Haskell; se si implementi o meno Haskell '98 è discutibile, ma data l'osservazione, è chiaro che gli autori lo intendevano.

Infine, come fa seq a generare elementi per un tipo di input arbitrario? (So ​​che QuickCheck definisce la tabella dei tipi arbitraria per questo, ma non ti è permesso aggiungere tali vincoli qui). Questo viola la parametricità.

Aggiornato : non sono riuscito a codificare questo diritto (perché non ho una conoscenza fluente di Haskel) e la correzione sembra richiedere runSTaree nidificate . Ho provato a usare una singola cella di riferimento (nella monade ST) per salvare tali elementi arbitrari, leggerli in seguito e renderli universalmente disponibili. La parametricità dimostra che break_parametricitynon è possibile definire di seguito (tranne restituendo bottom, ad esempio un errore), mentre potrebbe recuperare gli elementi generati dal seq proposto.

import Control.Monad.ST
import Data.STRef
import Data.Maybe

produce_maybe_a :: Maybe a
produce_maybe_a = runST $ do { cell <- newSTRef Nothing; (\x -> writeSTRef cell (Just x) >> return x) `seq` (readSTRef cell) }

break_parametricity :: a
break_parametricity = fromJust produce_maybe_a

Devo ammettere che sono leggermente confuso nel formalizzare la prova di parametricità necessaria qui, ma questo uso informale della parametricità è standard in Haskell; ma ho imparato dagli scritti di Derek Dreyer che la teoria necessaria è stata rapidamente elaborata in questi ultimi anni.

modifiche:

  • Non sono nemmeno sicuro che tu abbia bisogno di quelle estensioni, che sono studiate per linguaggi di tipo ML, imperativi e non tipizzati, o se le teorie classiche della parametricità coprono Haskell.
  • Inoltre, ho citato Derek Dreyer semplicemente perché mi sono imbattuto solo in seguito nel lavoro di Uday Reddy - ne ho appreso solo recentemente da "L'essenza di Reynolds". (Ho iniziato a leggere davvero letteratura sulla parametricità solo nell'ultimo mese).

La valutazione (\x -> writeSTRef cell (Just x) >> return x)su input casuali non esegue una scrittura sulla cella. runSTVengono eseguiti solo i comandi ST che entrano nella sequenza passata . Allo stesso modo, l'esecuzione main = (putStrLn "Hello") `seq` (return ())non stampa nulla sul display.
Russell O'Connor,

@ RussellO'Connor, ovviamente hai ragione: i test sono difficili poiché seq non ha il comportamento di cui discutiamo. Ma penso ancora che la generazione di elementi rompa la parametricità di per sé. Proverò a risolvere la risposta per esemplificarlo.
Blaisorblade,

Hm, la soluzione ovvia alla risposta richiede l'annidamento delle regioni runST e l'utilizzo della cella dalla regione esterna in quella interna, ma ciò non è consentito.
Blaisorblade,
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.