Confusão sobre currying e estilo livre ponto em Haskell
Pergunta
Eu estava tentando implementar a função
every :: (a -> IO Bool) -> [a] -> IO Bool
que foi o tema para esta questão . Tentei fazer isso sem recursão explícita ??strong>. Eu vim com o seguinte código
every f xs = liftM (all id) $ sequence $ map f xs
A minha função não funcionou uma vez que não era preguiçoso (que foi requerido na questão), por isso não upvotes lá: -).
No entanto, eu não param por aí. Eu tentei fazer a função sem ponto para que ele seria mais curto (e talvez até mais frio). Desde o f
argumentos e xs
são os últimos na expressão Eu só caiu-lhes:
every = liftM (all id) $ sequence $ map
Mas isso não funcionou como esperado, na verdade, não fez a todos os:
[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.
Por que isso? Fiquei com a impressão de que era possível deixar cair simplesmente arrastando os argumentos da função, que basicamente é o que currying é sobre.
Solução
A definição de $ é
f $ x = f x
Vamos entre parênteses plenamente a sua função:
every f xs = (liftM (all id)) (sequence ((map f) xs))
e sua versão curry:
every = (liftM (all id)) (sequence map)
Como você percebeu, estes não são idênticos. Você só pode soltar arrastando argumentos da função quando eles são a última coisa aplicada. Por exemplo,
f x = g c x
é realmente
f x = (g c) x
e a aplicação de (g c) x vem por último, assim você pode escrever
f = g c
Um padrão com o operador de inscrição R $ é que muitas vezes torna-se o operador de composição. em versões pontos livres. Isso ocorre porque
f $ g $ x
é equivalente a
(f . g) $ x
Por exemplo,
every f xs = liftM (all id) $ sequence $ map f xs
pode se tornar
every f xs = (liftM (all id) . sequence . map f) xs
em que ponto você pode cair xs:
every f = liftM (all id) . sequence . map f
Eliminar o argumento f é mais difícil, porque ele é aplicado antes que o operador composição. Vamos usar a definição do ponto de http://www.haskell.org/haskellwiki/Pointfree :
dot = ((.) . (.))
Com pontos, este é
(f `dot` g) x = f . g x
e é exatamente o que precisamos para tornar cada totalmente pontos-livres:
every = (liftM (all id) . sequence) `dot` map
Infelizmente, devido a restrições no sistema do tipo Haskell, este precisa de uma assinatura de tipo explícito:
every :: (Monad m) => (a -> m Bool) -> [a] -> m Bool