为Haskell中的类型派生自定义类的实例
我正试图减少副驾驶的样板文件数量 在最新版本中,我们添加了结构,使用它们需要声明两个实例。下面是一个例子 对于数据类型:为Haskell中的类型派生自定义类的实例,haskell,generic-programming,Haskell,Generic Programming,我正试图减少副驾驶的样板文件数量 在最新版本中,我们添加了结构,使用它们需要声明两个实例。下面是一个例子 对于数据类型: data TestStruct = TestStruct { i :: Field "i" Int8 } 我们需要以下实例: instance Struct TestStruct where typename _ = "teststruct" toValues t = [ Value Int8 (i t) ] instance Typed TestStru
data TestStruct = TestStruct
{ i :: Field "i" Int8
}
我们需要以下实例:
instance Struct TestStruct where
typename _ = "teststruct"
toValues t = [ Value Int8 (i t) ]
instance Typed TestStruct where
typeOf = Struct (TestStruct { i = Field 0 })
我希望通过自动生成实例使其更易于使用
我一直在研究泛型,以前也使用过模板Haskell,但在过去几年中情况发生了很大变化。许多软件包和推荐的解决方案现在都过时了,我花了几个月的时间浏览过去10年的论文和图书馆,后来才发现它们不再使用了。我很难理解编译器已经可以自动完成什么,我应该使用什么泛型扩展或包(如果有的话),并且通常很难找到一种最新的方法,这种方法可以工作并将继续工作
自动为这些类生成实例的最佳方法是什么?(如果可能的话,我想避免使用TH,但我知道从今天起可能不可能实现。)在大多数情况下,只需咨询 @Jon Purdy在评论中描述的方法是有价值的,我的回答基于此 派生
类型化
足够简单-已经存在为基类型和泛型类型生成默认值的实现。看起来很合适,但这不是一个建议:我只是选择了第一个Hoogle结果之一
defaulttypeof::(可键入a、结构a、默认a)=>键入a
typeOf=Struct def
然后就可以派生出默认值
和类型
,以获得良好的实现
上述要求为字段
提供一个默认
实例,但这几乎是显而易见的
实例(默认值f)=>默认值(字段nf),其中
def=字段def
派生Struct
这是一个更棘手的问题,因为它需要触及泛型的本质。然而,看看Copilot的具体用例,我们似乎可以忽略很多可能的事情(比如多个构造函数等等)
这个答案的较长形式还可以说,如果有必要,可以为D1
和C1
等对象提供递归实例
派生typename
不需要-只需使用。如果您确实想要,请定义一个类
class TypeName'f其中
typename'::f p->String
但是对于D1
,它只需要一个实例
派生toValues
这将需要更多的时间。定义一个类
class ToValues'a f其中
toValues':代理a->f p->[值a]
a
是一个类参数,因为它在机械上必须是类参数,Proxy
是一个函数参数,因为否则GHC会抱怨a
没有出现,并且没有资金来帮助解决该问题
通过组合多个字段的产品表示法来表示多个字段。这意味着单例字段S1
需要有一个实例,两个子实例的乘积需要有一个实例
定义实例并不难
实例(键入c,知道符号字段名)=>ToValues'a(s1m(Rec0(字段名c)),其中
toValues'p(M1(K1字段))=[值(typeOf@c)字段]
上面的实例为每个字段提供了一个单例,产品可以执行串联
instance(ToValues'al,ToValues'ar)=>ToValues'a(l:::r)其中
toValues'p(l::::r)=toValues'pl++toValues'pr
实例
我在这里使用类型统一~
进行类型级模式匹配
defaulttypename::(泛型a,repa~D1('MetaData typename m p nt)f),KnownSymbol typename=>a->String
typename=toLower(symbolVal(Proxy@typename))
默认toValues::(通用a,代表a~D1 m1(C1 m2 f),toValues'a f=>a->[值a]
toValues a=toValues'(代理@a)字段
其中(M1(M1字段))=(从a开始)
toLower
只是一个String
-小写函数,尽管您必须定义它symbolVal
给出类型级别Symbol
的字符串
toValues
代码通过将repa
约束为D1(C1)
的形式,假设存在单个数据构造函数
和以前一样,现在可以只导出泛型
和结构
来获得这个有用的实例。我没有时间详细说明这个问题,但是我在这里通常使用的技术是使用Generic
中的default
方法编写Struct
和Typed
,这允许使用DeriveGeneric
+DeriveAnyClass
编写派生(Generic,Struct,Typed)
(或派生股票(Generic)
+使用显式派生策略派生任何类(结构,类型)
)