Компилятор не выбирает класс типов для значения полиморфной константы - PullRequest
1 голос
/ 19 марта 2019

Я новичок в Хаскеле, так что прости меня заранее.

Почему следующий код на haskell не компилируется?

Кажется, что компилятору почему-то не удается увидеть, что тип выражения (maxBound :: a) имеет значение a, для которого предоставлен экземпляр Enum, а не какой-то type variable ‘a0’, который равен ambiguous.

class (Enum a, Bounded a) => SafeEnum a where
  ssucc :: a -> a
  ssucc x = if (fromEnum x) < (fromEnum (maxBound :: a)) then succ x else minBound

  spred :: a -> a
  spred x = if (fromEnum x) > (fromEnum (minBound :: a)) then pred x else maxBound
Stepik.hs:3:32: error:
    • Could not deduce (Enum a0) arising from a use of ‘fromEnum’
      from the context: SafeEnum a
        bound by the class declaration for ‘SafeEnum’
        at Stepik.hs:(1,1)-(6,82)
      The type variable ‘a0’ is ambiguous
      These potential instances exist:
        instance Enum Ordering -- Defined in ‘GHC.Enum’
        instance Enum Integer -- Defined in ‘GHC.Enum’
        instance Enum () -- Defined in ‘GHC.Enum’
        ...plus six others

Ответы [ 2 ]

5 голосов
/ 19 марта 2019

По умолчанию, даже несмотря на то, что переменные типа ограничены областью определения от класса до сигнатур типов методов класса (т. Е. a в class SafeEnum a совпадает с a и a в ssucc :: a -> a) они не ограничены сигнатурами типов методов к телам методов, поэтому в выражении maxBound :: a в телах ваших функций ssucc и spred, a не имеет ничего общего с a в сигнатурах типов для этих функций.

Вы можете включить расширение ScopedTypeVariables, например:

{-# LANGUAGE ScopedTypeVariables #-}

, после чего определение класса будет проверять тип.

Обратите внимание, что это расширение применяется только к "нормальным" объявлениям функций, если вы используете ключевое слово forall. Поэтому, вне определения класса, вам нужно включить это расширение и write:

ssucc :: forall a. a -> a 
ssucc x = ... maxBound :: a ...

или на самом деле:

ssucc :: forall a. (Enum a, Bounded a) => a -> a
ssucc x = ... maxBound :: a ...

но правила внутри предложения class отличаются.

Подробнее см. документы GHC .

2 голосов
/ 19 марта 2019

Вам нужно добавить эту строку в начало вашего файла:

{-# LANGUAGE ScopedTypeVariables #-}

Если это расширение не включено, maxBound :: a не относится к тому же a, что и в классе.

По сути, в стандартном Haskell каждая сигнатура типа имеет свои собственные переменные типа, которые не зависят от любой другой переменной.Например, этот код

foo :: [a] -> Int
foo xs = length ys
   where
   ys :: [a]
   ys = xs

терпит неудачу , поскольку ys :: [a] действительно означает ys :: [b] с независимой переменной b, а ys = xs не создает [b].

При включенном расширении компилируется:

foo :: forall a . [a] -> Int
foo xs = length ys
   where
   ys :: [a]
   ys = xs

Возможно, должно быть другое значение по умолчанию, например, расширение должно быть включено по умолчанию.В качестве альтернативы, GHC должен намекнуть на включение расширения, когда один и тот же a используется дважды, так как часто это проблема.

...