Question

Say we have two functions of type (a -> b -> c). I want to have a single function, which, when applied to a and b will give d, merged from c by a specified function (c -> c -> d). I came up with this solution using arrows:

combine :: (a -> b -> c) -> (a -> b -> c) -> (c -> c -> d) -> (a -> b -> d)
combine f g op = ((uncurry op) .) . (uncurry (&&&)) . (f &&& g)

Is there a way to do this in more elegant way, or to generalize it to be applicable to functions with greater arity (e.g. (a -> b -> c -> d) -> (a -> b -> c -> d) -> (d -> d -> e) -> (a -> b -> c -> e))?

Was it helpful?

Solution

Don't be afraid to be explicit. For me this is much easier to read:

combine :: (a -> b -> c) -> (a -> b -> c) -> (c -> c -> d) -> (a -> b -> d)
combine f g op = \a b -> op (f a b) (g a b)

Doesn't look any uglier for more arities:

combine3 f g op = \a b c -> op (f a b c) (g a b c)

OTHER TIPS

While I agree the explicit lambda might be the way to go here, there are alternatives for binary f and g. For unary f and g, the solution is elementary.

combine f g op = liftM2 op f g

for binary f and g, we can make them unary by uncurrying them and then apply the same solution! This makes the binary version

combine2 f g op = curry $ liftM2 op (uncurry f) (uncurry g)

I'm just putting it out there as an alternative.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top