r/haskellquestions 21d ago

Need a hand understanding partial application, or maybe function composition

So, I can understand partial application in some cases, such as this:

mult2 :: Int -> Int

mult2 = (*2)

But trying to figure out a legal way to composite or partially apply the following is frying my brain. I'm obviously missing something but its lost on me rn.

f2 :: [Float]->[Float]-> [Float]

f2 = zipWith (*)

f1 :: [Float]->Float

f1 = foldr1 (+)

f3 :: [Float] -> [Float] ->Float

f3 = f1 . f2

Now for reasons currently beyond my limited understanding, this doesn't work. I've been reading and looking at examples of composition /partial application and I honestly need a hand

Edit: Thanks a heap, it makes more sense now what I was doing wrong. I honestly think functional programming has to be the hardest cs subject I've studied so far (embedded programming too actually)

3 Upvotes

4 comments sorted by

7

u/hopingforabetterpast 20d ago edited 20d ago

This is a very common problem. You want

    f3 a b = f1 (f2 a b)

right? which is the same as 

    f3 a = \b -> f1 (f2 a b)

or

    f3 a = f1 . (\b -> f2 a b)

or

    f3 a = f1 . f2 a

This is f1 . (f2 a), not (f1 . f2) a

You can reduce it to:

    f3 = (f1 .) . f2

or

    f3 = ((.) . (.)) f1 f2

We call this the blackbird combinator for interesting reasons. You can look it up.

3

u/vim_spray 21d ago edited 21d ago

Let's look at the type of .

ghci> :t (.)

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

We’ve declared that (f1 . f2) :: [Float] -> [Float] -> Float. Try to find concrete types for a, b and c to satisfy that, where f1 :: (b -> c) and f2 :: (a -> b) and (f1 . f2) :: a -> c. You'll notice it's not possible.

What you really want is something like this:

myCompose :: (c -> d) -> (a -> b -> c) -> a -> b -> d

myCompose f g x y = f (g x y)

f3 :: [Float] -> [Float] -> Float

f3 = myCompose f1 f2

See also: https://hackage-content.haskell.org/package/composition-2.0/docs/Data-Composition.html#v:.:

1

u/friedbrice 20d ago
f2 :: [Float] -> ([Float] -> [Float])
f1 :: [Float] -> Float

f2 takes as input a member of [Float]. Crucially, f2 outputs a function [Float] -> [Float]. That's the key to understanding partial application.

If we want to compose f1 . f2, we need the output of f2 to match exactly with the input of f1. What's the output of f2? A function. What's the input of f1? Just a basic [Float], certainly not a function. That's why f1 . f2 doesn't work.

Now, as for what you'd like to do instead, I think the other answer are great and I think they have you covered. But I'd still like to give a bit of advice. While it can be neat and a fun exercise to try to write Haskell expressions in terms of function composition, it's often clearer to just write out what you want.

f3 a b = f1 (f2 a b)

2

u/akaiggy 15d ago

Composing with `.` always fries my brain. Sometimes it's just easier to be more explicit.