Это связано с тем, что filter
является функцией с двумя аргументами. Вы можете обойти это с помощью удобного оператора
(.:) = (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)
-- Important to make it the same precedence as (.)
infixr 9 .:
unique = ((== 1) . length) .: filter
Если вы посмотрите на тип (length .)
в GHCi, вы получите
(length .) :: (a -> [b]) -> a -> Int
Это означает, что она принимает функцию с одним аргументом, которая возвращает список. Если мы посмотрим на тип filter
:
filter :: (a -> Bool) -> [a] -> [a]
Это можно переписать, чтобы сделать его «единственным аргументом» как
filter :: (a -> Bool) -> ([a] -> [a])
И это совершенно явно не совпадает с a -> [b]
! В частности, компилятор не может понять, как сделать так, чтобы ([a] -> [a])
совпадало с [b]
, так как одно — это функция для списков, а другое — просто список. Так что это источник ошибки типа.
Интересно, что оператор .:
можно обобщить для работы с функторами:
(.:) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(.:) = fmap fmap fmap
-- Since the first `fmap` is for the (->) r functor, you can also write this
-- as (.:) = fmap `fmap` fmap === fmap . fmap
Для чего это нужно? Скажем, у вас есть Maybe [[Int]]
, и вам нужна сумма каждого подсписка внутри Just
, при условии, что он существует:
> let myData = Just [[3, 2, 1], [4], [5, 6]]
> sum .: myData
Just [6, 4, 11]
> length .: myData
Just [3, 1, 2]
> sort .: myData
Just [[1,2,3],[4],[5,6]]
Или что, если у вас есть [Maybe Int]
, и вы хотите увеличить каждое из них:
> let myData = [Just 1, Nothing, Just 3]
> (+1) .: myData
[Just 2,Nothing,Just 4]
Возможности продолжаются и продолжаются. По сути, он позволяет отображать функцию внутри двух вложенных функторов, и такая структура возникает довольно часто. Если у вас когда-либо был список внутри Maybe
, или кортежи внутри списка, или IO
, возвращающий строку, или что-то в этом роде, вы сталкивались с ситуацией, когда вы могли бы использовать (.:) = fmap fmap fmap
.
person
bheklilr
schedule
03.09.2014