我想与一些远程系统交换数据(可以通过HTTP、数据库、文件等),因此创建了数据传输对象(DTOs)--基本上,这些记录可以很容易地被序列化和反序列化。第二步,我想把这些DTO映射到我的域对象上。
所有的DTO都有一个ID字段(例如,一个技术主键、一个消息标识符或一个文件名)。因为这是一种通用模式,所以我尝试用一种通用类型来捕获它。这是我的第一次尝试。
data DtoShell = DtoShell
{ id :: UUID
, dto :: Dto}
data Dto
= MyDto1 ConcreteDto1
| MyDto2 ConcreteDto2
| ...
这样一来,那些处理任意DTO的函数 只需要ID,就可以处理这些东西 DtoShell
但不需要关心里面的实际数据。具体的DTO所特有的函数可以与之配合。在这两者之间,必须有一些 "调度器 "函数,在具体类型上进行模式匹配,并选择合适的函数--例如,将一个具体的DTO映射到其对应的域类型。
然而,这个方案很难扩展--如果有一个新的 ConcreteDto3
,我需要修改上面的代码,我需要修改 "dispatcher "函数。
我确实看到,这似乎是一些更基本的问题的表示(表达式问题?我不是CS专业的...)。在我的搜索中,我遇到了潜在的解决方案。幻象类型, 存在类型, 还有, 最重要的, 类型类. 但我对 Haskell 和一般抽象 CS 还不够精通,无法评估每种方法的利弊。因此,我有两个问题。
为什么不直接创建一个参数化的多态包络呢?
data Envelope a = Envelope { eid :: UUID, edata :: a } deriving (Eq, Show, Functor)
这基本上只是一个专门的对(二元组),所以你要让它成为所有类型类的一个实例。(,)
是一个实例。在这里,我把它变成了一个 Functor
实例,通过使用 DeriveFunctor
语言扩展。