Go Language: Difference between revisions
Line 364: | Line 364: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Returning a Functions in a Function== | ==Returning a Functions in a Function== | ||
This | This allows you to return a function (like maybe delegates) | ||
<syntaxhighlight lang="go"> | <syntaxhighlight lang="go"> | ||
func main() { | func main() { | ||
Line 372: | Line 372: | ||
} | } | ||
func MathExpression() func(float64,float64) | func MathExpression() func(float64,float64) float64 { | ||
return (f1, f2 float64) float64 { | return (f1, f2 float64) float64 { | ||
return f1 + f2 | return f1 + f2 | ||
} | } | ||
} | |||
</syntaxhighlight> | |||
==Passing a Functions as an Argument== | |||
This allows you to pass function to a function | |||
<syntaxhighlight lang="go"> | |||
func foo(f1, f2 float64, mathExpr func(float64,float64) float64) float64 { | |||
return 2 * mathExpr(f1,f2 | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 01:04, 20 August 2020
Introduction
Characteristics
Google was using c++, java and python.
- c++ high performance, type safety, slow compile, complex
- java rapid compile, type safety, complicated eco system
- python easy to use, lack of type safety, slow
So Go was created which had
- Fast compilation
- Fully compiled, better performance
- Strongly typed
- Concurrent by default, threaded
- Garbage collected
- Simplicity as a core value, for example Garbage collected, Strongly typed.
Whats Go Good At?
Good
- Go is easy to learn
- Easy concurrent programming with goroutines and channels
- Great standard library
- Go is performant
- Language defined source code format
- Standardized test framework
- Go programs are great for operations
- Defer statement, to avoid forgetting to clean up
- New types
Bad
- Go ignored advances in modern language design
- Interfaces are structural types
- Interface methods don't support default implementations
- No enumerations
- The := / var dilemma
- Zero values that panic
- Go doesn't have exceptions. Oh wait... it does!
Ugly
- The dependency management nightmare
- Mutability is hardcoded in the language
- Slice gotchas
- Mutability and channels: race conditions made easy
- Noisy error management
- Nil interface values
- Struct field tags: runtime DSL in a string
- No generics... at least not for you
- Go has few data structures beyond slice and map
- go generate: ok-ish, but...
Hello World
package main
import (
"fmt"
)
// Comments are here
func main() {
fmt.Println("Hello World")
}
Modules
Modules are files for controlling a project. To create a module
go mod init github.com/bibble235/webservice
To run our code
go run github.com/bibble235/webservice
Data Types
Boolean types
They are boolean types and consists of the two predefined constants: (a) true (b) false
Numeric types
They are again arithmetic types and they represents a) integer types or b) floating point values throughout the program.
Integer types
- uint8 Unsigned 8-bit integers (0 to 255)
- uint16 Unsigned 16-bit integers (0 to 65535)
- uint32 Unsigned 32-bit integers (0 to 4294967295)
- uint64 Unsigned 64-bit integers (0 to 18446744073709551615)
- int8 Signed 8-bit integers (-128 to 127)
- int16 Signed 16-bit integers (-32768 to 32767)
- int32 Signed 32-bit integers (-2147483648 to 2147483647)
- int64 Signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
Floating types
- float32 IEEE-754 32-bit floating-point numbers
- float64 IEEE-754 64-bit floating-point numbers
- complex64 Complex numbers with float32 real and imaginary parts
- complex128 Complex numbers with float64 real and imaginary parts
Other Numeric types
- fbyte same as uint8
- frune same as int32
- fuint 32 or 64 bits
- fint same size as uint
- uintptr an unsigned integer to store the uninterpreted bits of a pointer value
String types
A string type represents the set of string values. Its value is a sequence of bytes. Strings are immutable types that is once created, it is not possible to change the contents of a string. The predeclared string type is string.
Derived types
They include (a) Pointer types, (b) Array types, (c) Structure types, (d) Union types and (e) Function types f) Slice types g) Interface types h) Map types i) Channel Types
Declaring
There are 3 way to declare variables
// explicit
var i int
// explicit and assign
var i = 42
// Implicit
firstname := "Iain"
Pointers
We can create pointers with the "*"
var firstName *string // Nil
Assigning pointers is the same as c++ however it does not support pointer arithmathic
var firstName *string = new(string) // Nil
*firstName = "Arthur"
fmt.Println(*firstName)
Dereferencing is the same as c++ too.
var firstName *string // Nil
Assigning pointers is the same as c++ however it does not support pointer arithmathic
firstName := "Arthur"
ptr := &firstName
Constants
Introduction
Constant are available in go.
const pi = 3.1415
// types are not explicit so
const c = 3
fmt.Println(c + 1.2)
// Will work because there is not type and 1.2 can be added to c if c is deemed a float at runtime.
Iota
We can create consts like so
const (
fred1 := "fred1"
fred2 := 2
)
Using iota we can we can increment the values using an expression. e.g.
const (
fred1 = iota
fred2 = iota
)
// Outputs 0, 1
But we can put expression in this.
const (
fred1 = iota + 6
fred2 = 2 << itoa
)
// Outputs 6, 4
Omitting the expression after first declaration will mean the first expression is repeated. Iota is scoped to the block of code
const (
fred1 = iota + 6
fred2
)
// Outputs 6, 7
Collections
Arrays
With go all the elements must have the same type. The type is declared at the end. They are zero based. I.E. the first array is at index 0. You cannot increase the size dynamically.
var arr [3]int
arr[0] = 1
arr[0] = 2
arr[0] = 3
// Better with initializer
arr2 := [3]int {1,2,3}
Slices
Introduction
The slice is a dynamic type which can be derived/linked from an array
arr2 := [3]int {1,2,3}
slice := arr[:]
arr[1] = 42
slice[2] = 27
// output [1,42,27]
Appending
We can create a slice from an anonymous array and append
slice := []int {1,2,3}
slice = append(slice, 4,42,27)
// output [1,2,3,4,42,27]
Ranges
We can slice a slice
slice := []int {1,2,3}
slice = append(slice, 4,42,27)
s2 := slice[1:] // Element 1-end
s3 := slice[:2] // Element 0-2 Exclusive 0,1 not 2
s4 := slice[1:2] // Element 1-2 Exclusive 1 not 2
Map
Maps are like dictionaries. They associate a key with a value in a collection
m := map[string]int {"foo":42,"foo2":42}
fmt.Println(m["foo"])
// Deleting uses delete function
delete(m, "foo")
Structs
Introduction
Structs field definitions are fixed at compile time like c++ structs. They are defined using the word type. The fields are initialised to their zero value
// Define
type use struct {
ID int
FirstName string
LastName string
}
// Declare
var myUser user
// Initialisation like a dictionary
// Add comma on last line for multi-line
u2 := user {
ID:1,
FirstName: "Iain",
LastName: "Wiseman",
}
// Assign
myUser.ID = 1
myUser.FirstName = "Iain"
myUser.LastName = "Wiseman"
Functions
Basics
No surprises here except the symbol name is first
func test(inPort int, inNumberOfReries) bool {
return true
}
isDone = test(1701, 3)
Multiple Parameters
We can pass multiple parameters to as function and similar we can return multiple parameters for GO it is typical to return the error type back. Note consecutive input parameters same type can be comma delimited
func test(inParm1, inParam2 int, inNumberOfReries) (int, error) {
return 300, nil
}
Returning error
In GO it is typical to return a result and an error object. Here is the divide by zero in GO.
func divide(inParm1, inParam2 float64) (float64, error) {
if p2 == 0 {
return math.NaN(), errors.New("Cannot divide by zero")
return (p1/p2, nil)
}
Variadic Parameters
Easy pezzy lemon squezzy. Just gets passed as an array. Like c# this must be the last parameter.
func sum(values ...float64) float64 {
total := 0.0
for _, value := range values {
total += value
}
return total
}
Naming Return Variables
If you name the return parameters you can have a return with no parameters.
func test(p1, p2 float64) (answer float64, err error) {
answer = p1 / p2
return
}
Methods
Methods are just function on structs. Note the pointer, this is called a pointer receiver.
type Person struct {
ID id
Name string
}
func (p* Person) Test(param1, parm2 string) (int, test) {
return (20, nil)
}
Public and Private
Functions a can be made public by capitalising the first character.
func fooPrivate() bool {
return true
}
func FooPublic() bool {
return true
}
Anonymous Functions
We can create these within a function
func Foo() bool {
func() {
println("Hello World");
}()
}
We can assign and invoke too
func Foo() bool {
x := func(name string) {
println(name);
}
x("Function 1");
x("Function 2");
}
Returning a Functions in a Function
This allows you to return a function (like maybe delegates)
func main() {
addExpression := MathExpression()
println(addExpression(2,3))
}
func MathExpression() func(float64,float64) float64 {
return (f1, f2 float64) float64 {
return f1 + f2
}
}
Passing a Functions as an Argument
This allows you to pass function to a function
func foo(f1, f2 float64, mathExpr func(float64,float64) float64) float64 {
return 2 * mathExpr(f1,f2
}
Types
Introduction
We can either have alias or type definition. These are the same as typedef and class in C++
Type Alias
Type alias allow you to typedef a type but you cannot extend an alias.
type iains_type = string
Type Definition
This makes a thing is own type
type iains_type string
Embedding Type struct
In GO you can embed a type struct into another type.
type Name struct {
first string
last string
}
type Person struct {
Name
twitterHandler TwitterHandler
}
Embedding Type interfaces
In GO you can embed a type interface into another type.
type Identifiable interface {
ID() string
}
type socialSecurityNumber string
func NewSocialSecurityNumber(value string) Identifier {
return socialSecurityNumber(value)
}
func (ssn socialSecurityNumber) ID() string {
return string(ssn)
}
type Person struct {
Name,
twitterHandler TwitterHandler,
identifiable
}
func NewPerson(firstName, lastName string, identifiable Identifiable) Person {
return Person{
Name: Name {
first: firstName,
last: lastName,
},
Identifiable: identifiable
}
}
Type Equality
Equality with Basic structs
Standard approach for structs
name1 : = Name{First: "James", Last: "Wilson"}
name2 : = Name{First: "James", Last: "Wilson"}
if name1 == name2 {
// Success
}
type Name struct {
First string
Last string
}
Equality with Non Basic Structs
Adding a other types will not be the case. e.g. func or map
type Name struct {
First string
Last string
foo func()
}
type Name struct {
First string
Last string
Middle map[string]string
}
Equality with Empty Types
We can compare to empty types
if name1 == (Name{}) {
// Blah blah
}
Equality Overloads
We can overload with our own function
func (n Name) Equals(otherName Name) bool {
return n.First == other.First && n.Last == other.Last
}
Equality with Interfaces
This works based on the type which is returned within the method being compared
ssn :== organization.NewSocialSecurityNumber("123-123")
eu :== organization.NewEuroNumber("123","France")
eu2:== organization.NewEuroNumber("123","France")
if ssn == eu {
fmt.println("Fails but compiles")
}
if eu == eu2 {
fmt.println("Success")
}
Classes (Or not)
Introduction
package controllers
import (
"net/http"
"regexp"
)
// Class Attributes
type userController struct {
userIDPattern *regexp.Regexp
}
// Function for class
func (uc userController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from the User Controller!"))
}
// Constructor with default arguments
func newUserController() *userController {
return &userController{
userIDPattern: regexp.MustCompile(`^/users/(\d+)/?`),
}
}
Fields
By making the fields lowercase in a struct they become private. We can initialize them using the constructor method usually new<typename>(param1, param2 string) typename
func NewPerson(firstName, lastName string) Person {
return Person {
firstName: firstName,
lastName: lastName,
}
}
Getters And Setters
The course advice to have pointer arguments to functions for the object. So example of getters and setters for the above is.
func (p *Person) SetTwitterHandler(handler string) error {
p.twitterHandler = handler
return nil
}
func (p *Person) TwitterHandler() string {
return p.twitterHandler
}
Interfaces
The Go language does not require you to declare support for interfaces. Provided the type you pass has the functionality then it supports the interface. In the above object (hmmm class) the type provides an function called ServeHTTP(RepsonseWriter, *Request) and therefore implements the interface type Handler. (see https://golang.org/pkg/net/http/#Handler)
To define an interface you just need to define your interface.
package organization
type Identifiable interface {
ID() string
}
Any object (hmmm class) which implements ID() string is an interface
type Person struct {
}
func (p Person) ID() string {
return "12345"
}
Workflow
Looping
Introduction
All loops in GO are for loops. There are four types of for loops. GO supports break and continue.
Loop till condition
No surprises, prints 5 times
var i int
for i < 5 {
i++
}
Loop till condition With Post clause
We have the traditional loop. It takes the form
var i int
for i = 0;i < 5; i ++ {
fmt.printLn(i)
}
// Also valid
for ;i < 5; i ++ {
fmt.printLn(i)
}
Infinite loop
Happy looping.
for {
fmt.printLn(i)
}
// Also valid
for ; ; {
fmt.printLn(i)
}
Loop over collections
This is like a foreach
slice := []int{1,2,3}
for i, v := range slice {
fmt.printLn(i, v)
}
// Output index, value or
/ 0 1
//1 2
//2 3
// Using a Map
wellKnownPorts := map[string]int{"http":80, "https":443, "ssh":22}
for k, v := range wellKnownPorts {
fmt.printLn(k, v)
}
// Output key, value or
// http 80
// https 443
// ssh 22
// Only Keys
wellKnownPorts := map[string]int{"http":80, "https":443, "ssh":22}
for k, _ := range wellKnownPorts {
fmt.printLn(k)
}
// Only Values
wellKnownPorts := map[string]int{"http":80, "https":443, "ssh":22}
for _, v := range wellKnownPorts {
fmt.printLn(v)
}
Branching
If Statements
Example is provided below
if u1.ID == u2.ID {
// Blah
} else if u1.FirstName == u2.FirstName {
} else {
}
Switch Statements
With GO switch statements there is no fall through.
switch r.Method {
case "GET":
println("Get Request")
case "DELETE":
println("Delete Request")
case "POST":
println("Post Request")
default:
println("Default Request")
}
We can add a expression and switch on type
func foo(id interface{}) string
switch v := id.(type) {
case string:
return v
case int:
return strconv(v)
...
}
Exceptions
We don't have exceptions but we do have panics.The panic function stops the execution for the current function
func test() {
fmt.println("Fred Was Ere")
panic("was err")
fmt.println("Fred has left the building")
}