Even more on functions
In the previous chapter, we learned how to write variadic, and recursive
functions and how to use the
defer statement to defer functions calls to
just before the currently executing function returns.
In this chapter, we will learn something that might seem surprising a little bit, which will end up being awesome.
Do you remember when we talked about :ref:`functions signatures <functions-signatures>`? That what matters most in a function, beside what it actually does, is the parameters and results numbers and types.
Well, a function type denotes the set of all functions with the same parameter and result types.
For example: the functions with signatures: func
SUM(int, int) int and
MAX(int, int) int are of the same type, because their parameter count,
results count and types are the same.
Does this sound strange? One computes a sum, and the other the max, yet they are both of the same type?
Yes, they are. Picture this with me: The numbers 1, and 23 are both of the same
int) yet their values are different. Paul and Sam are both of the
same "human" type, yet their "values" (beliefs, jobs, weight...) are different.
The function's body, what it actually does, is what makes the difference between two functions of the same type. Just like how many coffees you can by with 1 dollar or 23 dollars.
Same thing for functions: What they do, their bodies, is what differentiate two functions of the same type.
Convinced? Let's see how to declare functions types.
How to declare a function type?
The syntax is simple:
type type_name func(input1 inputType1 [, input2 inputType2 [, ...]) (result1 resultType1 [, ...])
Some examples to illustrate this:
Is it clear now?
Yes, by why is this even useful or worth the hassle? You might find yourself asking. Well, I'm glad you asked this question! Read on, you will see the beautiful madness to this method!
Functions are values
Functions of the same type are values of this type! This means: You can declare a variable of a given function-type and assign any function of the same type to it. You can pass functions as parameters to other functions, you can also have functions that return other functions as results... And this, my friend, offers a lot of beautiful, intellignt and powerful opportunities! Let me show you some examples (don't worry, this doesn't result in Skynet... quite...).
Example 1: Write a program which when given a slice of integers will return another slice which contains only the odd elements of the first slice (yes... of course... by 'odd' I meant the 'weird' ones... what is wrong with you people?!?).
isEven are very simple.
They both take an
int, and return a
Of course, they're just little examples of functions of type
One can imagine more advanced and complex functions that are of this type.
The interesting part is the
filter function, which takes a slice of type
int and a function of type
test_int and returns a slice of type
Look at how we made a call
f(value) on line 30. In fact, the function
filter doesn't care how
f works, what matters for it is that
int parameter, and that it returns a
And we used this fact, in our
main function. We called
different functions of type
If we're asked to extend the program to filter all the elements of the slice
that, for example, are multiple of 3. All we would have to do is write a new
is3Multiple(i int) bool and use it with
Now, if we want to filter the odd integers that are multiple of 3, we call filter twice likes this:
slice := filter(filter(s, isOdd), is3Multiple)
filter is good at what it does. It filters a slice given a
criteria, and this criteria is expressed in the form of functions.
Cool, isn't it?
In the previous example, we wrote the functions
of the main function. It is possible to declare anonymous functions in Go,
that is functions without a name, and assign them to variables.
Let's see what this means with an example:
add1 is assigned a complete function definition, minus the
name. This function is said to be "anonymous" for this reason (lack of a name).
Let's see an example using anonymous functions, and that returns functions.
Functions that return functions
Back to the filtering problem, but now, we'd like to design it differently. We
want to write a function
filter_factory that given a single function
isOdd, will produce a new function that takes a slice
s of ints,
and produces two slices:
yes: a slice of the elements of
no: a slice of the elements of
How does this work? First, we won't discuss
they're simple and both of the same type:
func(int) bool. That's all that
matters for us, now.
Now the heart of the program: the
filter_factory function. Like stated in
the comments, this function takes as a parameter a function
f of type
fun(int) bool (i.e. the same as
filter_factory returns an anonymous function that is of type:
func(int)(int, int) --or in plain english: a function that takes a
slice of ints as its parameters and outputs two slices of ints.
This anonymous function, justly, uses
f to decide where to copy each
element of its input slice. if
true then append it to
yes slice, else append it to the
filter function in the first example, the anonymous function
doesn't care how
f works. All it knows is that if it gives it an
it will return a
bool. And that's enough to decide where to append the
value; in the
yes or the
Now, back to the
main function and how we use
filter_factory in it.
The detailed one: we declare a variable
odd_even_function and we assign to
it the result of calling
isOdd as its parameter. So
odd_even_function is of type
func(int) (int, int).
s as its parameters and we retrieve two
The second way is compact. The call
filter_factory(isBiggerThan4) returns a
function of type
func(int)(int, int) so we use that directly with our
s and retrieve two slices:
Does it make sense, now? Re-read the code if not, it's actually simple.
Functions as data
Since functions have types, and can be assigned to variables, one might wonder whether it is possible to, for example, have an array or a slice of functions? Maybe a struct with a function field? Why not a map?
All this is possible in fact and, when used intelligently, can help you write simple, readable and elegant code.
Let's see a silly one:
It may sound funny, and probably silly, but what I wanted to show you is how
functions can be used as
struct fields just as simply as classic data types.
Yes, I know what you are thinking; It would have been more elegant if the
speak of the type
person could access her
itself and we won't even need the
compose function then.
That's called "Object Oriented Programming" (OOP), and even though Go isn't really an OOP language, it supports some notions of it. This will be the subject of our next chapter. Stay tuned, you, geek! :)