What you have is
prod x y z t = x * y * z * t
= (x * y * z) * t
= (*) (x * y * z) t
Hence by eta reduction (where we replace foo x = bar x
with foo = bar
)
prod x y z = (*) (x * y * z)
= (*) ( (x * y) * z )
= (*) ( (*) (x * y) z )
= ((*) . (*) (x * y)) z
so that by eta reduction again,
prod x y = (*) . (*) (x * y)
Here (.)
is the function composition operator, defined as
(f . g) x = f (g x)
What you're asking about is known as point-free style. "Point-free" means "without explicitly mentioning the [implied] arguments" ("point" is a mathematician's jargon for "argument" here).
"Currying" is an orthogonal issue, although Haskell being a curried language makes such definitions -- and partial application ones, shown in Willem's answer -- easier to write. "Currying" means functions take their arguments one at a time, so it is easy to partially apply a function to a value.
We can continue the process of pulling the last argument out so it can be eliminated by eta reduction further. But it usually rapidly leads to more and more obfuscated code, like prod = ((((*) .) . (*)) .) . (*)
.
That's because written code is a one-dimensional encoding of an inherently two-dimensional (or even higher-dimensional) computational graph structure,
prod =
/
*
/ \
*
/ \
<-- *
\
You can experiment with it here. E.g., if (*)
were right-associative, we'd get even more convoluted code
\x y z t -> x * (y * (z * t))
==
(. ((. (*)) . (.) . (*))) . (.) . (.) . (*)
representing just as clear-looking, just slightly rearranged, graph structure
/
<-- *
\ /
*
\ /
*
\