
On 11/01/07, Marco Túlio Gontijo e Silva
is there a way to defined something as a map to use in tuples? I tried this:
mapTuple f (a, b) = (f a, f b)
But the type inferred to it is not as generic as I wanted:
mapTuple :: (t -> t1) -> (t, t) -> (t1, t1)
Let's think about what type we might want to give this. Here's a first attempt: mapTuple :: (a -> c) -> (a, b) -> (c, d) mapTuple f (x, y) = (f x, f y) But this won't typecheck, because we can only apply the function to the first element in the tuple from the type that we have. In other words, we don't know that we can pass values of type b into f. We could say: mapTuple :: (a -> c) -> (a, a) -> (c, c) But this isn't as polymorphic as you'd like. An alternative is sum types: mapTuple :: (Either a b -> c) -> (a, b) -> (c, c) mapTuple f (x, y) = (f $ Left x, f $ Right y) Note that we can't do: mapTuple :: (Either a b -> Either c d) -> (a, b) -> (c, d) Because we don't know that feeding a value of type c into f will yield a value of type d; f (Left x) might return a Right-tagged value. We could perhaps do this: mapTuple :: (Either a b -> (c, d)) -> (a, b) -> (c, d) mapTuple f (x, y) = (fst $ f $ Left x, snd $ f $ Right y) And a function would then return both a value of type c and d (possibly one of them being undefined). However, it doesn't really seem satisfactory to impose this constraint on our arguments. Instead, it seems we want to supply two functions: mapTuple :: (a -> b) -> (c - >d) -> (a, b) -> (c, d) And this appears now as a the classic f × g operation over pairs, implemented as (***) in Control.Arrow: (***) :: (a -> b) -> (c -> d) -> (a, b) -> (c, d) (f *** g) (x, y) = (f x, g y) (Actually the original is a little lazier, using irrefutable patterns on the pair.) Then you can do: (show *** show) ("string", True) And I suspect that's the best you'll get. There may be a wild solution involving typeclasses and existentials, but this doesn't seem altogether too bad. -- -David House, dmhouse@gmail.com