Путаница с каррированием и стилем без точек в Haskell
Вопрос
Я пытался реализовать функцию
every :: (a -> IO Bool) -> [a] -> IO Bool
какая была тема для этот вопрос.Я пытался это сделать без явной рекурсии.Я придумал следующий код
every f xs = liftM (all id) $ sequence $ map f xs
Моя функция не работала, так как она не была ленивой (что требовалось в вопросе), поэтому никаких голосов "за" не было :-).
Однако я не остановился на достигнутом.Я попытался сделать функцию без очков чтобы было короче (а возможно даже круче).Поскольку аргументы f
и xs
являются последними в выражении, которое я только что бросил:
every = liftM (all id) $ sequence $ map
Но это не сработало так, как ожидалось, на самом деле это вообще не сработало:
[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.
Почему это?У меня сложилось впечатление, что можно просто отбросить конечные аргументы функции, что, по сути, и представляет собой каррирование.
Решение
Определение $
f $ x = f x
Давайте полностью заключим вашу функцию в круглые скобки:
every f xs = (liftM (all id)) (sequence ((map f) xs))
и ваша версия с карри:
every = (liftM (all id)) (sequence map)
Как вы заметили, они не идентичны.Вы можете удалять конечные аргументы функции только тогда, когда они применяются последними.Например,
f x = g c x
на самом деле
f x = (g c) x
и применение (g c) к x идет последним, поэтому вы можете написать
f = g c
Одна из особенностей оператора приложения $ заключается в том, что он часто становится оператором композиции.в безбалльных версиях.Это потому что
f $ g $ x
эквивалентно
(f . g) $ x
Например,
every f xs = liftM (all id) $ sequence $ map f xs
может стать
every f xs = (liftM (all id) . sequence . map f) xs
в этот момент вы можете отказаться от xs:
every f = liftM (all id) . sequence . map f
Устранить аргумент f сложнее, поскольку он применяется перед оператором композиции.Давайте воспользуемся определением точки из http://www.haskell.org/haskellwiki/Pointfree:
dot = ((.) . (.))
С баллами это
(f `dot` g) x = f . g x
и это именно то, что нам нужно, чтобы сделать все полностью свободными от очков:
every = (liftM (all id) . sequence) `dot` map
К сожалению, из-за ограничений в системе типов Haskell здесь требуется явная сигнатура типа:
every :: (Monad m) => (a -> m Bool) -> [a] -> m Bool