Martin Sulzmann
package main
import "fmt"
var x int
func hi(y int) {
fmt.Printf("hi %d\n",y)
}
func main() {
x= 1
hi(x)
fmt.Printf("hello, world\n")
}var varName varTypevarName of Type varTypePrintf similar to printf in C, see here for brief description of the C printf variant.
go run hello.go
gofmt hello.go
gofmt -w hello.goChoose your editor (emacs, …)
In most cases, our programs consist of a single file.
The type of y is inferred by the right-hand side.
Pretty convenient!
Languages like Haskell support full type inference where the types of functions can be inferred.
The only control structure.
Infinite loop with break
Unlike in C, Go strings are not null terminated. In Go, strings are represented by a struct consisting of
The length of the string.
A pointer to the first byte.
These implementation details are hidden from the user. The user can
make use of array notation to access a specific position in the string.
There is a primitive len that computes the length of a
string. Strings are immutable. This means they cannot be updated (but
copied of course).
Next, we discuss a few examples that involve strings.
Printing a string.
Several ways to iterate over a string.
for i := 0; i < len(x); i++ {
fmt.Printf("%c", x[i])
}
for i, e := range x {
fmt.Printf("%c", e)
fmt.Printf("%c", x[i])
}
// Don't care notation _
for i, _ := range x {
fmt.Printf("%c", x[i])
}
for _, e := range x {
fmt.Printf("%c", e)
}Via range we obtain current position and element. The
notation _ indicates that we don’t care about the
value.
Out of bounds check.
Short-hand array initialization
Iteration
More flexible arrays (length may change)
Short-hand
Functions on slices, e.g. append
Go uses call-by-value for paramter passing.
Call-by-reference can be emulated via pointers. Go supports pointers (but does not support pointer arithemtic).
Slices are internally represented via pointers.
package main
import "fmt"
func inc(i int) int { return i + 1 }
func mapInc(xs []int) []int {
for i, e := range xs {
xs[i] = inc(e)
}
return xs
}
func mapInc2(xs []int) {
for i, e := range xs {
xs[i] = inc(e)
}
}
func main() {
xs := []int{1, 2, 3}
ys := mapInc(xs)
fmt.Printf("%d %d\n", xs[0], ys[0])
mapInc2(ys)
fmt.Printf("%d \n", xs[0])
}Yields
2 2
3
package main
import "fmt"
func inc(i int) int { return i + 1 }
// Direct reference to return values by name
// Only applies to tuple return values
func myDiv(x int, y int) (res int, status bool) {
status = false
if y == 0 {
return
}
res = x / y
status = true
return
}
func myDiv2(x int, y int) (int, bool) {
if y == 0 {
return 0, false
}
return x / y, true
}
func main() {
var res int
var status bool
res, status = myDiv(inc(3), 2)
fmt.Printf("Result = %d \n", res)
fmt.Printf("Status = %t \n", status)
res, status = myDiv2(1, 0)
fmt.Printf("Result = %d \n", res)
fmt.Printf("Status = %t \n", status)
}We write a function to split a string into a prefix and a suffix once we see given byte.
func splitAt(x string, b byte) (string, string) {
var r1, r2 []byte
suffix := false
for i, _ := range x {
if !suffix {
if x[i] == b {
suffix = true
} else {
r1 = append(r1, x[i])
}
} else {
r2 = append(r2, x[i])
}
}
s1 := (string)(r1)
s2 := (string)(r2)
return s1, s2
}We use slices r1 and r2 to collect the prefix and suffix. We check if we have seen the given element b yet, and then append elements in the original string to either r1 or 2. The element b is not added.
We require slices as append only operates on slices. As we wish to retrieve the result as strings, we need to include a type conversion.
Function splitAt returns two results, the prefix and the suffix. Multiple return results can be retrieved as follows.
Simple (increment) function.
Functions can be arguments.
func apply(i int, f func(int) int) int {
return f(i)
}
func mapInt(f func(int) int, xs []int) []int {
ys := make([]int, len(xs))
for i, e := range xs {
ys[i] = f(e)
}
return ys
}
func execute(f func()) {
f()
}Point to note:
func apply ... declares that we define a function
with name apply
f func(int) int declares that parameter
f is of function type
func(int) int is a function type where the input is
an int and the output is an int as
well
Functions can be return values.
func hello() {
fmt.Print("Hello \n")
}
func hello2(x int) func() {
fmt.Printf("%d ", x)
return hello
}package main
import "fmt"
func inc(i int) int { return i + 1 }
func square(i int) int { return i * i }
func apply(i int, f func(int) int) int {
return f(i)
}
func execute(f func()) {
f()
}
func hello() {
fmt.Print("Hello \n")
}
func hello2(x int) func() {
fmt.Printf("%d ", x)
return hello
}
// Create a new slice.
func mapInt(f func(int) int, xs []int) []int {
ys := make([]int, len(xs))
for i, e := range xs {
ys[i] = f(e)
}
return ys
}
// In-place update. Result refers to xs.
func mapIntWithSideEffect(f func(int) int, xs []int) []int {
for i, e := range xs {
xs[i] = f(e)
}
return xs
}
func main() {
execute(hello)
execute(hello2(2))
fmt.Printf("%d \n", apply(1, inc))
xs := []int{1, 2, 3}
ys := mapInt(inc, xs)
zs := mapInt(square, xs)
fmt.Printf("%d %d %d \n", xs[1], ys[1], zs[1])
}Anonymous functions are function without a name. They originate from the lambda calculus and therefore we refer to anonymous functions also as lambda funtions.
Function definitions so far.
Go supports anonymous functions (lambdas).
Almost same syntax as for function definitions. We simply drop the function name.
How can we use lambda functions?
Instead of
we can also write
Advantage:
Define and use a lambda function “in place”
Lambda functions can be defined anywhere and may refer to local variables
Consider
func() result is a parameter-less function with not
result value
func () { fmt.Printf("Hello %d \n", x) } lambda
function where we refer to the local variable x
package main
import "fmt"
func apply(i int, f func(int) int) int {
return f(i)
}
func execute(f func()) {
f()
}
func hello() {
fmt.Print("Hello \n")
}
func hello2(x int) func() {
fmt.Printf("%d ", x)
return hello
}
// Create a new slice.
func mapInt(f func(int) int, xs []int) []int {
ys := make([]int, len(xs))
for i, e := range xs {
ys[i] = f(e)
}
return ys
}
func main() {
execute(hello)
execute(hello2(2))
fmt.Printf("%d \n", apply(1, func(i int) int { return i + 1 }))
xs := []int{1, 2, 3}
ys := mapInt(func(i int) int { return i + 1 }, xs)
zs := mapInt(func(i int) int { return i * i }, xs)
fmt.Printf("%d %d %d \n", xs[1], ys[1], zs[1])
}Recall
Consider
f(1) yields a function of type
func(int) int
This function is effectively the increment function
Consider
The call (f(2))(3) calculates
2 + 3
First we call f(2)
This yields function of type func(int) int
Hence, we call this function with parameter 2
f(2) is an example of a partial function
application (because we obtain another function as the result).
To avoid clutter, the convention adopted in Go (and other languages that support higher-order functions) is to allow removal of parentheses by assuming that
function application is left-associative
function types are right-associative
Compare
versus
Both are the same!
func(int) func(int) int = func(int) (func(int) int)
because function types are right-associative
f(2)(3) = (f(2))(3)
because function application is left-associative
package main
import "fmt"
func add(x int) func(int) int {
return func(y int) int { return x + y }
}
func main() {
var f func(int) (func(int) int)
f = add
inc := f(1)
fmt.Printf("\n %d", inc(3))
x := (f(2))(3)
fmt.Printf("\n %d", x)
var g func(int) func(int) int
g = add
y := g(2)(3)
fmt.Printf("\n %d", y)
}Consider
The arguments to plus must be both present.
In Go, it’s possible to ‘incrementally’ supply addition with its arguments.
Function add expects an integer argument (left operand)
and yields a function. This function expects another integer argument
(right operand) and then yields the expected result.
How to transform one function into the other? See below
curry and uncurry functions.
“Curried style”
Arguments can be supplied incrementally
Allows for a more flexible use of functions
“Uncurried style”
All arguments must be present
Generally less run-time overhead
package main
import "fmt"
func plus(x int, y int) int {
return x + y
}
func add(x int) func(int) int {
return func(y int) int {
return x + y
}
}
func curry(f func(int, int) int) func(int) func(int) int {
return func(x int) func(int) int {
return func(y int) int {
return f(x, y)
}
}
}
func uncurry(g func(int) func(int) int) func(int, int) int {
return func(x int, y int) int {
return (g(x))(y)
}
}
func main() {
x1 := plus(1, 2)
x2 := (add(1))(2)
p := uncurry(add)
x3 := p(1, 2)
p2 := curry(plus)
x4 := (p2(1))(2)
fmt.Printf("%d %d %d %d", x1, x2, x3, x4)
}What’s the output of the following program?
package main
import "fmt"
type Type0 func()
func main() {
var fn Type0
var x int = 2
fn = func() { fmt.Printf("%d \n", x) }
fn()
x = 3
fn()
}For each call to fn there is a different result!
Unlike pure functional programming languages, the Go language is impure. Pure means that for each function call and the same input arguments, we obtain the same output. This is also referred to as referential transparency.
Introduces a new type named myint. Values of type
int and myint cannot be mixed.
The type myint is structurally isomorphic to
int, so we can (type) convert one into the other.
Here is the complete example.
package main
import "fmt"
type myint int
func main() {
var i int
var j myint
j = 1
i = (int)(j)
// i = j
// yields a type error
j = (myint)(i)
fmt.Printf("%d", i)
}Defines a named type rectangle that is structurally
isomorphic to a struct of two ints where both ints can be accessed via
field labels.
Construction of values.
var r1 rectangle = rectangle{1, 2}
var r2 rectangle = rectangle{width: 2, length: 1}
r3 := rectangle{width: 2, length: 1}Either by position or by field reference.
Control structures
Arrays, slices
Functions
func add(x int) func(int) int {
return func(y int) int { return x + y }
}
// func add ... Function declaration, I'm a function with name add
// func(int) int Function type, from int to int
// func(y int) int { ...} Anonymous function, lambda binder and bodyFunction application is left-associative.
(add(1))(2) versus add(1)(2)
Functions types are right-associative.
func(int) (func(int) int) versus f func(int) func(int) int
Connection to OO. In Go functions are first-class (that is, they can appear anywhere). This is similar to OO where objects are first-class.
For example, in an OO language, we can call a method m1
on some object o1. The result is an object on which we call
another method m2.