#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
, которые уже были определены, поскольку этот экземпляр всегда совпадает.