Generic Haskell:确定构造函数是否递归

时间:2014-10-16 17:50:23

标签: generics haskell constructor algebraic-data-types

我想编写一些适用于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]),我只是检查构造函数是否是其中之一。但是,自动推断某些内容是否在此列表中会很好。

1 个答案:

答案 0 :(得分:1)

您使用gfoldl已经有了一个良好的开端。我们需要进行两项更改。首先,当我们遍历结构时,我们需要跟踪每个遇到的类型以查看它们是否以递归方式出现。其次,我们需要递归地使用gfoldl来查看我们在构造函数中发现的类型。

让我们获得一些实用工具。我们完成后会bool获得Bool结果,bId会将B从一种类型转换为另一种类型。

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}

import Data.Data

data Tree a = Leaf a | Node (Tree a) (Tree a)
    deriving (Data, Typeable)

data B a = T | F

bool :: B a -> Bool
bool T = True
bool F = False   

bId :: B a -> B b
bId T = T
bId F = F

我们可以从TypeRep类中获取类型的表示形式或TypeableTypeableData的超类,因此每当我们为某些Data a设置a个实例时,我们也会有Typeable a个实例。我们通过携带包含类型的列表来跟踪这些内容。 [TypeRep]中的表示。

recursivelyConstructed :: Data a => a -> Bool
recursivelyConstructed = bool . rc []
    where
        cf :: forall d. d -> B d
        cf = const F        
        rc :: forall d. Data d => [TypeRep] -> d -> B d
        rc ts d = gfoldl (rc' (typeOf d:ts)) cf d
        rc' :: forall d b. Data d => [TypeRep] -> B (d -> b) -> d -> B b
        rc' _  T _ = T
        rc' ts F d =
            if elem (typeOf d) ts
            then T
            else bId . rc ts $ d

让我们尝试一些例子

infiniteTree = Node infiniteTree infiniteTree

recursivelyConstructed (Leaf 1                        :: Tree Int) = False
recursivelyConstructed (Node (Leaf 1) (Leaf 2)        :: Tree Int) = True
recursivelyConstructed (infiniteTree                  :: Tree Int) = True
recursivelyConstructed (Leaf (Leaf 1)                 :: Tree (Tree (Int))) = False
recursivelyConstructed (Just (Leaf 1)                 :: Maybe (Tree Int))  = False
recursivelyConstructed (Just (Node (Leaf 1) (Leaf 2)) :: Maybe (Tree Int))  = True
recursivelyConstructed (Just infiniteTree             :: Maybe (Tree Int))  = True
recursivelyConstructed (Just (Leaf (Leaf 1))          :: Maybe (Tree (Tree Int))) = False