haskell: создание суперкласса Num

Я хочу сделать суперкласс Num, называемый Linear

class Linear a where 
  add :: a -> a -> a

instance (Num a) => Linear a where
  add = (+)

Я получаю сообщение об ошибке:

Illegal instance declaration for `Linear a'
  (All instance types must be of the form (T a1 ... an)
   where a1 ... an are *distinct type variables*,
   and each type variable appears at most once in the instance head.
   Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Linear a'

из того, что я понимаю, что-то о линии instance (Num a) => Linear a where некорректно. (Он компилируется, если я использую флаги:-XFlexibleInstances -XUndecidableInstances)

есть ли способ достичь этого без использования этих страшных флагов? (и что в мире неразрешимо в коде выше??)

обновление: добавлен полиномиальный тип Линейный.

newtype Polynomial a = Polynomial (a,[a]) deriving Show-- list of coeffients 

instance (Linear a) => Linear (Polynomial a)
         where 
           add (Polynomial (c1, l1)) (Polynomial (c2, l2))
             = Polynomial (add c1 c2, zipWith (add) l1 l2)

p1 = Polynomial (0, [3,4,5])
p2 = Polynomial (0, [])

main = putStrLn $ show ((add p1 p2):: Polynomial Int)

после добавления полинома, он не компилируется даже с этими флагами и дает ошибку:

Overlapping instances for Linear (Polynomial Int)
  arising from a use of `add'
Matching instances:
  instance Num a => Linear a -- Defined at Algebra.hs:22:10-28
  instance Linear a => Linear (Polynomial a)
    -- Defined at Algebra.hs:25:10-44
In the first argument of `show', namely
  `((add p1 p2) :: Polynomial Int)'
In the second argument of `($)', namely
  `show ((add p1 p2) :: Polynomial Int)'
In the expression: putStrLn $ show ((add p1 p2) :: Polynomial Int)

2 ответов


языковой отчет не разрешает экземпляры формы instance Class a where..., Так что единственный способ избежать FlexibleInstances (что совсем не страшно) было бы использовать оболочку newtype,

newtype LinearType a = Linear a

liftLin2 :: (a -> b -> c) -> LinearType a -> LinearType b -> LinearType c
liftLin2 op (Linear x) (Linear y) = Linear (op x y)

instance Num a => Linear (LinearType a) where
    add = liftLin2 (+)

Фу.

на UndecidableInstances расширение необходимо, потому что ограничение Num a не меньше, чем головка экземпляра (она использует те же переменные типа столько же раз), поэтому компилятор не может заранее доказать, что проверка типа завершится. Таким образом, вы должны пообещать компилятор, который проверяет тип, завершится, чтобы он принял программу (на самом деле он не будет цикл с GHC, который имеет стек контекста, который управляет глубиной рекурсии проверки типа, поэтому, если проверка типа не завершится достаточно скоро, она завершит компиляцию с "превышением стека контекста"-вы можете установить размер с -fcontext-stack=N).

это расширение звучит намного страшнее, чем есть. В основном все, что он делает, это сказать компилятору "поверьте мне, проверка типа завершится", поэтому компилятор начнет, не зная наверняка, что он завершится.

но чего вы пытаетесь достичь? То, что у вас сейчас есть,

instance (Num a) => Linear a where
  add = (+)

говорит: "каждый тип является экземпляром Linear, и если вы пытаетесь использовать add в типе, а не экземпляре Num, это ошибка времени компиляции". Это не очень полезно. Вы не можете добавить дополнительные экземпляры для типов, не принадлежащих Num, если вы не включите также OverlappingInstances и возможно IncoherentInstances. И эти расширения are страшно, они должны использоваться редко и только тогда, когда вы знаете, что вы делаете.


есть предложение разрешить объявление суперклассов. AFAIK он еще не реализован, но поскольку GHC является открытым исходным кодом, вы можете изменить это, если хотите;)