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.