Go Functions: Difference between revisions
(→main()) |
|||
Line 232: | Line 232: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=<span id='Environment'></span>Environment of a Function= | ==<span id='Environment'></span>Environment of a Function== | ||
=Closure= | =Closure= | ||
A closure is a function and its [[#Environment|environment]]. | A closure is a function and its [[#Environment|environment]]. |
Revision as of 22:22, 28 August 2023
External
Internal
Overview
A function is a block of instructions, grouped together, and that optionally have a name.
Functions exist for code reusability reasons: the function is declared once and then can be invoked any number of times. The functions can be reused from within the project, or from other projects, if the function is declared as part of a package that is imported in the project that needs to use the function.
Function exist for abstraction reasons: they hide details into a compact "packaging" and improve the understandably of the code.
Declaration
A statical function declaration starts with the func
keyword followed by the function name and a mandatory parentheses pair. Note that functions can also be created dynamically.
func <function_name>([parameters]) [(return_declaration)] {
// body
[return [return_values]]
}
func someFunction(color string, size int) (float64, error) {
//
// body
//
var result float64
var err error
// ...
return result, err
}
Parameters
The parentheses optionally enclose function parameters. A function may not have any parameters, but in this situation, the parentheses must still be provided. The parameters, when exist, are vehicles for the input data the function needs to operate on. The parameter declaration syntax consists in a set of variables listed after the function name, between parentheses. Parameters become local variables to the function, scoped to the function body.
...(<par_name_1> <type>, <par_name_2> <type>, ...)
Example:
func blue(x int, s string) {
...
}
If there are multiple parameters of the same type, they can be provided as a comma separated list postfixed by the type:
func blue(x, y int, s string) {
...
}
Also see:
Return Declaration
The function output must have a type (or types), which are listed in the function declaration after the parameter list.
func ...(...) <return-type> {
...
}
If the function has more than one return values, their types must be enclosed in parentheses.
func ...(...) (<return-type-1>, <return-type-2>, ....) {
...
}
Function Body
Parameters are local variables visible inside the function body.
Go functions allow new local variables to be declared, inside the function, with the short variable declaration. The short variable declaration is not allowed anywhere else, except a function body.
The function returns its output value(s) with the return
keyword:
{
...
return someVar
}
More than one values can be returned at the same time, and such a function can be used with the multi-value short variable declaration form.
{
...
return someVar1, someVar2
}
Invocation
All functions, except main()
must be invoked explicitly from the program to execute.
A function is invoked, or called, by specifying the function name, mandatory followed by open parentheses, optionally followed by arguments, if the function has parameters, then mandatory followed by closing parenthesis.
result, err := someFunction("blue", 3)
Arguments
The arguments consist of the data supplied to the function as part of the invocation.
Pass by Value vs. Pass by Reference vs. Pass by Pointer
"Call by value vs. call by reference" describes how arguments are passed to parameters during a function call. When the function is invoked, arguments are passed to the function and the values of those arguments are bound to the function's parameters.
In Go, arguments are always passed by value. Arguments are copied to parameters, on the function's call stack. The data that function uses is a copy of the original. This approach promotes encapsulation: the function cannot modify the original data. The called function cannot changed the variables inside the calling function. The approach also comes at the cost of the argument copy time, which for large pieces of data, can be non-trivial. Arrays are also passed by value, their data is always copied, and for large arrays this can be a problem. This is the reason array should not be used directly, but slices should be used instead.
The alternative to pass by value is pass by reference (or by pointer). Go does not have a built-in pass by reference mechanism, but pass by pointer can be manually implemented, by passing the pointer to a variable to a function.
func passByPointerExample(i *int) {
*i = *i + 1
}
func callerFunction() {
x := 2
passByPointerExample(&x)
fmt.Println(x) // will print 3
}
main()
All programs in Go must have a main()
function, where the program execution starts. The main()
function must be declared in the main
package.
You never call this function. When a program is executed, the main()
gets called automatically.
Built-in Functions
Built-in functions are available by default, without importing any package. Their names are predeclared function identifiers. They give access to Go's internal data structures. Their semantics depends on the arguments.
Length and Capacity
len()
len()
returns string length, array length, slice length and map size.
cap()
cap()
returns slice capacity.
Complex Number Manipulation
TO DO: Continue to Distribute These Built-in Functions
Allocation: new()
Making slices, maps and channels: make()
Appending to and copying slices: append(), copy()
Deletion of map elements delete()
Handling panics panic(), recover()
Functions as First-Class Values
Go implements features of a functional programming style. Function are treated as first-class value, which means functions are treated like any other type. Variables can be declared to be of a function type.
Function instances can be created dynamically at runtime, as opposite to declaring them statically in the program with the func
keyword. Functions can be passed as arguments into a function invocation and they can be returned as values of a function invocation. Functions can be stored into a data structure.
Declaring a Function Variable
The variable name acts as an alias, another name for the function.
var <var_name> <func_type>
A function type is declared using the following syntax:
func(<parameter_1_type, parameter_2_type, ...) (return_1_type, return_2_type, ...)
Example:
func SomeFunction(s string, i int) (int, error) {
...
}
...
var f func(string, int) (int, error)
f = SomeFunction
f("10", 20)
Passing a Function as Argument into a Function
func <func_name>(<func_var> <func_type_declaration>, ...) ... {
...
}
Example:
func Invoker(f func(string) int, s string) int {
result := f(s)
return result
}
func SomeFunction(s string) int {
i, _ := strconv.Atoi(s)
return i
}
...
result := Invoker(SomeFunction, "10")
...
Anonymous Functions
These are lambdas.
In the example for Passing a Function as Argument into a Function, we can simply create the function passed as argument on the fly, without giving it a name:
result := Invoker(func(s string) int {
i, _ := strconv.Atoi(s)
return i
}, "10")
Returning Function as Result of Function Invocations
func FunctionMaker() func(int) int {
// we are creating an anonymous function
f := func(i int) int {
return i + 1
}
return f
}
Environment of a Function
Closure
A closure is a function and its environment.
Elements of Style
Strive to write your functions so it enhances the understandability of your code. A code is understandable if, when you are in the position to find a feature, you can find it quickly. In general, you should be able to answer fast to question of type "Where is the code that does something?"
Avoid global variables. Without global variables, data is local to function.
Name functions and variables meaningfully. You don't want the names to be too long. "If you want to work with people, naming is really important".
Limit the number of parameters. A large number of parameter is a symptom of bad functional cohesion.
Functional cohesion: a function should perform only one operation.
Functional complexity: don't make function too complex: lines of code, control-flow complexity.
To reduce control-flow complexity, separate conditionals in different functions.
DEPLETE THIS
Built-in functions for type conversions.
deplete this Go Concepts - Functions