我假设可以使用类型算术解决以下问题,但尚未找到解决方案。
问题
我有一个从字符串到值的有限映射(使用 Try 作为实现(,我从二进制/文本文件(json、xml、...(解析。
type Value = ...
type Attributes = Data.Trie Value
data Object = Object Attributes
每个映射具有相同类型的值,但具有不同的键集。我将具有同一组键的地图分组在一起,以便能够防止一直进行键入切换,我有一个需要某些键的专用功能:
data T1
data T2
...
data Object a where
T1 :: Attributes -> Object T1
T2 :: Attributes -> Object T2
...
这允许我写类似的东西:
f1 :: Object T1 -> ...
而不是
f1 :: Object ->
f1 o | check_if_T1 o = ...
这有效,但有两个缺点:
- 对象的同构列表现在变成了异构的,即我不能再有列表[对象]了。
我需要编写很多样板来获取/设置属性:
get :: Object a -> Attributes get (T1 a) = a get (T2 a) = a ...
问题
- 有没有更好的方法来根据 ADT 的构造函数来专门化函数?
如何重新获得拥有列表 [对象] 的能力?是否有只允许某些类型的专用动态版本?我想过再次包装对象,但这会增加很多样板。例如
数据 = TT1 T1 |TT2 T2 ...
我需要的是:
get :: a -> TObject -> Object a
这样我就可以推导出:
collect :: a -> [TObject] -> [Object a]
我研究了HList,但我认为它不适合我的问题。特别是,因为 [Object] 中的类型顺序在编译时是未知的。
在我看来,这可以使用函数依赖/类型算术来解决,但我还没有找到一种好方法。
-
如果所有构造函数都返回单态
Object
类型并且没有递归,则可能需要考虑仅使用单独的类型。而不是data T1 data T2 data Object a where T1 :: Attributes -> Object T1 T2 :: Attributes -> Object T2
考虑
data T1 = T1 Attributes data T2 = T2 Attributes
-
Dynamic
是一种方式,使用上述方法,您只需添加deriving Typeable
即可完成。或者,您可以手动完成:data TSomething = It's1 T1 | It's2 T2 getT1s :: [TSomething] -> [T1] getT2s :: [TSomething] -> [T2] getT1s xs = [t1 | It's1 t1 <- xs] getT2s xs = [t2 | It's2 t2 <- xs]
正如你所说,这涉及一些样板。
Typeable
版本看起来更好一点:deriving Typeable T1 deriving Typeable T2 -- can specialize at the call-site to -- getTs :: [Dynamic] -> [T1] or -- getTs :: [Dynamic] -> [T2] getTs :: Typeable a => [Dynamic] -> [a] getTs xs = [x | Just x <- map fromDynamic xs]