Aufheben
Aufheben

Reputation: 857

How to map a function over the arguments of another function in Haskell?

So initially I wrote:

xs <- getAddrInfo (Just hints) (Just addr) (Just port)

then it seemed to me that the function 'Just :: a -> Maybe a' is kind of "mapped" over 'hints', 'addr', and 'port', so I came up with something like this:

map_arg g f a b c = f (g a) (g b) (g c)
xs <- map_arg Just getAddrInfo hints addr port

but GHC expects (g a), (g b) and (g c) to be of the same type, so this doesn't type check.

Is there a way to do this, or more generically, is there a way to map a function over the arguments of another function?

Upvotes: 4

Views: 194

Answers (3)

Earth Engine
Earth Engine

Reputation: 10436

A most generic type signature would looks like

map_arg :: (forall b.b -> a b) -> (a b -> a c -> a d -> e) -> b -> c -> d -> e
map_arg g f a b c = f (g a) (g b) (g c)

for your case, if you choose not to put g as a parameter, you can do

map_just f a b c = f (g a) (g b) (g c) where g = Just
xs <- map_just getAddrInfo hints addr port

or you can just give type signature to g only:

map_arg (g :: forall b.b -> a b) f a b c = f (g a) (g b) (g c)

To bypass the polymorphic type signature, remember we have Control.Functor.Pointed so you can use it:

map_arg :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d
map_arg f a b c = f (point a) (point b) (point c)

(The implementation of Pointed for Maybe is Just what you want)

To have a generalized version, note that

map1 :: Pointed p => (p a -> b) -> a -> b
map1 f = f . point

map2 :: Pointed p => (p a -> p b -> c) -> a -> b -> c
map2 f = map1 . map1 f

map3 :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d
map3 f = map2 . map1 f

See that? you only need map1 and all others is just simple combination!

Upvotes: 5

Roman Cheplyaka
Roman Cheplyaka

Reputation: 38718

The short answer is no (if you're talking about a generic map_arg that'd work for any number of arguments).

You might be able to achieve this with Oleg-level type magic, but if you're just looking for the ways to improve your code, there's not much to improve here.

Upvotes: 4

snak
snak

Reputation: 6703

Just add a type annotation to map_arg and make g polymorphic.

map_arg :: (forall a. a -> Maybe a) ->
           (Maybe AddrInfo -> Maybe HostName -> Maybe ServiceName -> IO [AddrInfo]) ->
           AddrInfo -> HostName -> ServiceName -> IO [AddrInfo]
map_arg g f a b c = f (g a) (g b) (g c)

You need Rank2Types or RankNTypes extension to do this.

Upvotes: 2

Related Questions