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

3

我看到了HList包,但我认为这对我的需求来说太过头了。

我有这个:

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

我可以使用以下方法成功地在其前面添加内容:

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

但我希望能够以某种方式迭代“列表”,并对元素执行操作,但我不确定如何实现。


1
小心,:*:现在是GHC.Generics中的(非)标准类型。 - alternative
2个回答

5

以下两篇论文可能对您有所帮助:HList papergrapefruit-records paper

基本迭代相对简单(前提是您习惯于以实质上的Prolog方式思考),通过元素类型(或标签)匹配记录是困难的:根据类型相等性做某些事情目前需要使用OverlappingInstances,并且您将无法决定(大部分?全部?)多态类型的相等性。

根据类型匹配可能是您需要“对元素执行某些操作”的方法,具体取决于这个“某些操作”是什么;删除很容易,应用一个函数,其中其参数必须与元素类型匹配,不是微不足道的,但应该可以在没有编译器扩展的情况下完成,只要您能给出元素的数字索引。

(编辑:这假设您想将一等函数应用于列表。Rampion的答案展示了如何使用普通类型类轻松匹配类型,但类型类无法作为值传递。)

因此,最终可能会发现HList或grapefruit-records并不过度,毕竟。


2
赞同@barsoap的建议。如果您真的需要可扩展性,那么比HList更简单可能会很困难。 HList论文(或者肯定是草稿)指出,在类型级别列表中拥有Nil比省略它更容易。问题中的:*:定义只是一对,因此没有Nil。 - stephen tetley

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

Haskell的类型系统非常先进,它可以使函数不是一等公民;) 好耶,表达式问题! - barsoap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接