La necessità di puro nelle applicazioni


19

Sto imparando le candidature di Haskell. Mi sembra (probabilmente sbaglio) che la purefunzione non sia realmente necessaria, ad esempio:

pure (+) <*> [1,2,3] <*> [3,4,5]

può essere scritto come

(+) <$> [1,2,3] <*> [3,4,5]

Qualcuno può spiegare il vantaggio che la purefunzione offre sulla mappatura esplicita con fmap?


1
Hai ragione - pure f <*> xè esattamente lo stesso di fmap f x. Sono sicuro che c'è qualche motivo per cui è purestato incluso Applicative, ma non sono del tutto sicuro del perché.
Brad

4
Non ho tempo per una risposta, e non sono convinto che ne farebbe comunque una buona o completa, ma un'osservazione: purepermette di usare valori "puri" in un calcolo Applicativo. Mentre, come si osserva in modo corretto, pure f <*> xè lo stesso che f <$> x, non esiste un'equivalente per, diciamo, f <*> x <*> pure y <*> z. (Almeno non la penso così.)
Robin Zigmond,

3
Come altra, più teorica, giustificazione - esiste una formulazione alternativa che la collega strettamente alla Monoidclasse importante - in cui purecorrisponde Monoidall'elemento identità. (Ciò suggerisce che Applicativesenza purepotrebbe essere interessante, poiché Semigroup- che è un Monoidsenza necessariamente avere un'identità - viene ancora utilizzato. In realtà, ora ci penso, mi sembra di ricordare che PureScript abbia esattamente una tale pureclasse "Applicativo senza ", anche se so a cosa serve.)
Robin Zigmond,

2
@RobinZigmond fmap (\f' x' z' -> f' x' y z') f <*> x <*> z, penso. L'idea è nella Applicativedocumentazione come legge di "interscambio".
HTNW,

3
@RobinZigmond Applicativesenza pureesiste come Applyda semigroupoids .
duplode il

Risposte:


8

Sono al limite della mia competenza qui, quindi non prenderlo per più di quello che è, ma è stato un po 'troppo lungo per un commento.

Potrebbero esserci ragioni pratiche da includere purenella classe del tipo, ma molte astrazioni di Haskell derivano da basi teoriche, e credo che anche questo sia il caso Applicative. Come dice la documentazione, è un forte funzione monoidale lassista (vedi https://cstheory.stackexchange.com/q/12412/56098 per un'elaborazione). Suppongo che pureserva da identità , proprio come returnfa per Monad(che è un monoide nella categoria degli endofunctor ).

Considerare puree liftA2:

pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c

Se strizzi gli occhi un po ', potresti essere in grado di immaginare che liftA2sia un'operazione binaria, che è anche ciò che la documentazione afferma:

Solleva una funzione binaria in azioni.

pure, quindi, è l'identità corrispondente.


6
Esattamente. Applicativesenza puresarebbe un, hm, funzione semigruppo invece che monoidale.
lasciato il

20

fmapnon sempre lo taglia. In particolare, pureè ciò che ti consente di introdurre f(dove si ftrova Applicative) quando non lo hai già. Un buon esempio è

sequence :: Applicative f => [f a] -> f [a]

Prende un elenco di "azioni" che producono valori e lo trasforma in un'azione che produce un elenco di valori. Cosa succede quando non ci sono azioni nell'elenco? L'unico risultato sensato è un'azione che non produce valori:

sequence [] = pure [] -- no way to express this with an fmap
-- for completeness
sequence ((:) x xs) = (:) <$> x <*> sequence xs

Se non lo avessi fatto pure, saresti costretto a richiedere un elenco non vuoto di azioni. Potresti sicuramente farlo funzionare, ma è come parlare di addizione senza menzionare 0 o moltiplicazione senza 1 (come hanno detto altri, perché gli Applicatives sono monoidali). Incontrerai ripetutamente casi limite che potrebbero essere facilmente risolti purema che dovrebbero invece essere risolti da strane restrizioni sui tuoi input e altri aiuti di banda.

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.