So we can have a function which takes a single parameter which is a tuple :
function_name :: (Param1Type, Param2Type) -> ReturnType
Or we can have a function which takes a single parameter and returns another function: function_name :: Param1Type -> (Param2Type -> ReturnType)
Using implied operator precedence, the later is written: function_name :: Param1Type -> Param2Type -> ReturnType
And has to be distinguished from a function which takes a function as parameter: function_name :: (Param1Type -> Param2Type) -> ReturnTypeWhat we usually see is something like this:
f(x) = y
But if you look at it carefully the function f is a mapping of the domain x to the range y (the arrow is f), written like so: x -> y
Of course, the domain, being a set of all permissible values, is the type. So we write the type instead: type1 -> type2
Everything in Haskell is a function, and pure functions only ever take ONE parameter. Therefore the commas make no sense. The name in front just aids in naming stuff.If you have a function that takes 2 parameters, they'd have to be curried. How would you represent curried functions?
f::type1 -> type2 -> type3
To make it more concrete, let's look at add instead of f.So we can define a function like this (let's use Python for its readability):
def add(a, b): return a + b
But remember, in haskell, functions are pure. Meaning they only map one input to one output. In order to make this happen, we need to split the function up into parts that only take one parameter.Let's start with the plus operator as a function (it is one in Haskell just made into an infix). To think about it, it'd be something like this:
plus(a)(b)
Where plus(a) is defined as: def plus(a): return plusA
Hence the first part would become a curried function like so, which takes another parameter: plusA(b)
Where plusA() is defined as such: def plusA(b): return b + a # a is a constant
So if you look at it from the types it was being transformed from: plus() # takes a Real, returns plusA.
plusA(___) # takes a Real, returns a Real. written as (Real -> Real)
So if you put it together, the function signature for plus() is:plus() takes Real -
plus :: Real->
plus() returns plusA (which is Real->Real) - plus :: Real-> (Real-> Real)
Or in other words we can write it as such add :: Real -> Real -> Real fun :: A -> B -> C -> D
can be bracketed in a bunch of different ways - fun1 :: (A -> B) -> (C -> D)
fun2 :: A -> (B -> C -> D)
etc. and don't these all mean different things?fun1 would take a function and return a function, whereas fun2 takes an A and returns a curried function of B,C -> D.
A -> B -> C -> D
is equivalent to any of the following A -> (B -> C -> D)
A -> (B -> (C -> D))
A -> B -> (C -> D)
They're equivalent through currying. But you can only add or remove parentheses for stuff that is on the right of a function arrow. (A -> B) -> (C -> D) is a different thing. fun :: A -> (B -> (C ->D))Secondly, note that in Haskell a space represents function application -- it's not just there to separate terms.
This stuff might seem crazy, but once you start using it for useful things you come to really miss it in other languages. :)
Say we have a function with signature
f :: Int -> Int -> Int -> Int
Let's say this just add those Ints together.Now let's create another function by partially applying the initial function: f1 = f 42
f1 signature will be
f1 :: Int -> Int -> Int
Kind of "consuming" the signature. Repeat this until using up all the parameters. You end up with a function with no parameters, which map to a single Int.You could say that f3 -> Int (note, not a valid notation for the sake of example). No need for a different symbol.
function_name :: param1 -> param2 -> return
is a function that if you only give it param1 it returns a function that takes an argument of param2Type and returns returnType.
This way you can easily compose functions
Why not have something sensible like this?