Почему я получаю здесь ошибку «переменная неоднозначного типа»?

#haskell #functional-programming

#haskell #функциональное программирование

Вопрос:

 import Data.Monoid

times :: Monoid a => Int -> a -> a
times i = mconcat . replicate i

main =
  print $ times 5 5
  

Этот код выдает следующую ошибку:

 Ambiguous type variable `a0' in the constraints:
  (Monoid a0) arising from a use of `times'
              at :7:11-15
  (Show a0) arising from a use of `print'
            at :7:3-7
  (Num a0) arising from the literal `5'
           at :7:19
Probable fix: add a type signature that fixes these type variable(s)
In the second argument of `($)', namely `times 5 5'
In the expression: print $ times 5 5
In an equation for `main': main = print $ times 5 5
  

Почему это выдает эту ошибку? Как здесь Num вообще задействовано?

Ответ №1:

Проблема в том, что для чисел определены два моноида. Один с добавлением, а другой с умножением. Они реализованы как экземпляры для newtypes Sum и Product , и вы должны указать, какой из них вы хотите, поскольку для простых числовых типов нет экземпляров monoid.

 *Main> times 5 (Sum 5)
Sum {getSum = 25}
*Main> times 5 (Product 5)
Product {getProduct = 3125}
  

Num упоминается, потому 5 что является полиморфным значением:

 *Main> :t 5
5 :: Num a => a
  

Обычно это приводило бы к повсеместным ошибкам неоднозначного типа, если бы не тип по умолчанию, который заставляет компилятор перебирать набор типов по умолчанию (обычно Integer и Double ) и выбирать первый подходящий. Поскольку Integer ни Double один из них не имеет Monoid экземпляра, ошибка типа по умолчанию завершается ошибкой, и вы получаете ошибку неоднозначного типа.

Также возможно, что вы хотели использовать моноид списка, поскольку из вопроса неясно, какого результата вы ожидали.

 *Main> times 5 [5]
[5,5,5,5,5]
  

Комментарии:

1. Спасибо, Хаммар. Я внес несколько изменений в код, и теперь я получаю новую ошибку: paste.pocoo.org/show/486225 . (На самом деле это должен быть отдельный вопрос, и я бы создал новый поток, если вы попросите об этом.)

2. @missingfaktor: для этого экземпляра требуется UndecidableInstances расширение.

3. Я действительно ожидал Monoid экземпляр для Int , но не знал, что его нет.

4. @missingfaktor: В основном это связано с тем, что выбор экземпляра больше не гарантированно завершается, если тип не становится «меньше» на каждом шаге. Например, может быть instance Num a => Monoid a , или цикл, который проходит через несколько экземпляров. Это приведет к тому, что компилятор перейдет в бесконечный цикл. UndecidableInstances Расширение устраняет это требование, и теперь вам, программисту, не определять экземпляры, которые могут вызвать бесконечные циклы во время компиляции.

5. @missingfaktor Кроме того, этот экземпляр не выполняет то, что вы думаете. Если вы добавите этот экземпляр, вы не сможете использовать ни один из других экземпляров Monoid , которые уже были определены, поскольку этот экземпляр всегда совпадает.