在Haskell

时间:2015-10-03 20:16:09

标签: haskell

我想创建一个实例声明,但free类型变量不是最后一个变量。例如,我有一个类声明

class Poppable m where
  tryPop :: m a -> Maybe (a, m a)

现在我想让Q.PSQ(优先级队列)成为Poppable的一个实例。具体来说,我想要这样的东西:

instance (Ord p) => Poppable (\a -> Q.PSQ a p) where
  tryPop = fmap (first Q.key) . Q.minView

但是,这不是合法的Haskell代码。如果切换到PSQ的参数顺序,那么我就没有问题了:

instance (Ord p) => Poppable (Q.PSQ p) where
  tryPop = fmap (first Q.key) . Q.minView

如何切换实例声明的参数顺序?

现在我可以使用newtype包装PSQ:

newtype PSQ'' a b = PSQ'' (Q.PSQ b a)

然而这对我来说似乎很笨拙,因为我必须不断地包装/打开它。有更简单的方法吗?

*

我尝试使用数据/类型系列,但都会出错。

(1)使用数据族声明:

data family PSQ' a b
data instance PSQ' a b = PSQ b a
instance (Ord p) => Poppable (PSQ' p) where
  tryPop = fmap (first Q.key) . Q.minView

然而,这会产生错误

Couldn't match type `Q.PSQ a p0' with `PSQ' p a'

即使他们可以通过设置p = p0来匹配。

(2)类型家庭也不会工作。

type family PSQ' a b where
  PSQ' b a = Q.PSQ a b

给出

Illegal type synonym family application in instance: PSQ' p

2 个答案:

答案 0 :(得分:8)

  

现在我可以使用newtype包装PSQ:

newtype PSQ'' a b = PSQ'' (Q.PSQ b a)
     

然而这对我来说似乎很笨拙,因为我必须不断地包装/打开它。有更简单的方法吗?

不,不是真的。当然,您可以编写Poppable课程,使其与PSQ匹配。如果您愿意,可以将您的新类型推广到

newtype Flip f a b = Flip (f b a)

此时你可以写

instance Poppable (Flip Q.PSQ a)

但这些都不会消除潜在的烦恼因素。有一些原因Haskell不支持这一点(显然,它使得推理更难,有时甚至不可能等),所以你只需要处理它。

P.S。,这种类型的同义词可能更有用,使用{-# LANGUAGE PolyKinds #-}进行多角化,最终意味着

newtype Flip (f :: k1 -> k2 -> *) (a :: k2) (b :: k1) = Flip (f b a)

答案 1 :(得分:2)

您可以简单地放弃参数化并使用类型族来投射与“元素类型”相对应的参数':

class Poppable q where
  type Elem q 
  tryPop :: q -> Maybe (Elem q, q)

data PSQ a p = ... 

instance Ord p => Poppable (PSQ a p) where 
  type Elem (PSQ a p) = a 
  tryPop = ...

请注意,这是一个更通用的表述,但可能更难以使用。