f1 = (zipWith ($)) . (mapĀ  (*))

f2 = sum

First, f1 would be clearer as zipWith (*).

Second, look at the type of (.):

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

Notice that (b -> c) and (a -> b) each take one argument, but f1 takes two arguments. That's why using (.) with f1 doesn't work.

You can do what you want by uncurrying f1 so it takes only one argument, which makes it easier to compose. You can then re-curry the result after you do the composition.

f1 :: [Integer] -> [Integer] -> [Integer]
uncurry f1 :: ([Integer], [Integer]) -> [Integer]
f2 . uncurry f1 :: ([Integer], [Integer]) -> Integer
curry (f2 . uncurry f1) :: [Integer] -> [Integer] -> Integer

I'm sure there are shorter versions that use more than just (.), curry, and uncurry.