Calling functions
In Haskell, functions are called using the function name, followed by parameters, all seperated by spaces.
In C/ C++/ Java
function fn(x, y, z)In Haskell
fn x y zNotice that the Haskell syntax is a lot more terse than that of C, C++, and Java.
Calling a function on the result of another function
If you wish to chain functions together, there are a couple of different ways you would usually do this
fn a = a * a
gn a = a + a
x a = fn $ gn a
y a = fn (gn a)
main = print $ (x 5, y 5)The function fn returns the square if its input.
The function gn returns its input added to itself (or its input times two).
The function x calls gn on its input,
and takes the result from that and calls fn on it.
The function y does exactly the same thing as x,
merely expressed with a different syntax.
The $ operator can be thought of as equivalent to
surrounding the remainder of the line (to its right)
in parentheses.
x = 5
main = print $ x == (x)Note that a tuple with only one value within it evaluates to that value on its own, hence why the above works.
Haskell's baked in functions
Haskell comes with a load of built in functions. Let's practice calling some of them for fun, and chaining them together, as we have above.
min3 a b c = min a $ min b c
max3 a b c = max a (max b c)
main = print $ (min3 10 30 (-3), max3 (-1) (-20) (-99))Haskell's min and max functions do exactly what
you would think that they do -
find the minimum and maximum of the two inputs given to it.
I have built on top of these, functions that find the minimum of
three inputs instead of two.
If you pay close attention, you will notice that
I have chained one function call to another
in the same manner as I did previously with fn and gn.
Conditionals
So let us make a function that computes the distance of the diagonal edge of a right angled triangle:
pythag a b = sqrt $ a * a + b * b
main = print $ pythag 3 4We make use of sqrt to compute the square root of a number.
What if we decide that triangles cannot have negative lengths for their edges?
Here, there are three new things in play:
- if .. then .. else syntax
- The basic conditional execution syntax
- do block syntax
- Syntax used in Haskell to denote a sequence of actions
- error syntax
- Used to show that something has gone wrong
The error is used to disallow arguments which we consider to be illegal
from being used in this function.
The do block is used here, because we want two separate print statements.
Previously, we have simply printed a tuple, with one value for each output.
However, doing so means that all outputs need to evaluate before any of it
may print. Separating this into two print statements allows any intermediate
output to be printed, prior to attempting to evaluate the next expression.
pythag a b = if a < 0 || b < 0
then error "Lengths are not allowed to be negative."
else sqrt $ a * a + b * b
main = do
print $ pythag 3 4
print $ pythag (-3) 4Here the function evaluates to an if statement.
If either of the inputs are negative, error is raised.
(Note that this is not the best example,
let us stay focussed on the if for now)
Otherwise, the result is computed as before.
Haskell provides some syntactic sugar,
called guards, which allows us to state the above
in a more concise manner.
pythag a b
| a < 0 || b < 0 = error "Lengths are not allowed to be negative."
| otherwise = sqrt $ a * a + b * b
main = do
print $ pythag 3 4
print $ pythag (-3) 4Note how the if statement takes precedence over the rest of the function. It is as if it is acting as the contoller for the rest of the evaluation of the function. That is because it is. Haskell is effectively defining two different possible definitions for the body of this function, based on its inputs matching a set of conditions.
Let us examine how we would write the equivalent in C/ C++/ Java
function pythag(int a, int b)
{
if (a < 0 || b < 0)
{
throw Exc("Lengths are not allowed to be negative.");
}
else
{
return sqrt(a * a, b * b);
}
} (Assume we have typedefed Exc to be the exceptions object we wish to throw)
While this might look very similar, it exposes a very fundamental difference.
In C/ C++/ Java, however, the function does not evaluate in this sense.
Instead it depends on a a return statement to define the possible exit points.