Неоднозначная переменная типа `a0 'в ограничениях
Я пытаюсь пройти через YesNo
пример из Learn You a Haskell for Great Good!
книги.
Вот мой исходный код:
module Main where
main :: IO ()
main = putStrLn ( show (yesno 12) )
class YesNo a where
yesno :: a -> Bool
instance YesNo Bool where
yesno b = b
instance YesNo [a] where
yesno [] = False
yesno _ = True
instance YesNo Int where
yesno 0 = False
yesno _ = True
При выполнении этого кода происходит следующее исключение:
Ambiguous type variable `a0' in the constraints:
(YesNo a0) arising from a use of `yesno'
at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:25-29
(Num a0) arising from the literal `12'
at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:31-32
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `show', namely `(yesno 12)'
In the first argument of `putStrLn', namely `(show (yesno 12))'
In the expression: putStrLn (show (yesno 12))
Не могли бы вы объяснить, что не так с этим кодом?
Ответы
Ответ 1
Проблема в том, что он не знает, что такое тип 12! Это может быть любой тип с экземпляром Num:
GHCi> :t 12
12 :: Num a => a
Вам нужно указать тип, который вы хотите напрямую: try putStrLn (show (yesno (12 :: Int)))
.
Почему GHC не может выбрать Int, поскольку никакой другой выбор не будет работать, спросите вы? Хороший вопрос. Ответ заключается в том, что с помощью системы классов Haskell добавление экземпляра никогда не приведет к аннулированию существующих правильных программ или изменению их поведения. (Это называется предположением открытого мира.) Если он выбрал Int, то что произойдет, если вы добавите instance YesNo Integer
? Выбор станет двусмысленным, и ваша программа сломается!
Поэтому, когда вы хотите использовать класс типа, подобный этому, с полиморфным значением, вы должны указать, какой тип вы имеете в виду более точно. Это не должно возникать на практике, так как обычно существует некоторый окружающий контекст, чтобы заставить тип быть тем, что вы хотите; это в основном числовые литералы, на которые это влияет.
Ответ 2
Проблема заключается в том, что 12
имеет тип Num a => a
, а не Int
, как вы ожидаете. Если вы добавите явный тип anotation, такой как 12 :: Int
, он должен скомпилироваться.
Ответ 3
У меня была та же проблема.
Это решение, возможно, не самое лучшее, но оно работает:
class YesNo a where
yesno :: a -> Bool
instance YesNo Int where
yesno 0 = False
yesno _ = True
instance YesNo Integer where
yesno 0 = False
yesno _ = True