开发者

How to iterate through a heterogeneous recursive value in Haskell

I saw the HList package but I think that's way overkill for what I need.

I have this:

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

and I can prepend to it successfully with this:

prepend :: a -> b -> a :*: b
prepend a b =开发者_如何学运维 a :*: b

but I'd like to be able to iterate through the "list" somehow and do things with the elements but not sure how to do so.


The HList paper as well as the grapefruit-records paper might be of use, here.

Basic iteration is relatively simple (provided you're comfortable thinking in what's essentially Prolog), matching a record by element type (or label) is where things get hairy: Doing something depending on type equality currently demands OverlappingInstances, and you won't be able to decide equality on (most? all?) polymorphic types.

Matching on types might well be what you need to "do something with the elements", depending on what something is; Deletion is easy, applying a function, where its argument has to match the element type, is not trivial, but should be possible to do without compiler extensions, provided you can give the numeric index of the element.

(EDIT: This assumes that you want to apply a first class function to your list. Rampion's answer shows how to easily match on types using vanilla typeclasses, but typeclasses can't be passed around as values.)

So, in the end, it might turn out that HList or grapefruit-records isn't overkill, after all.


Note that you can already iterate through the list in two ways:

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

You can mimic this using your own typeclasses:

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

And then iterating over the collection is automatic

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

Alternately, you could do much the same using ExistentialQuantification and wrapper types, and then you wouldn't have to define your own list type.

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜