4

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?

3 Answers 3

5

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!

Sign up to request clarification or add additional context in comments.

4 Comments

So without a type signature, GHC just assumes g must be monomorphic? (Why? Just a simplifying assumption?)... g's type signature declares that g is polymorphic in its argument, and returns a result whose type is dependent on the argument's type. If you wanted the return type to be not of the form a b, (for example, g = id, or g = fromIntegral with a type class constraint), I guess you would need to set up a type family or fundep for g to use. (?)
@misterbee I think that is the limitation of Hindley–Milner type system, which is GHC based on. Without polymorphic type signature, Haskell is unable to do polymorphic type inference, unless the variable is bound by using a where or let clause.
@misterbee I guess you are right for using type family or fundep, although I don't know how to do so (yet).
@misterbee There is a way to bypass the limitation, see my latest edit.
4

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.

Comments

2

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.

1 Comment

Note that this solution will only work for getAddrInfo function or any other function with same signature, hence it is not a "generic" solution as question asked for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.