编写一个类型级函数,用于GHC.Generics

时间:2015-07-16 06:08:23

标签: haskell generics dependent-type

我正在尝试使用GHC.Generics编写一个泛型函数,该函数将返回值中使用的所有数据类型名称。

这是我到目前为止所做的:

{-# LANGUAGE DefaultSignatures, DeriveGeneric, TypeOperators, FlexibleContexts #-}
{-# LANGUAGE MonomorphismRestriction #-}

module Lib4 where

import GHC.Generics

class Names f where
  names' :: f a -> [String]

instance Names U1 where
  names' _ = []

instance (Names a, Names b) => Names (a :+: b) where
  names' (L1 x) = names' x
  names' (R1 x) = names' x

instance (Names a, Names b) => Names (a :*: b) where
  names' (a :*: b) = names' a ++ names' b

instance Names (M1 i c a) where
  names' (M1 x) = ...  -- use datatypeName here?

names x = names' (from x)

缺少的唯一部分是M1 i c a实例定义。

如何调用datatypeName来获取该类型的名称?

我正在跟随Stephan Diehl的“我希望我知道什么......”关于泛型的博文[1]

[1] http://dev.stephendiehl.com/hask/#generic

1 个答案:

答案 0 :(得分:3)

首先提示,如果您还不知道这一点:您可以在GHCi中按Rep查看类型:kind! - s。例如:

> :kind! Rep [Int]
Rep [Int] :: * -> *
= D1
    GHC.Generics.D1[]
    (C1 GHC.Generics.C1_0[] U1
     :+: C1
           GHC.Generics.C1_1[]
           (S1 NoSelector (Rec0 Int) :*: S1 NoSelector (Rec0 [Int])))

关于实际问题,对于当前作业datatypeName不适用,我们可以使用typeOf中的Data.Typeable来恢复字段类型。

{-# LANGUAGE MonomorphismRestriction #-}

import GHC.Generics
import Data.Typeable

class Names f where
  names' :: f a -> [TypeRep]

instance Names U1 where
  names' _ = []

instance (Names a, Names b) => Names (a :+: b) where
  names' (L1 x) = names' x
  names' (R1 x) = names' x

instance (Names a, Names b) => Names (a :*: b) where
  names' (a :*: b) = names' a ++ names' b

instance Names f => Names (M1 i c f) where
  names' (M1 fa) = names' fa

instance (Typeable t) => Names (Rec0 t) where
  names' (K1 x) = [typeOf x]

names x = names' (from x)  

示例:

> data Foo = Foo Int Bool () deriving (Generic)
> names $ Foo 0 True ()
[Int,Bool,()]

请注意,此实现不是递归的,它只是查看最顶层构造函数的字段。

> names [0, 1]
[Integer, [Integer]]

递归版本将涉及更多的机制,因为某些类型的表示很容易导致天真实现中的无限循环,因此我们必须跟踪访问的字段。