问题描述
我有一个 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
推出自己的产品。实例将用于 M1
和 K1
。我的第一个想法是你应该在最外面(在你打开通用表示之前)使用 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