Daniel Causebrook
Daniel Causebrook

Reputation: 479

Function composition errors

I'm learning Haskell and I came across a situation in which I'm not entirely sure what is going on.

I'm working on a bigger project, and have been trying to 'simplify' my code. I'd learnt about function composition and I think I mostly understand it. However, whilst pushing the limits of this, I encountered a situation I'm not sure how to explain.

After a few hours of fiddling about with it, I boiled it down to this:

foo1 x y z = x == (y && z)
foo2 x y z = (x ==) . (&&) y z     -- Error
foo3 x y   = (x ==) . (&&) y
foo4 x     = (x ==) . (&&)         -- Error

Why do foo1 and foo3 work, whilst foo2 and foo4 give errors when I try to define them? As far as I know, there's no reason adding or removing equivalent arguments to both the input and output should change the function.

Edit: Include errors

foo2 yields the error:

<interactive>:1:23: error:
    • Couldn't match expected type ‘a -> a1’ with actual type ‘Bool’
    • Possible cause: ‘(&&)’ is applied to too many arguments
      In the second argument of ‘(.)’, namely ‘(&&) y z’
      In the expression: (x ==) . (&&) y z
      In an equation for ‘foo2’: foo2 x y z = (x ==) . (&&) y z
    • Relevant bindings include
        x :: a1 (bound at <interactive>:1:6)
        foo2 :: a1 -> Bool -> Bool -> a -> Bool
          (bound at <interactive>:1:1)

Whilst foo4 gives the error:

<interactive>:2:15: error:
    • No instance for (Eq (Bool -> Bool))
        arising from an operator section
        (maybe you haven't applied a function to enough arguments?)
    • In the first argument of ‘(.)’, namely ‘(x ==)’
      In the expression: (x ==) . (&&)
      In an equation for ‘foo4’: foo4 x = (x ==) . (&&)

Upvotes: 1

Views: 113

Answers (1)

Johnny Liao
Johnny Liao

Reputation: 567

function application

Let's start with function application.

a b is an application of a function a to one arguments b.

a b c is an application of a function a to two arguments b and c.

a b c d is an application of a function a to three arguments b, c, and d.

(&&) and (.)'s type signature

Then look at these two functions' type signature.

(&&) :: Bool -> Bool -> Bool

takes two arguments

(.) :: (b -> c) -> (a -> b) -> a -> c

takes three arguments, two of them functions

Then we look into these four cases.

foo1

foo1 x y z = x == (y && z)

(y && z) is (&&) y z, fully applied. So the result is a Bool type.

x == Bool is (==) x Bool. So x is a Bool type.

It works.

foo2

foo2 x y z = (x ==) . (&&) y z

(x ==) is a function that expects one argument.

so (x ==) . :: (a -> b) -> a -> c

However, as @Carcigenicate says, your (&&) is fully applied.

Hence, compiler say, "Couldn't match expected type ‘a -> a1’ with actual type ‘Bool’"

foo3

foo3 x y   = (x ==) . (&&) y

(.) :: (b -> c) -> (a -> b) -> a -> c

(.) function composition takes function ( x==) and (&&) y

and all it need now is another a to produce c.

so foo3 is just foo1's point-free style.

It works, too.

foo4

foo4 x     = (x ==) . (&&)

So we already meet (x ==) .

its type signature is (a -> b) -> a -> c

However, (&&) :: Bool -> Bool -> Bool

Although it can be seen as Bool -> (Bool -> Bool), a function that takes one argument and produce a Bool -> Bool function.

(Bool->Bool) is not an instance for Eq. It doesn't have (==).

Thus the compiler says "No instance for (Eq (Bool -> Bool))"

Point-free style

As far as I know, there's no reason adding or removing equivalent arguments to both the input and output should change the function.

It holds true when they are on the same level.

name b c d = function b c d --fully applied

name b c = function b c --wait one argument

name b = function b --wait two arguments

name = function --wait three/all arguments

but function composition needs to compose two one-argument functions into one-argument function(and the right one's output type need to match the left one's input type.)

So the order is function application first, then (.) / function composition second. And you need to feed (b->c) and (a->b) to (.) to get a new (a->c) function.

Upvotes: 2

Related Questions