Go: General Concepts
Go is a Strongly Typed, Compiled, programming language that supports essential Object Oriented concepts without imposing a rigid, heavy-weight, OOD hierarchy.
The Golang Empty Interface can serve as a kind of Generic:
// GoLang generic // The empty interface specifies that any type may be passed func golangGeneric(val interface{}) { fmt.Println(val) }Export from files and Modules using capitalized Variable Names.
Use
make()for Arrays and Maps:nums := [][]int{{1,1},{0,0},{2,1},{100,200},{15,244}} a := make([]int, 5) var animals = make(map[string]string)Go has no Ternary Operator.
Variables can be declared in two ways:
var:- Can be used in Global Scope (at the top of a Module/Package).
- Optional declared Type.
var a = 900:=- Short Variable Declaration
- Can only be declared in some local Scope.
b := 100
Go: Object Oriented Design
Go uses Structs instead of Classes.
They are most akin to a kind of mutable Java Record or JavaScript Object IMO.
They lack internal Functions ("true Methods") and Constructors like in many other languages.
Inheritance
Go does not have pure Inheritance. It supports Weak Composition and Embedding:
type Animal struct {
name string
}
// Weak Inheritance by Composition
type Mammal struct {
Animal
}
// As a field
type Reptile struct {
animalParts Animal
}
Polymorphism
Polymorphism is achieved through flexible Structs where an attribute plays the role of a Type.
Receiver Functions
type Animal struct {
animalName string
foodEaten string
locomotionMethod string
spokenSound string
}
func (a Animal) Eat() {
fmt.Println(a.foodEaten + "\n")
}
func (a Animal) Move() {
fmt.Println(a.locomotionMethod + "\n")
}
func (a Animal) Speak() {
fmt.Println(a.spokenSound + "\n")
}
Methods (Functions that live within a Class or Instance) are implemented by Receiver Function pattern.
Visibility
Encapsulation is obtained and Access controlled through several techniques and in-built properties of the language:
- Export from files and Modules using capitalized Variable Names. (Non-capitalized Variable Names will not be exported - are functionally private.)
- Fields on Structs can be read and altered using familiar dot-notation:
p.X + p.Y + p.Z.
Examples
type Animal struct {
animalName string
foodEaten string
locomotionMethod string
spokenSound string
}
cow := Animal{"cow", "grass", "walk", "moo"}
bird := Animal{"bird", "worms", "fly", "peep"}
snake := Animal{"snake", "mice", "slither" ,"hsss"}
// Note this is a weakly OOP language
// Note the lack of inheritance and constructors
type Point struct {
X float32
Y float32
Z float32
}
// Pseudo constructor/factory
// Note capitalized name indicates "to export"
func NewPoint(X float32, Y float32, Z float32) *Point{
// Partially initialize object
p := Point{X:X, Y: Y}
p.Z = Z
// Return pointer
return &p
}
// Pseudo-class methods
// Call by value
// Copy of value made
func AddCoordinates(p Point) float32 {
return p.X + p.Y + p.Z
}
// Receiver type function
// This is also how visibility is controlled:
// By exporting receiver methods but limiting exporting of structs (by using lower-case names)
func (p Point) AdditionReceiver () float32 {
return p.X + p.Y + p.Z
}
Resources and Links
Code samples:
Go: Asynchronous Programming
Go uses WaitGroups, go routines, and the defer keyowrd to make both Multi-Threaded and Asynchronous Programming possible.
func dine(wg *sync.WaitGroup, philosophers []*Philosopher) {
defer wg.Done()
//...
wg := new(sync.WaitGroup)
wg.Add(1)
go dine(wg, philosophers)
wg.Wait()
Channels
The output of a go routine can be sent to a Go Channel:
func sortAsync(wg *sync.WaitGroup, arr []int, c chan []int) {
defer wg.Done()
fmt.Printf("Go begin: %v \n", arr)
for i := 0; i < len(arr) - 1; {
if arr[i] > arr[i+1] {
orig := arr[i]
arr[i] = arr[i+1]
arr[i+1] = orig
i = 0
} else {
i++
}
}
c <- arr
fmt.Printf("Go end: %v \n", arr)
}
// Create necessary resources
wg := new(sync.WaitGroup)
c := make(chan []int)
arrs := make([][]int, 4)
//...
for i := 0; i < len(arrs); i++ {
wg.Add(1)
fmt.Printf("Sorting: %v \n", arrs[i])
go sortAsync(wg, arrs[i], c)
}
sOne := <- c
Resources and Links
Code samples:
Go: Pointers
Like C++, Go gives explicit control over the use of Pointers.
Useful to control scenarios where we'd encounter: Deep/Shallow Copying, Pass by Value, and Pass by Reference.
Address Of Operator and Dereferencing
var num int = 100
var numAddress *int = &num // declare a variable with pointer type '*int'
// obtain the address of 'num'
var derefNum int = *numAddress // dereference back to the int value from the pointer type
*numAddress = 42 // update the value of dereference to 'numAddress'
Examples
var num int = 100
fmt.Println("pointers > <variable> 'num' has <value>:", num)
var numAddress *int = &num
fmt.Println("pointers > 'numAddress' obtains <pointer> via & <address operator> and <pointer type>: *int = &num ", numAddress)
var derefNum int = *numAddress
fmt.Println("pointers > <derefences> back to the <value> with <dereferencing operator> on 'numAddress': derefNum = *numAddress", derefNum)
fmt.Println("pointers > call & <address operator> on 'derefNum' to obtain <address>: &derefNum", &derefNum)
fmt.Println("pointers > can <dereference> back directly with & and * operators sequentially: *&derefNum", *&derefNum)
*numAddress = 42
fmt.Println("pointers > set <value> on <dereference> of 'numAddress', then get <address> from <pointer type>: *numAddress = 42", numAddress)
fmt.Println("pointers > <dereference> back to <value>: *numAddress ", *numAddress)
pointers > <variable> 'num' has <value>: 100
pointers > 'numAddress' obtains <pointer> via & <address operator> and <pointer type>: *int = &num 0xc0000a4090
pointers > <derefences> back to the <value> with <dereferencing operator> on 'numAddress': derefNum = *numAddress 100
pointers > call & <address operator> on 'derefNum' to obtain <address>: &derefNum 0xc0000a4098
pointers > can <dereference> back directly with & and * operators sequentially: *&derefNum 100
pointers > set <value> on <dereference> of 'numAddress', then get <address> from <pointer type>: *numAddress = 42 0xc0000a4090
pointers > <dereference> back to <value>: *numAddress 42
Resources and Links
Code samples:
Go: Generics
Examples
any Type:
func anyParamTypeOne[T any](x T) T {
return x
}
Empty Interface:
// Everything implements this interface and so
// can be passed successfully here
func golangGeneric(val interface{}) {
fmt.Println(val)
}
With a shared interface that types are implementing:
func exampleNine[TT W](s TT, t TT) {
fmt.Println("reflection > exampleNine", s, t)
}
Resources and Links
Code samples:
Go: Interfaces
Examples
type S interface{}
type W interface {
M()
}
type AA struct {
msg string
}
type BB AA
type CC BB
func (p AA) M() {
fmt.Println(1 + 2)
}
// Each of BB and CC require the implementation method M()
// they do not automatically or implicitly inherit like super()
func (p BB) M() {
fmt.Println(1 + 2)
}
func (p CC) M() {
fmt.Println(1 + 2)
}
- Implicit - no explicit implementation keyword required (such as
implementsin Java).- Similar to an Aggregated Interface implementation pattern in Java.
- https://github.com/Thoughtscript/languages_2024/blob/main/java/src/main/java/thoughtscript/io/review/InterfaceAggregationTest.java
- Above,
AA,BB,CCimplement bothSandW. - If
BB,CCdid not implementM()they'd fail to implementSdespite being related toAAdirectly through theirtypedefinitions. M()isn't implicitly inherited (not equivalent tosuper()in other languages) here.Sis effectively the Empty Interface since it lacks any methods to be implemented (and will even allow sayvar X stringto implementS).- Therefore, use an
interfacedefinition likeWfor actual parameter ortypeconstraints.
- Therefore, use an
Resources and Links
Code samples:
Go: Reflection
Allows one to reflect and introspect the properties of a Variable, struct, or type.
Examples
type W interface {
M()
}
var wtype = reflect.TypeOf((*W)(nil)).Elem() // Obtain the type of interface W
func exampleEight(s AA, t any) {
T := reflect.TypeOf(t) // Get the type of argument t
if T.Implements(wtype) { // Ensure that T implements interface W
fmt.Println("reflection > exampleEight", s, t)
} else {
panic("reflection > t doesn't implement W")
}
}
Resources and Links
Code samples: