Подтвердить что ты не робот

Неоднозначная переменная типа `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))

Не могли бы вы объяснить, что не так с этим кодом?

4b9b3361

Ответ 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