You can't really do this with a standard function. The best approach is to have a map function for each field. Happily, you can generate these automatically with a bit of template haskell from the lens library. It would look something like this:
data ManyFields a b c d = MF { _f1 :: a, _f2 :: b, _f3 :: c, _f4 :: d }
makeLenses ''ManyFields
This generates a lens for each field of ManyFields
. The lens is a simple construct that allows you to both access and change the value there--the changes can even be polymorphic, just like map! Note how each field is prefixed with an underscore: the lens has the same name as the field minus the underscore.
You can now access values like this:
> foo = MF 'a' "b" 3 False
> foo^.f1
'a'
You can set values using the set
operator. When used with a lens, it creates a setter function:
> :t set f1
set f1 :: a' -> ManyFields a b c d -> ManyFields a' b c d
To actually use it, you could do this:
> set f1 () foo
MF () "b" 3 False
Since you have a getter and a setter, writing a map function is pretty trivial. Happily, we don't even have to do this: the library provides a function called over
:
> :t over f1
over f1 :: (a -> a') -> ManyFields a b c d -> ManyFields a' b c d
If you like infix operators more, set
can also be called .~
and over can be called %~
. (The latter has a mnemonic: %
is mod, or "modify" :P.) This is also useful with the &
operator which is just $
flipped. So the following two versions are equal:
> over f1 ord foo
MF 97 "b" 3 False
> foo & f1 %~ ord
MF 97 "b" 3 False
I personally think the operators are a bit much. Unless you're going to be using lenses everywhere, I would stick to set
and over
.
I don't know of a good solution for zipping functions the way you described. But do take a look through the rest of the lens library--it's pretty large, and you never know what you'll find!