【问题标题】:haskell : making a superclass of Numhaskell:创建 Num 的超类
【发布时间】:2012-03-26 19:48:00
【问题描述】:

我想做一个 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)

【问题讨论】:

  • 你能指出为什么需要它们吗?不确定性是可怕的:)
  • 在 Haskell 中,很多东西都有可怕的名字,在其他语言中没有人会担心一秒钟。

标签: haskell typeclass superclass decidable


【解决方案1】:

语言报告不允许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 循环,它具有控制类型检查器的递归深度的上下文堆栈,所以如果类型检查没有t 很快就完成了,它会因为“超出上下文堆栈”而导致编译失败 - 你可以使用 -fcontext-stack=N 设置大小。

这个扩展听起来比实际要可怕得多。基本上它所做的只是告诉编译器“相信我,类型检查将终止”,因此编译器将在不知道它是否会完成的情况下启动。

但是,你想达到什么目的?你目前拥有的,

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

说“每种类型都是 Linear 的一个实例,如果你尝试在一个不是 Num 实例的类型上使用 add,那就是编译时错误”。它不是很有用。您不能为不属于Num 的类型添加更多实例,除非您还启用了OverlappingInstances 和可能的IncoherentInstances。而且这些扩展可怕的,它们应该很少使用,只有在你知道自己在做什么的时候才使用。

【讨论】:

  • 感谢您对此的解释,但是否有一种标准/默认方式可以制作不需要用户向编译器提供任何保证的超类?
  • 我想做一些 Linear 的实例,并且我希望 Num 的所有内容也都是 Linear。 (即 Linear 是 Num 的超类,并且比 Num 有更多的实例)
  • 我开始怀疑了。不过,这不是通常所说的超类。但是,这会将您带入OverlappingInstances 领域,这不是很舒服。对于Polynomial,拥有instance Num a => Num (Polynomial a) where... 是有意义的。 abssignum 会有点可疑,但 Num 中的其他所有内容都对多项式具有直接意义。
  • 这意味着 Linear 类型的所有东西都必须有 Num 的实例定义,但定义很荒谬——这不是一个好的解决方法!!
  • 好吧,实例解析(至少在 GHC 中)只考虑了“=>”之后的部分,所以instance (Context a) => Foo a 确实说每种类型都是Foo 的实例。只有在使用它时,编译器才会尝试查找 Context a 实例。这是不直观的,但有充分的理由。
【解决方案2】:

有一个proposal 允许声明超类。 AFAIK 它尚未实现,但由于 GHC 是开源的,您可以根据需要更改它;)

【讨论】:

  • 您能否同时提出一个解决方法(这不涉及手动将 Num 类型的所有内容(即 Int、Float 等)都设为 Linear 的实例)
  • 问题是,你真正想做什么。根据您的问题,您的问题可能有不同的解决方案。
猜你喜欢
  • 1970-01-01
  • 2018-05-14
  • 1970-01-01
  • 1970-01-01
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
相关资源
最近更新 更多