haskell - 为粗略 - 元组 - 同构数据类型生成“派生”实例的任何方法吗?

时间:2012-03-11 23:21:24

标签: haskell typeclass deriving

假设我有一个类似

的数据类型
data D a = D a a a

和类型类

class C c ...
instance (C c1, C c2) => C (c1, c2)

然后,我希望能够写

data D a = D a a a deriving C

并生成一个实例,

instance C ((a, a), a) => C (D a)

使用模 - 懒 - 评估同构,

D a ~ ((a, a), a)

注意的。例如,如果某个人具有GeneralizedNewtypeDeriving,那么使用新类型和data D m = D (m Integer) (m Integer)将无效。

注2 。这个问题一般与Haskell表达有关 - 像Python这样的语言有一个名为元组的东西,它可以在任何使用元组的地方使用;这个问题显示我在哪里/如何不知道如何在Haskell中模拟同样的事情。

1 个答案:

答案 0 :(得分:14)

使用GHC 7.4 generic programming support,您可以相对干净,高效地完成此任务。 documentation for GHC.Generics可能会有所帮助。这是一个例子。

考虑以下示例类和一些示例实例:

class C a where
  -- | Double all numbers
  double :: a -> a

instance C Int where
  double i = 2 * i

instance (C a, C b) => C (a, b) where
  double (a, b) = (double a, double b)

我们需要一些语言编译指示和导入:

{-# LANGUAGE TypeOperators, DefaultSignatures, DeriveGeneric, FlexibleContexts, FlexibleInstances #-}
module Example where

import GHC.Generics hiding(C, D)

我们现在给出一些“通用实例”。泛型类型都有一个幻像参数x,这使得实例头部更加复杂:

-- "Insert" a normal value into a generic value
instance C c => C (K1 i c x) where
  double (K1 c) = K1 (double c)

-- Ignore meta-information (constructor names, type names, field names)
instance C (f x) => C (M1 i c f x) where
  double (M1 f) = M1 (double f)

-- Tuple-like instance
instance (C (f x), C (g x)) => C ((f :*: g) x) where
  double (f :*: g) = double f :*: double g

我们现在重新定义我们的课程C以利用GC

class C a where
  -- | Double all numbers
  double :: a -> a

  -- specify the default implementation for double
  default double :: (Generic a, C (Rep a ())) => a -> a
  double = to0 . double . from0

-- from, with a more specialised type, to avoid ambiguity
from0 :: Generic a => a -> Rep a ()
from0 = from

-- to, with a more specialised type, to avoid ambiguity
to0 :: Generic a => Rep a () -> a
to0 = to

现在我们可以非常轻松地定义一些实例:

data D a = D a a a deriving Generic
instance C a => C (D a)

data D2 m = D2 (m Int) (m Int) deriving Generic
instance C (D2 D)