Пытаюсь понять, почему эта функция с использованием foldr в Haskell не работает - PullRequest
6 голосов
/ 07 августа 2020

Итак, я новичок в Haskell и изучаю его с помощью WikiBooks. И в главе о функциях более высокого порядка используется следующий пример.

echoes = foldr (\ x xs -> (replicate x x) ++ xs) []

Я попытался запустить его, но он выдает следующую ошибку:

 * Ambiguous type variable `t0' arising from a use of `foldr'
  prevents the constraint `(Foldable t0)' from being solved.
  Relevant bindings include
    echoes :: t0 Int -> [Int] (bound at HavingFun.hs:107:1)
  Probable fix: use a type annotation to specify what `t0' should be.
  These potential instances exist:
    instance Foldable (Either a) -- Defined in `Data.Foldable'
    instance Foldable Maybe -- Defined in `Data.Foldable'
    instance Foldable ((,) a) -- Defined in `Data.Foldable'
    ...plus one other
    ...plus 29 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
* In the expression: foldr (\ x xs -> (replicate x x) ++ xs) []
  In an equation for `echoes':
      echoes = foldr (\ x xs -> (replicate x x) ++ xs) []

А затем если я напишу это следующим образом, это сработает.

echoes lis = foldr (\ x xs -> (replicate x x) ++ xs) [] lis

Я сбит с толку и думаю, что это как-то связано с определениями функций без точек? Пожалуйста, поясните, в чем проблема. Ссылка по которой учусь - https://en.wikibooks.org/wiki/Haskell/Lists_III

1 Ответ

6 голосов
/ 07 августа 2020

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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...