Functions
Let's write our first function. It simply substracts 1
from its argument. (Please don't mind the '
after return
for now.)
-- show
import Prelude hiding ((.))
x.f = f(x)
-- /show
apply(f) = \(x) -> f(x)
return'(a) = a
-- show
function(x) = do {
return'(x - 1);
}
main = do {
print(function(42));
}
-- /show
Anonymous Functions
We can also define a function declaring its parameter after the equals sign. (A right-hand side argument declaration is surrounded by \... ->
.) By doing so the expression on the right-hand side of our function declaration becomes a valid function on its own, which can be used inline, i.e. without an identifier.
import Prelude hiding ((.))
x.f = f(x)
apply(f) = \(x) -> f(x)
return'(a) = a
-- show
function = \(x) -> do {
return'(x - 1);
}
main = do {
print(function(42));
print(42.apply(function));
print(42.apply(\(x) -> do {return'(x - 1);}));
}
-- /show
An anonymous function is a lambda expression in FP lingo. (The \
is supposed to resemble a λ
.)
Closures
A closure is a partially applicable function, i.e. a function that accepts a single argument and returns a function, which accepts a single argument and returns a value. (The nesting can be as deep as you like.)
import Prelude hiding ((.))
x.f = f(x)
apply(f) = \(x) -> f(x)
return'(a) = a
-- show
closure = \(a) -> do {
return'(
\(b) -> do {
return'(b - a);
}
);
}
partiallyAppliedClosure(x) = do {
return'(x.closure(1));
}
main = do {
print(partiallyAppliedClosure(42));
print(42.apply(partiallyAppliedClosure));
print(42.closure(1));
}
-- /show
A closure can be made a function that accepts a tuple of arguments and vice versa. This process is called "(un-)currying".
import Prelude hiding ((.))
x.f = f(x)
apply(f) = \(x) -> f(x)
return'(a) = a
-- show
closure = \(a) -> do {
return'(
\(b) -> do {
return'(b - a);
}
);
}
closure' = do { return'(uncurry(closure)); }
function(x, y) = do {
return'(y - 1);
}
function' = do { return'(curry(function)); }
main = do {
print(closure'(1, 42));
print(42.function'(1));
}
-- /show
Operators
At the end of the last tutorial we saw that x.apply(f)
can be written as f `apply` x
. The reason for that is that apply
is a closure with two arguments, a so-called operator. You normally see operators like -
written between its two arguments like in 42 - 1
. In order to use an operator written with alphanumeric characters like closure
in infix notation, we have to surround it in backticks: 1 `closure` 42
. In return, we need to surround -
in parenthesis in order to treat it like an ordinary closure, i.e. 1.(-)(42)
.
42.closure(1) -- -> 41
1 `closure` 42 -- -> 41
42 - 1 -- -> 41
1.(-)(42) -- -> 41
As we can guess, closure
is -
but with its arguments flipped. Since closure
is alphanumeric and -
is not, this makes closure
the alphanumeric equivalent of -
, provided that we will use an alphanumeric operator like closure
in object-oriented notation and a non-alphanumeric operator like -
in infix notation.
42.closure(1) -- -> 41
42 - 1 -- -> 41
Let's give closure
a propper name, sub
, and define it in terms of -
. As we have seen, sub
is -
but with its arguments flipped, so:
sub = do {
return'(flip((-)));
}
42 - 1 -- -> 41
42.sub(1) -- -> 41
Methods
As we have seen in the previous tutorial, we can write a function that takes one argument in object-oriented notation, too. This is the very definition of our .
operator: x.f = f(x)
. As we are parenthesis-phile, we introduced apply
which is yet another convoluted way of function application: f `apply` x = f(x)
. Together, .
and apply
form inversion of control.
x.apply(f) == f(x)
It is interesting to note that f(x)
has parenthesis, whereas x.f
doesn't. This is not a necessity, but a choice. We could define f()
which looks nice in x.f()
, but looks awkward in f()(x)
. Therefore, we will avoid empty parenthesis from now on.
decrement() = \(x) -> do {
return'(x.sub(1));
}
dec = \(x) -> do {
return'(x.sub(1));
}
42.sub(1) -- -> 41
42.decrement() -- -> 41 :(
42.dec -- -> 41 :)
Function Composition
You may have noticed, that sub = do { return'(flip((-))); }
doesn't contain any variable. This style is called "pointfree" in FP lingo. (We could have defined sub
as b `sub` a = do { return'(a - b); }
, too.)
When define functions in terms of other functions, we often concatenate two functions into one. This is called function composition. For example, given the functions even
, which returns True
or False
depending on the argument being even or not, and the function not
, which inverts True
and False
, we can easily define a function odd
, both in pointfull and pointfree style:
import Prelude hiding ((.))
x.f = f(x)
return'(a) = a
-- show
f `compose` g = \(x) -> do { return'(f(g(x))); }
oddPointfull(x) = do { return'(not(even(x))); }
oddPointfree = do { return'(not `compose` even); }
main = do {
print(42.oddPointfull);
print(42.oddPointfree);
}
-- /show
Haskell already has a compose operator of its own, i.e. .
, which we hid from the Prelude
in the previous tutorial in order to define object-oriented notation. This is why we just had to define our own compose
operator.
Constants
Let's define a constant.
import Prelude hiding ((.))
x.f = f(x)
-- show
constant = 42
main = do {
print(constant);
}
-- /show
We may have written constant = do { return'(42); }
, but this doesn't make sense, since constant
is a constant, and not a function, right? Well, in Haskell there is virtually no difference between a constant and the return value of a function, as the same argument applied to the same function will always yield the same return value. In FP lingo, functions are referentially transparent. (This has the benefit of return values being cachable - they can be memoized in FP lingo.) Therefore, we can treat functions just like constants and ommit the do { return'( ... ); }
ceremony.
import Prelude hiding ((.))
x.f = f(x)
-- show
f `compose` g = \(x) -> f(g(x))
oddPointfull(x) = not(even(x))
oddPointfree = not `compose` even
main = do {
print(42.oddPointfull);
print(42.oddPointfree);
}
-- /show