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)

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

UPDATE: добавлен тип полинома к линейному.

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, у которого есть стек контекста, который контролирует глубину рекурсии проверки типов, поэтому, если проверка типов не работает, t закончить достаточно скоро, это приведет к сбою компиляции с "превышением стека контекста" - вы можете установить размер с помощью -fcontext-stack=N).

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

Но чего вы пытаетесь достичь? Что у вас есть,

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

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


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

licensed under cc by-sa 3.0 with attribution.