heinwol
heinwol

Reputation: 448

Point free composition of multivariate functions

Say, we want to introduce the notion of sum of functions of different arguments (let's call it <+>), which behaves like the that: (f1 <+> f2)(x1, x2) == f1(x1) + f2(x2).

While this can be easily written out manually, it makes sense to use point-free style with the help of the notion of cartesian product of functions. The latter is defined below and seems alright and quite general to me:

x :: (x1 -> y1) -> (x2 -> y2) -> (x1 -> x2 -> (y1, y2))
x f1 f2 = \x1 x2 -> (f1(x1), f2(x2))

Then we can write:

(<+>):: Num a => (a -> a) -> (a -> a) -> (a -> a -> a)
(<+>) = (uncurry (+)) . x

And the code above seems fine to me too, but GHC thinks otherwise:

 * Couldn't match type: (x20 -> y20) -> a -> x20 -> (a, y20)
                     with: ((a -> a) -> a -> a -> a, (a -> a) -> a -> a -> a)
      Expected: (a -> a)
                -> ((a -> a) -> a -> a -> a, (a -> a) -> a -> a -> a)
        Actual: (a -> a) -> (x20 -> y20) -> a -> x20 -> (a, y20)
    * Probable cause: `x' is applied to too few arguments
      In the second argument of `(.)', namely `x'
      In the expression: (uncurry (+)) . x
      In an equation for `<+>': (<+>) = (uncurry (+)) . x
    * Relevant bindings include
        (<+>) :: (a -> a) -> (a -> a) -> a -> a -> a

It feels like the compiler cannot infer the second function's type, but why? And what am I supposed to do, is this even possible to do?

Upvotes: 1

Views: 94

Answers (3)

Will Ness
Will Ness

Reputation: 71109

Define

compose2 :: (b -> c -> t) -> (a -> b) -> (d -> c) -> a -> d -> t
compose2 p f g x y = p (f x) (g y)

Now, compose2 (+) is your <+>:

> :t compose2 (+)
compose2 (+) :: Num t => (a -> t) -> (d -> t) -> a -> d -> t

As you can see its type is a bit more general than you thought.

compose2 already exists.

Upvotes: 1

Daniel Wagner
Daniel Wagner

Reputation: 153102

If you supply two arguments, you will see what has gone wrong.

(<+>) = uncurry (+) . x
(<+>) a = (uncurry (+) . x) a
        = uncurry (+) (x a)
(<+>) a b = uncurry (+) (x a) b

Whoops! That b gets passed to uncurry as a third argument, rather than x as a second argument as you probably intended. The third and fourth arguments are also supposed to go to x rather than uncurry, as in:

(<+>) a b c d = uncurry (+) (x a b c d)

Here's the correct way to point-free-ify a four-argument composition.

\a b c d -> f (g a b c d)
    = \a b c d -> (f . g a b c) d
    = \a b c -> f . g a b c
    = \a b c -> ((.) f . g a b) c
    = \a b -> (.) f . g a b
    = \a b -> ((.) ((.) f) . g a) b
    = \a -> (.) ((.) f) . g a
    = \a -> ((.) ((.) ((.) f)) . g) a
    = (.) ((.) ((.) f)) . g

Most people then write this with section syntax as (((f .) .) .) . g. Applying this new fact to your case:

\a b c d -> uncurry (+) (x a b c d)
    = (((uncurry (+) .) .) .) . x

Upvotes: 3

Noughtmare
Noughtmare

Reputation: 10645

The . operator is only for composing functions with a single argument, but the function x has four arguments, so you have to use . four times:

(<+>) = (((uncurry (+) .) .) .) . x

Do keep in mind that this is not considered good style in actual code.

Upvotes: 2

Related Questions