使用泛型生成关于 newtype

问题描述

我有一个 Haskell 项目,它使用了几个 newtypes。 我想导出这些表格,因此我可以将它包含在我的文档(非黑线鳕)中,例如作为降价表。我对此并不熟悉,但通过阅读,我的计划是使用 Generics/Data.Data 创建一些大致如下所示的函数

data MyRowRepresentation = MyRowRepresentation
                                String -- Name of the newtype
                                String -- Name of the type its wrapping

todocRow :: (Generic a,HasDatatypeInfo a) -> (Proxy a) -> MyRowRepresentation
todocRow = <the part I'm struggling with>

newtype MyType0 = MyType0 Int16
newtype MyType1 = MyType0 Int8
newtype MyType2 = MyType0 Int32
newtype MyType3 = MyType0 Word8

main = do
  let table = [
        todocRow (Proxy::MyType0),todocRow (Proxy::MyType1),todocRow (Proxy::MyType2),todocRow (Proxy::MyType3),]
  -- Do something to write `table` to disk.

但是,我很难理解 Generics/Data.Data 如何与构造函数交互。这是我第一次使用 Haskell 区域,所以我尝试了以下代码,只是为了看看它是如何工作的:

{-# LANGUAGE DeriveGeneric #-}
module TmpTmpExport where

import RIO
import RIO.List.Partial (head)
import Text.Show.Pretty
import Data.String.Conversions    (cs)
import Data.Data


newtype MyNewType = MyNewType Integer
  deriving (Show)
  deriving (Data,Typeable)

main :: IO ()
main = do
  traceM $ cs $ "INSTANCE: "            ++ (show $ dataTypeConstrs $ dataTypeOf $ (MyNewType 0))
  traceM $ cs $ "INSTANCE constrType: " ++ (show $ constrType $ head $ dataTypeConstrs $ dataTypeOf $ (MyNewType 0))
  traceM $ cs $ "INSTANCE constrType: " ++ (show $ dataTypeConstrs $ constrType $ head $ dataTypeConstrs $ dataTypeOf $ (MyNewType 0))
  traceM $ cs $ "INSTANCE constrType: " ++ (show $ (fmap showConstr) $ dataTypeConstrs $ constrType $ head $ dataTypeConstrs $ dataTypeOf $ (MyNewType 0))
  traceM $ cs $ "INSTANCE constrType: " ++ (show $ dataTypeConstrs $ constrType $ head $ dataTypeConstrs $ constrType $ head $ dataTypeConstrs $ constrType $ head $ dataTypeConstrs $ dataTypeOf $ (MyNewType 0))



  return ()

给出以下输出

INSTANCE: [MyNewType]
INSTANCE constrType: DataType {tycon = "MyNewType",datarep = AlgRep [MyNewType]}
INSTANCE constrType: [MyNewType]
INSTANCE constrType: ["MyNewType"]
INSTANCE constrType: [MyNewType]

我不认为我在一百万英里之外,但是通过阅读 Data.Data 的文档并尝试各种功能,我无法弄清楚如何使用 {“进入”新类型{1}} 和 dataTypeConstrs,这样我就可以创建 constrType,其中的行如下:

table

我是否在概念上遗漏了 table = [ (MyRowRepresentation "Type0" "Int16"),(MyRowRepresentation "Type1" "Int8") (MyRowRepresentation "Type2" "Int32") (MyRowRepresentation "Type3" "Word8") ] newtype间的区别,这可能与 data 有关,还是我应该查看不同的工具/库?

>

解决方法

您绝对可以使用 GHC.Generics 做到这一点。我不太熟悉 Data.Data

简单的方法,使用 coercible-utils >= 0.1.0

import CoercibleUtils.Newtype

toDocRow :: forall a o proxy. (Newtype a o,Typeable a,Typeable o) => proxy a -> MyRowRepresentation
toDocRow _ = MyRowRepresentation
  (show (typeRep (Proxy :: Proxy a)))
  (show (typeRep (Proxy :: Proxy o)))

这个版本坚持实际的新类型,而不是单一构造函数的数据类型。


以下是您如何使用 Generic 推出自己的产品。实例将用于 M1K1。我的第一个想法是你应该在最外面(在你打开通用表示之前)使用 Typeable 来获取 newtype 名称,然后使用一个无聊的实例来忽略 M1 层,然后再次使用 Typeable为 K1 获取包装类型信息。在外部,您可以改为使用附加到最外层 M1 的元数据,但这似乎更尴尬。

请注意,可以使用 TypeApplications 而不是代理传递来清理以下内容。

{-# language DeriveGeneric,FlexibleContexts,ScopedTypeVariables,KindSignatures #-}
import GHC.Generics
import Data.Typeable
import Data.Int
import Data.Word
import Data.Kind (Type)

data MyRowRepresentation = MyRowRepresentation
  String -- Name of the newtype
  String -- Name of the type its wrapping
  deriving Show

toDocRow :: forall a proxy. (Generic a,Grump (Rep a)) => proxy a -> MyRowRepresentation
toDocRow _ = MyRowRepresentation
  (show (typeRep (Proxy :: Proxy a)))
  (show $ grump (Proxy :: Proxy (Rep a)))

class Grump (f :: Type -> Type) where
  grump :: Proxy f -> TypeRep

instance Grump f => Grump (M1 i c f) where
  grump _ = grump (Proxy :: Proxy f)

instance Typeable c => Grump (K1 i c) where
  grump _ = typeRep (Proxy :: Proxy c)

newtype MyType0 = MyType0 Int16
  deriving Generic
newtype MyType1 = MyType1 Int8
  deriving Generic
newtype MyType2 = MyType2 Int32
  deriving Generic
newtype MyType3 = MyType3 Word8
  deriving Generic

main = do
  let table = [
        toDocRow (Proxy:: Proxy MyType0),toDocRow (Proxy:: Proxy MyType1),toDocRow (Proxy:: Proxy MyType2),toDocRow (Proxy:: Proxy MyType3)
        ]
  print table