如何在SOP表示转换后从类型中获取数据类型信息?

时间:2017-07-02 15:01:11

标签: haskell generics

我正在尝试对数据类型名称进行浅层打印,但我不确定如何使用Generics-SOP执行此操作。

对于我有新类型列表的情况,我可以很容易地打印出我需要的内容:

class GTypeName f where
  gtypeName :: g f -> String

instance (HasDatatypeInfo a) => GTypeName (SOP I ('[ '[], '[a, [a]] ])) where
  gtypeName _ = "(Array " ++ name ++ ")"
    where
      name = datatypeName $ datatypeInfo (Proxy @ a)

正如您所看到的,我在List类型上匹配并使用内部a的数据类型信息来获取名称。但我不知道如何处理我想获得实际顶级构造函数名称的情况。

在GHC Generics中,我做了以下工作:

instance (GTypeName f) => GTypeName (D1 m f) where -- peer inside
  gtypeName _ = gtypeName (Proxy @ f)

-- go into the Cons constructed type
instance {-# OVERLAPPING #-} (GTypeName f) => GTypeName (C1 ('MetaCons ":" g s) f) where
  gtypeName _ = gtypeName (Proxy @ f)

-- return the constructor name here
instance (KnownSymbol n) => GTypeName (C1 ('MetaCons n g s) f) where
  gtypeName _ = symbolVal (Proxy @ n)

-- take the left side, as this is the result of a type product from Cons
instance (GTypeName a) => GTypeName (a :*: b) where
  gtypeName _ =
    gtypeName (Proxy @ a)

-- match on array and take it out here
instance (GTypeName b) => GTypeName ((C1 ('MetaCons "[]" g s) f) :+: b) where
  gtypeName _ = "(Array " ++ gtypeName (Proxy @ b) ++ ")"

这最终用于newtype和一些数据类型,如:

newtype Status = Status Text

newtype OpenRequest = OpenRequest
  { path :: Path
  }

data Route req res = Route
  { method :: Method
  , url :: Url
  }

open :: Route OpenRequest Success
open = Route {method = POST, url = Url "/api/open"}

1 个答案:

答案 0 :(得分:1)

您可以使用generics-sop获取值的顶级构造函数名称,如下所示:

constructor ::
  forall a .
  (Generic a, HasDatatypeInfo a) => a -> String
constructor a =
  hcollapse $
  hzipWith
    (\ con _args -> K (constructorName con))
    (constructorInfo (datatypeInfo (Proxy @a)))
    (unSOP $ from a)

在这里,constructorInfo ...为您提供了一个产品,其中包含所讨论数据类型的所有构造函数名称。 hzipWith调用然后选择给定值a所属的构造函数。

示例:

GHCi> constructor True
"True"
GHCi> constructor (Just 3)
"Just"
GHCi> constructor [1,2,3]
":"

不幸的是,我不清楚你想要对列表类型做什么,所以我无法展示如何将它与你已有的代码结合起来。