tl; dr
просто всегда пишите явные подписи типов , тогда вы в безопасности (r) от подобных странных проблем.
Причина это раньше работало, но теперь не работает это то, что foldr
раньше имел подпись
foldr :: (a -> b -> b) -> b -> [a] -> b
, что предполагает WikiBooks, но в более новой GH C он действительно имеет строго более общая подпись
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Старая версия является частным случаем этого, просто выбирая t ~ []
. Причина, по которой они изменились, заключается в том, что вы также можете складывать другие контейнеры, такие как массивы или карты. Фактически, в вашем коде
echoes = foldr (\ x xs -> (replicate x x) ++ xs) []
нет ничего, что требовало бы, чтобы входной контейнер был списком, поэтому он фактически отлично работал бы с подписью
echoes :: Foldable t => t Int -> [Int]
. ..из которого, опять же, [Int] -> [Int]
является особым случаем, так что эту функцию можно затем использовать как
> echoes [1,2,3]
[1,2,2,3,3,3]
, но также как
> echoes $ Data.Map.fromList [('a',2), ('c',5), ('b',1)]
[2,2,1,5,5,5,5,5]
Или вы могли бы указать function the list-specifici c signature
echoes' :: [Int] -> [Int]
echoes' = foldr (\x xs -> (replicate x x) ++ xs) []
Это работает точно так же на [1,2,3]
, но не может принимать Map
.
Теперь вопрос, почему GH C сам по себе не выводит ни одну из этих подписей? Что ж, если бы ему пришлось выбрать один, это должна быть более общая версия Foldable
, потому что людям, возможно, потребуется использовать это с другими контейнерами и не захотеть повторять квантификатор Foldable t =>
. Однако это противоречит другому правилу Haskell, ограничению мономорфизма . Поскольку ваша реализация echoes
не явно принимает какие-либо параметры (она только делает это без указания точки), это постоянная аппликативная форма , а автономный CAF должен иметь monomorphi c, если явно не указано, что это polymorphi c. Таким образом, сообщение об ошибке, с которым вы столкнулись: GH C действительно хочет, чтобы это был monomorphi c, но в нем нет информации, которая ограничивает какой конкретный Foldable
контейнер для выбора.
Есть четыре способа обойти это:
Как вы заметили, явно вводя аргумент в область видимости, echoes
больше не является CAF, и, следовательно, GH C выводит полиморфизм c тип:
echoes'' l = foldr (\x xs -> (replicate x x) ++ xs) [] l
> :t echoes''<br>echoes'' :: Foldable t => t Int -> [Int]
Отключив ограничение мономорфизма, GH C больше не будет заботиться о том, является ли это CAF, и просто присвоит ему более общий тип независимо:
{-# LANGUAGE NoMonomorphismRestriction #-}
echoes''' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoes'''<br>echoes''' :: Foldable t => t Int -> [Int]
Не рекомендуется Если вы включите добавочный номер -XExtendedDefaultingRules
, GH C автоматически выберет []
как конкретный контейнер monomorphi c для CAF:
{-# LANGUAGE ExtendedDefaultRules #-}
echoes'''' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoes''''<br>echoes'''' :: [Int] -> [Int]
GHCi имеет -XExtendedDefaultingRules
по умолчанию, так что то же самое произойдет, если вы просто объявите функцию в приглашении GHCi.
Настоятельно рекомендуется I Если вы явно укажете подпись, вы и GH C точно знаете, что предназначено, и ведете себя соответственно, не требуя каких-либо специальных расширений GH C.
echoes :: Foldable t => t Int -> [Int]
echoes = foldr (\x xs -> (replicate x x) ++ xs) []
echoes' :: [Int] -> [Int]
echoes' = foldr (\x xs -> (replicate x x) ++ xs) []
> :t echoes<br>echoes :: Foldable t => t Int -> [Int]<br>> :t echoes'<br>echoes' :: [Int] -> [Int]