如何在Haskell中迭代异构递归值

时间:2011-02-17 01:22:33

标签: haskell

我看到了HList包,但我认为这对我所需要的东西来说太过分了。

我有这个:

data a :*: b = a :*: b deriving (Show, Eq)

我可以成功地使用它:

prepend :: a -> b -> a :*: b
prepend a b = a :*: b

但是我希望能够以某种方式遍历“列表”并使用元素进行操作,但不知道如何操作。

2 个答案:

答案 0 :(得分:5)

此处可能会使用HList paper以及grapefruit-records paper

基本迭代相对简单(假设您在基本上是Prolog时很容易思考),按元素类型(或标签)匹配记录是事情变得毛茸茸的事情:根据类型相等做一些事情当前需要OverlappingInstances ,你将无法决定(大多数?所有?)多态类型的相等性。

匹配类型可能就是你需要“对元素做些什么”,取决于什么是什么;删除很简单,应用一个函数,其参数必须与元素类型匹配,这不是微不足道的,但是如果你可以给出元素的数字索引,那么应该可以不使用编译器扩展。

(编辑:这假定您要将第一类函数应用于列表.Rampion的答案显示了如何使用vanilla类型类轻松匹配类型,但是类型类不能作为值传递。)

所以,最终,可能会发现HList或者葡萄柚记录毕竟不是过度杀伤。

答案 1 :(得分:4)

请注意,您可以通过两种方式遍历列表:

ghci> let hetlist = (1 :: Int) :*: ("two" :: String) :*: (3.0 :: Double) :*: ()
ghci> hetlist
((1 :*: "two") :*: 3.0) :*: ()
ghci> hetlist == hetlist
True

您可以使用自己的类型类来模仿:

class DoStuff a where
  dostuff :: a -> a

instance DoStuff Int    where dostuff i = 2*i
instance DoStuff String where dostuff s = s ++ s
instance DoStuff Double where dostuff d = d / 2
instance DoStuff ()     where dostuff () = ()

instance (DoStuff a, DoStuff b) => DoStuff (a :*: b) where
  dostuff (a :*: b) = dostuff a :*: dostuff b

然后自动遍历集合

ghci> dostuff hetlist
((2 :*: "twotwo") :*: 1.5) :*: ()

或者,您可以使用ExistentialQuantification和包装类型执行相同的操作, 然后你就不必定义自己的列表类型了。

data DoStuffWrapper = forall a. (DoStuff a) => DoStuffWrapper a
homlist = [ DoStuffWrapper (1 :: Int)
          , DoStuffWrapper ("two" :: String)
          , DoStuffWrapper (3.0 :: Double) 
          ]
homlist' = map (\(DoStuffWrapper a) -> DoStuffWrapper (dostuff a)) homlist