Home > Blockchain >  Point free composition of multivariate functions
Point free composition of multivariate functions

Time:09-29

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?

CodePudding user response:

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

CodePudding user response:

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.

CodePudding user response:

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.

  • Related