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
精彩评论