【问题标题】:haskell -- any way to generate "deriving" instances for roughly-tuple-isomorphic data types?haskell——有什么方法可以为大致元组同构数据类型生成“派生”实例?
【发布时间】:2012-03-11 23:21:23
【问题描述】:

假设我有一个类似的数据类型

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)

注意。例如,如果有data D m = D (m Integer) (m Integer),则使用新类型和GeneralizedNewtypeDeriving 将不起作用。

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

【问题讨论】:

    标签: haskell typeclass deriving


    【解决方案1】:

    您可以使用 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)
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    相关资源
    最近更新 更多