定义部分应用的类型类 [英] Defining partially applied typeclasses

查看:102
本文介绍了定义部分应用的类型类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

探索类型实质上是没有嵌套继承的C ++抽象类的想法,我写了类型类

  class接口ic其中
i :: c - > i

实例接口i我在哪里i = id

infixl 1#
(#):: Interface i c => c - > (i→r)→> r
c#f = f $ ic

使用像

  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 =可绘制。 objectDraw
实例IDrawable Person其中i = IDrawable。 personDraw

尽管 type IDrawable c ConstraintKinds ,我不允许做实例IDrawable对象,其中i = IDrawable。 objectDraw 与错误

 'i'不是类'IDrawable`的(可见)方法

有没有办法来声明 IDrawable c = Interface IDrawable'c 以便它可以实例化吗?

这纯粹是出于学术兴趣,我不推荐任何人在实际应用程序中使用这种模式,我只是想知道这种事情是可能的,而不应用 TemplateHaskell CPP

$ b $不,这是不可能的(从7.8.3开始,我也认为是7.10);它的 GHC bug#7543 。这不是一个非常贩卖的bug;显然至少有一些人希望能够写出这样的东西(例如,你,爱德华Kmett),但大多数人不会注意到这一点。在改变跟踪器上记录的行为方面没有任何进展。



至于你为什么做不到,请让我解释一下Simon Peyton-Jones对bug跟踪器的解释。问题是类型检查实例有两个部分:首先,GHC必须查找方法名称(这里 i )来自哪里;其次,GHC必须扩展类型同义词。由于这两个步骤是由GHC的两个不同组件执行的,因此不支持约束同义词实例; GHC无法知道需要查找什么类才能找到 i






另一个原因是这是一个错误 - 我发现这个错误的原因是,根据AndrásKovács的回答< a> - 是目前的行为不像它不工作那么简单。相反,它试图来工作,但是你不能声明任何方法......但是你可以声明一个无方法的实例!在GHCi中:

  GHCi,版本7.8.3:http://www.haskell.org/ghc/:?寻求帮助
...
Prelude> :set -XMultiParamTypeClasses -XFlexibleInstances -XConstraintKinds
Prelude> class Interface i c where i :: c - >我
前奏>实例接口我我在哪里我= id
前奏> let(#):: Interface i c => c - > (i→r)→> r; c#f = f $ i c; infixl 1#
Prelude> data IDrawable'= IDrawable {draw :: IO()}
Prelude>类型IDrawable =接口IDrawable'
前奏>实例IDrawable()其中i _ = IDrawable $ return()

< interactive>:8:29:
'i'不是类'IDrawable'的(可见)方法
前奏> ()#draw

< interactive>:9:3:
由于使用'#'
而没有(Interface IDrawable' :()#draw
在'it'的等式中:it =()#draw
Prelude>


< interactive>:10:10:警告:
没有显式实现
'i'
在实例声明中'Interface IDrawable'()'
Prelude> ()#draw
***异常:< interactive>:10:10-21:没有实例或类操作的默认方法Ghci1.i

 实例IDrawable()其中i _ = {$ p 

IDrawable $ return()

失败,但

  instance IDrawable()where {} 

成功!很明显,检查需要放松或收紧,具体取决于所需的行为: - )




PS:一更多的事情:你应该总是尽可能地减少类型同义词。这就是为什么我将 IDrawable 更改为

  type IDrawable = Interface IDrawable' 

并在两边放置 c 参数在上面的 GHCi 代码中。这样做的好处是,由于类型同义词不能被部分应用,所以你不能将你的版本的 IDrawable 作为参数传递给任何东西;然而,完全eta简化版本可以传递到任何需要类型的地方 * - >约束



(这在 AndrásKovács的回答,我在那里发表了评论;尽管如此,由于我最终也写了一个答案,所以我想我也会在这里添加它。)


Exploring the idea that typeclasses are essentially C++ abstract classes without nested inheritance, I have written the typeclass

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

With an interface like

data IDrawable' = IDrawable { draw :: IO () }

I'd like to have something like

type IDrawable c = Interface IDrawable' c

So that I could do

data Object = Object { objectDraw :: IO () }
data Person = Person { personDraw :: IO () }

instance IDrawable Object where i = IDrawable . objectDraw
instance IDrawable Person where i = IDrawable . personDraw

While the type IDrawable c compiles with ConstraintKinds, I'm not allowed to do instance IDrawable Object where i = IDrawable . objectDraw with the error

'i' is not a (visible) method of class 'IDrawable`

Is there a way to declare IDrawable c = Interface IDrawable' c so that it can be instanced?

This is purely out of academic interest, I'm not recommending anyone use this pattern in a real application, I just want to know if this sort of thing is possible without applying TemplateHaskell or CPP.

解决方案

No, this isn't possible (as of 7.8.3, and I think also 7.10); it's GHC bug #7543. It's not a very trafficked bug; there are clearly at least a few people who'd like to be able to write this sort of thing (e.g., you, Edward Kmett), but mostly this goes unnoticed. There's no progress on changing this behavior recorded on the tracker.

As for why you can't, let me paraphrase Simon Peyton-Jones's explanation on the bug tracker. The problem is that type checking instances has two parts: first, GHC has to look up where the method names (here, i) are from; second, GHC has to expand the type synonyms. Because these two steps are performed in this issue by two different components of GHC, constraint synonym instances can't be supported; GHC can't tell what class it needs to look in to find i.


The other reason this is a bug – and the reason I found this, as per the comments on András Kovács's answer – is that the current behavior isn't as simple as "it doesn't work". Instead, it tries to work, but you can't declare any methods… but you can declare a methodless instance! In 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

In other words:

instance IDrawable () where i _ = IDrawable $ return ()

fails, but

instance IDrawable () where {}

succeeds! So clearly, the check needs to either be loosened or tightened, depending on the desired behavior :-)


P.S.: One more thing: You should always eta-reduce type synonyms as much as possible. This is why I changed IDrawable to

type IDrawable = Interface IDrawable'

and dropped the c parameter on both sides in the GHCi code above. The advantage of this is that since type synonyms can't be partially applied, you can't pass your version of IDrawable as a parameter to anything; however, the fully eta-reduced version can be passed anywhere expecting something of kind * -> Constraint.

(This is touched on in András Kovács's answer, and I mentioned this in a comment there; nevertheless, since I ended up writing an answer too, I figured I'd add it here as well.)

这篇关于定义部分应用的类型类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆