Haskell Applicative [], почему я не могу заменить pure[] на [] в функции?
ghci> :t pure []
pure [] :: Applicative f => f [a]
ghci> pure []
[]
ghci> :t []
[] :: [a]
ghci> fmap ((:) 2) (pure [])
[2]
ghci> fmap ((:) 2) ([])
[]
Я бы подумал заменить pure[]
с []
в fmap ((:) 2) (pure [])
приведет к тому же результату.. кто может объяснить мне разницу?
2 ответа
Тип pure
является Applicative f => a -> f a
так pure []
имеет тип Applicative f => f [a]
, Вы можете указать f
явно для разных аппликативных типов, например
pure [] :: Maybe [String] //Just []
pure [] :: IO [String] //displays '[]' in ghci
pure [] :: [[String]] //[[]]
Тип fmap ((:) 2) (pure [])
является (Applicative f, Num a) => f [a]
, Так как Ghci работает в монаде IO, которая является Applicative
выбирает f
быть IO
и отображает полученный список [2]
это то, что вы видите.
Вы можете указать аппликативный тип напрямую, чтобы увидеть другой результат, например
fmap ((:) 2) (pure []) :: (Maybe [Int])
который Just [2]
В fmap ((:) 2) ([])
, нет элементов в списке ввода, поэтому вы получите пустой список.
С чего бы это было так же? Это разные выражения.
Возможно, это упрощает вещи, если мы просто посмотрим на списки. Итак, есть пустой список []
, Его тип может быть любым, заключенным в квадратные скобки, в частности, вложенным списком:
Прелюдия> []:: [[Int]]
[]
Но есть и другой "пустой вложенный список":
Прелюдия> [[]]:: [[Int]]
[[]]
Хотя этот не совсем пустой, он содержит только пустой список. ([[],[],[],...]
также будет возможно). Фактически, это будет результатом того, что вы попробовали, если бы это было сделано в аппликативном списке:
Prelude Control.Applicative> pure []:: [[Int]]
[[]]
Это, конечно, не то же самое, что пустой список,
Прелюдия> [[]] == []
Ложь
Особенно,
Прелюдия> (длина [], длина [[]])
(0,1)
А также fmap
перебрасывать пустой список никогда ничего не делает, пока fmap
Пинг по пустому списку вызовет функцию с []
: простой способ увидеть это
Prelude Control.Applicative> fmap undefined []
[]
Prelude Control.Applicative> fmap undefined [[]]
[* Исключение: Prelude.undefined
Действительно запутанная вещь в ваших испытаниях заключается в том, что ghci молча использует IO
монада.
Есть также подкласс Applicative
для фактической пустоты:
Prelude Control.Applicative>: i Альтернатива
Класс Applicative f => Альтернатива f где
пусто:: fa
(< |>):: fa -> fa -> fa
некоторые:: f a -> f [a]
многие:: f a -> f [a]
- Определено в Control.Applicative
Экземпляр Alternative [] - определен в Control.Applicative
Экземпляр Альтернатива Возможно - Определено в Control.Applicative