【发布时间】:2014-10-16 17:50:23
【问题描述】:
我想编写一些适用于 Haskell 中所有数据类型的函数(至少是 Data.Data 中的所有数据实例)。我遇到的一个普遍问题是根据构造函数是否递归来选择要做什么——递归构造函数是指数据类型是 D,并且有一个构造函数 C,其中一个参数是 D。
我可以通过解决一个更简单的问题来解决上述问题:给定一个数据,确定最外面的构造函数是否是递归的。
这是第 1 次尝试:
data B a = T | F
gIsLeafCons :: (Data a) => a -> B a
gIsLeafCons m = gfoldl (\x y -> F) (\x -> T) m
这个想法是,如果构造函数是递归的,那么我们返回 F,否则我们返回 T。乍一看这很好用:
gIsLeafCons 1 ==> T
gIsLeafCons ([] :: [Int]) ==> T
但是,gfoldl 是多态的,所以给定一个树数据类型比如
data Tree a = Leaf a | Node (Tree a) (Tree a)
我们失败了。
gIsLeafCons (Leaf 1) ==> F
原因是 (Leaf 1) 不是一个真正的空构造函数:它有参数 1,因此应用了构造函数 (\x y -> F)。
第 2 次尝试:
我们可以使用“叶构造函数”,即所有子级都评估为 F。这是一个轻微的改进:
gIsLeafByChild (Leaf 1) ==> T
但是,如果叶子持有不同的递归结构;这将再次失败。
gIsLeafByChild (Leaf (Leaf 1)) ==> F
我真的很想在第一个“叶子构造函数”上停下来,但是 gfoldl 的多态特性使它看起来很难做到这一点,如上所示。
最后,有没有办法判断构造函数是否是“叶构造函数”。我目前的解决方案是让用户传入一个叶子构造函数列表([Constr]),我只是检查构造函数是否是其中之一。但是,如果有东西在这个列表中,自动推断会很好。
【问题讨论】:
-
如果有两个不同数据类型的相互递归的构造函数,你会怎么做?例如
data T = T Int F; data F = F Int T。这可以合理地被认为是递归或“叶”。 -
@amalloy:这是个好问题。我对此并没有想太多,但是如果折叠该构造函数需要调用折叠,我会说构造函数是递归的。在上面 foldT t f (T n a) = t n (foldF f t a);折叠F f t (F n a) = f n (折叠T t f a)。所以我会称它们都是递归的。这合理吗?
-
您可能对变压器包中的
Data.Constant.Functor中的Constant感兴趣。 hackage.haskell.org/package/transformers-0.4.1.0/docs/… 它概括了您的B a类型的概念,例如,B a将是Constant Bool a。
标签: generics haskell constructor algebraic-data-types