Confusione sul currying e sullo stile senza punti in Haskell
Domanda
Stavo cercando di implementare la funzione
every :: (a -> IO Bool) -> [a] -> IO Bool
quale era l'argomento questa domanda.Ho provato a farlo senza ricorsione esplicita.Mi è venuta in mente il seguente codice
every f xs = liftM (all id) $ sequence $ map f xs
La mia funzione non ha funzionato poiché non era pigra (cosa richiesta nella domanda), quindi nessun voto positivo :-).
Tuttavia, non mi sono fermato qui.Ho provato a creare la funzione senza punti in modo che fosse più breve (e forse anche più interessante).Fin dalle argomentazioni f
E xs
sono gli ultimi nell'espressione che li ho appena eliminati:
every = liftM (all id) $ sequence $ map
Ma questo non ha funzionato come previsto, anzi non ha funzionato affatto:
[1 of 1] Compiling Main ( stk.hs, interpreted ) stk.hs:53:42: Couldn't match expected type `[m a]' against inferred type `(a1 -> b) -> [a1] -> [b]' In the second argument of `($)', namely `map' In the second argument of `($)', namely `sequence $ map' In the expression: liftM (all id) $ sequence $ map Failed, modules loaded: none.
Perché?Avevo l'impressione che fosse possibile semplicemente eliminare gli argomenti finali della funzione, che fondamentalmente è ciò che riguarda il currying.
Soluzione
La definizione di $ è
f $ x = f x
Mettiamo tra parentesi la tua funzione:
every f xs = (liftM (all id)) (sequence ((map f) xs))
e la tua versione al curry:
every = (liftM (all id)) (sequence map)
Come hai notato, questi non sono identici.Puoi eliminare gli argomenti finali della funzione solo quando sono l'ultima cosa applicata.Per esempio,
f x = g c x
è effettivamente
f x = (g c) x
e l'applicazione di (g c) a x viene per ultima, quindi puoi scrivere
f = g c
Uno schema con l'operatore dell'applicazione $ è che spesso diventa l'operatore di composizione.nelle versioni senza punti.Questo è perché
f $ g $ x
è equivalente a
(f . g) $ x
Per esempio,
every f xs = liftM (all id) $ sequence $ map f xs
può diventare
every f xs = (liftM (all id) . sequence . map f) xs
a quel punto puoi eliminare xs:
every f = liftM (all id) . sequence . map f
Eliminare l'argomento f è più difficile perché viene applicato prima dell'operatore di composizione.Usiamo la definizione di punto da http://www.haskell.org/haskellwiki/Pointfree:
dot = ((.) . (.))
Con i punti, questo è
(f `dot` g) x = f . g x
ed è esattamente ciò di cui abbiamo bisogno per rendere tutti i punti completamente gratuiti:
every = (liftM (all id) . sequence) `dot` map
Purtroppo, a causa delle restrizioni nel sistema di tipi Haskell, questo necessita di una firma di tipo esplicita:
every :: (Monad m) => (a -> m Bool) -> [a] -> m Bool