6

我想创建一个 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)
4

2 回答 2

10

语言报告不允许 form 的实例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 的实例,如果您尝试在类型而不是 Num 的实例上使用 add,那将是编译时错误”。它不是很有用。您不能为不属于 的类型添加更多实例Num,除非您也启用OverlappingInstances并且可能启用IncoherentInstances。而且这些扩展可怕,它们应该很少使用,只有在你知道自己在做什么时才应该使用。

于 2012-03-26T11:19:49.120 回答
3

有一个提议允许声明超类。AFAIK 它还没有实现,但由于 GHC 是开源的,如果你愿意,你可以改变它;)

于 2012-03-26T11:48:00.737 回答