haskell: создание суперкласса Num - PullRequest
6 голосов
/ 26 марта 2012

Я хочу сделать суперкласс 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 ]

10 голосов
/ 26 марта 2012

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

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.И эти расширения страшны, их следует использовать редко и только тогда, когда вы знаете, что делаете.

3 голосов
/ 26 марта 2012

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

...