探索 typeclasses are essentially C++ abstract classes without nested inheritance 的想法,我已经写了类型类
class Interface i c where
i :: c -> i
instance Interface i i where i = id
infixl 1 #
(#) :: Interface i c => c -> (i -> r) -> r
c # f = f $ i c
使用类似的界面
data IDrawable' = IDrawable { draw :: IO () }
我想要类似的东西
type IDrawable c = Interface IDrawable' c
这样我就可以做到
data Object = Object { objectDraw :: IO () }
data Person = Person { personDraw :: IO () }
instance IDrawable Object where i = IDrawable . objectDraw
instance IDrawable Person where i = IDrawable . personDraw
而
type IDrawable c
用 ConstraintKinds
编译,我不允许做instance IDrawable Object where i = IDrawable . objectDraw
有错误'i' is not a (visible) method of class 'IDrawable`
有没有办法声明
IDrawable c = Interface IDrawable' c
以便它可以被实例化?这纯粹是出于学术兴趣,我不建议任何人在实际应用中使用这种模式,我只是想知道这种事情是否可以不应用
TemplateHaskell
或 CPP
.
最佳答案
不,这是不可能的(从 7.8.3 开始,我认为也是 7.10);它是 GHC bug #7543 .这不是一个非常容易被贩卖的错误。显然,至少有少数人希望能够写出这种东西(例如,你,Edward Kmett),但大多数人都没有注意到这一点。跟踪器上记录的更改此行为没有任何进展。
至于为什么不能,让我解释一下 Simon Peyton-Jones 对 bug 跟踪器的解释。问题是类型检查实例有两个部分:首先,GHC 必须查找方法名称(这里是 i
)的来源;其次,GHC 必须扩展类型同义词。由于这两个步骤在本期中由 GHC 的两个不同组件执行,因此不支持约束同义词实例; GHC 无法判断它需要查看哪个类才能找到 i
.
这是一个错误的另一个原因——也是我发现这个的原因,根据 András Kovács's answer 上的评论——是不是当前的行为不像“它不起作用”那么简单。相反,它会尝试工作,但你不能声明任何方法……但你可以声明一个无方法的实例!在 GHCi 中:
GHCi, version 7.8.3: http://www.haskell.org/ghc/ :? for help
...
Prelude> :set -XMultiParamTypeClasses -XFlexibleInstances -XConstraintKinds
Prelude> class Interface i c where i :: c -> i
Prelude> instance Interface i i where i = id
Prelude> let (#) :: Interface i c => c -> (i -> r) -> r ; c # f = f $ i c ; infixl 1 #
Prelude> data IDrawable' = IDrawable { draw :: IO () }
Prelude> type IDrawable = Interface IDrawable'
Prelude> instance IDrawable () where i _ = IDrawable $ return ()
<interactive>:8:29:
‘i’ is not a (visible) method of class ‘IDrawable’
Prelude> ()#draw
<interactive>:9:3:
No instance for (Interface IDrawable' ()) arising from a use of ‘#’
In the expression: () # draw
In an equation for ‘it’: it = () # draw
Prelude> instance IDrawable () where {}
<interactive>:10:10: Warning:
No explicit implementation for
‘i’
In the instance declaration for ‘Interface IDrawable' ()’
Prelude> ()#draw
*** Exception: <interactive>:10:10-21: No instance nor default method for class operation Ghci1.i
换句话说:
instance IDrawable () where i _ = IDrawable $ return ()
失败,但是
instance IDrawable () where {}
成功!很明显,检查需要放松或收紧,具体取决于所需的行为:-)
P.S.:还有一件事:你应该尽可能地 eta-reduce 类型同义词。这就是我更改
IDrawable
的原因至type IDrawable = Interface IDrawable'
并删除了
c
GHCi
中两边的参数上面的代码。这样做的好处是,由于不能部分应用类型同义词,因此您无法通过 IDrawable
的版本。作为任何东西的参数;然而,完全 eta 减少的版本可以通过任何期望的东西 * -> Constraint
.(这在 András Kovács's answer 中有所提及,我在评论中提到了这一点;不过,由于我最终也写了一个答案,我想我也会在这里添加它。)
关于haskell - 定义部分应用的类型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29039526/