GO(lang) for programmers

home

 

GO, sometimes called golang, is Google's language. It's most unique features are easy-to-create mutexs and producer/consumer queues, and aschycronous functions. It also has Duck-type polymorphism using only lightweight interfaces. Oddly, it doesn't have inheritance-based polymophism.

Surprisingly, GO uses C++ style explicit pointers and heap vs. stack object creation (instead of Reference/Value types). But it's garbage collected. We can't do without that. It's pretty funny to read GO explanations of pointers aimed at users who have only ever used Reference types.

Syntax-wise GO is C/Java-style. But it adds modern tricks -- optional line-endings, slices, implicit tuples -- and some very odd "we just wanted to be different" choices.

It's OK, I guess. But I wouldn't call it good.

Formatting, weirdness

GO borrows from new-style languages by requiring the block {}'s after loops and ifs, and removing the ()'s from their conditions. I don't really mind that. Semi-colons to end statements are optional. A weird side-effect of this is that breaking statements over multiple lines is delicate. They can only break in certain places. Ex's (using a declare):

var n int = 9 // declare and init n

// breaking it like this is an error:
var n int
= 9

// but this way is fine:
var n int =
9

I suppose it's not really a problem since modern editors immediately yell at you until you get the line-break right. And you shouldn't have so mnay long lines anyway.

Onto a really annoying rule: declaring unused variables, or unused anything, is an error. Bleh! This may be fine for finished programs, but it's horrible for writing and debugging. You're contantly having to comment and uncomment everything. GO's official hack/fix uses the dummy (underscore) variable: _=n and _=f(2). Now we've "used" n and f. Oh -- including files you don't need? Also an error. This should be a compiler option, not a rule.

Declaring, types

Declarations use var identifier type. Most langauges with this style use a colon, but not GO. Some simple declarations:

var n1 int
var n2 string = "abc" // initialize
var n3, n4 float32 // multiple vars

In practice, most of your declares will use the implicit type shortcut: leave out var and use := instead of =. Ex's:

n:=6 // same as: var n int = 6
w:="cat" // var w string = "cat"

n:=5 // ERROR -- := is a declaration; we can't redeclare n

GO has type int but wants you to be specific with int32 and int16 and uint8 (byte). It doesn't have float or double -- you have to use float32 or 64.

A char type doesn't exist in GO. Instead, characters are stored as unicode in uint32's. With some casting, we can turn these into length-1 strings (again, GO has no char type). rune is an alias for uint32 (the internet may tell you that rune is GO's version of char -- no, it's not).

GO gives us the "raw" string trick: a quoted string allows escape sequences, but one with backticks, like `a\nb`, doesn't (it's 4 letters long). Because of this, GO programs tend to alternately use single or double quotes to write strings ("cat" or 'cat').

Everything in GO is auto-initialized (even members of structs). var n1 int sets n1 to 0, strings are set to "", and so on.

GO gives us the modern multi-assign (both in and out of declarations):

var a,b int = 10,12 // a=10, b=12
// also legal for a non-declare assign:
a,b=4,5 // a=4, b=5

// multi-assign with inferred-type declare:
a,b := 4,2 // same as: var a,b int = 4,2

// size of list must match:
a,b = callReturningThreeItems() // ERROR -- need to catch all 3
a,b,_ = callReturningThreeItems() // legal, using don't-care

printing/string formats

The built-in Printf has the old C-like formatting: %4.2f and so on. Added tricks are %v as a stand-in for "any type" and %T to print the type:

// formatting:
fmt.Printf("val=%4.2f", 7.0) // 7.00
// %v is a wildcard:
fmt.Printf("show me %v and %v", 5, "arf") // show me 5 and arf
// get the type:
fmt.Printf("type of n is %T", n) // type of n is int32

GO doesn't have mixed string and number math. You can't write "cat"+8. This is a pain. The GO solution is the old string-print function:

// GO's version of n1+" and "+n2:
w=fmt.Sprintf("%v and %v",n1, n2) // "5 and 9"

Arrays

GO arrays are super primative. They can't change their size, which is fine. But the length is a part of the type. Really. A function might take type [5]string, which means only size-5 arrays. That's a crazy limit. Examples of GO arrays:

var A [5]int // A is a length 5 array of ints
aLen := len(A) // 5 (len gets the length)

// proving length is part of the type:
var A [3]int
var B [5]int
A=B // error: A has type [3]int, B has type [5]int

// this sad function takes only a size-5 array as input:
func XY(A [5]int) { ...

Array constants are written the usual way -- {a,b,c}. But a fun shortcut allows listing index:value pairs:

// basic array constant use:
var A [3]int = [3]int{1,2,3}
// using the implied-type shortcut:
B := [5]int {3,6,7,2,5}

// advanced index:value array constant:
var C [6]string = [6]string {1:"cat", "rat" 4:"dog"}
// C is {"","cat","rat","","dog",""}

As you'd guess, values not mentioned in the index:val syntax get the default values. But we also get the trick where "naked" values use successive indexes (1:"cat" followed by just "rat"). This is fun and seems readable.

The system can also figure out the size of constants, but it requires a new funny triple-dot [...] syntax:

// an array constant with a deduced size:
A := [...]string{"hot","cold"} // the system fills in the [2]
Nums := [...]int{4,9,12,67,-45} // becomes [5]int

slices

GO's main list type is named slice. This is confusing since everyone else thinks a slice is a way to get a small read/write window into a list. Well, GO slices also do that. They do both. Whenever you see a GO slice you'll need to figure out which one it is, based on context. Yay!

As lists, the only slice operation we get is "add-to-end". For anything else we need slice tricks (remove-from-end is L=L[:len(L)-1]). But using GO slices as traditional slices is just fine -- we get the usual operations.

Slices are declared as an array with no size, then grown using append. Example of a list-like slice:

Nums := []int{} // length 0 slice

// basic add-to-end (from empty list):
Nums=append(Nums, 3) // Nums is {3}
Nums=append(Nums, 7,9,12) // adds all: {3,7,9,12}

Notice how append isn't self-modifying. It can't be, since the list (which includes the list-pointer) is passed by value. We also can't have v.append(x) since, well, we just can't. So we're stuck with this old-style append function.

We can't even directly add two slices. We need to use a trick where we unpack a slice into variadic (multiple) parameters, using dot-dot-dot:

N=append(N, []int{4,5,6}...)
// same as append(N, 4,5,6)

If you were wondering, GO doesn't allow math-like list-concatentation like N+12 or N1+N2.

Growing a slice works the usual way -- when the backing array gets full it creates and copies into a new twice-as-big one. Creating one with make allows you to specify the starting capacity (which can be found with cap(N)):

// a len-5 slice of strings, with capacity 20:
s1 := make([]string, 5, 20)

// testing:
lenUsed, arrayLen := len(s1), cap(s1) // 5, 20

Part-of-list slices: We can also have traditional part-of-a-list slices. GO uses the usual slice index rule where you select using N[start:endPlusOne]. It allows leaving out the ends, like [:7], but doesn't have the shortcut where [:-2] means all but the last two. Examples of various slices:

// pretend N is [0,1,2,3,4,5,6]
S = N[2:5] // {2,3,4}
S = N[3:4] // {3}
S = N[2:] // {2,3,4,5,6}
S = N[:len(N)-1] // {0,1,2,3,4,5}

GO slices have 0-based index's. That's nice, because not all languages do that.

The point of a slice-like slice is being a read/write window, and it works just fine for that. S[0]=7 changes the underlying slice. But we have to remember not to use append on this type: it overwrites until the underlying array runs out of space, then jumps to a new array:

// N is the base, s1 is a slice from 1 to the end:
N := []int{2,4,6,8}
s1 := N[:3] // {2,4,6}

// s1 knows it has capacity 4 (due to using N):
fmt.Printn("len=%v cap=%v", len(s1), cap(s1)) // len=3 cap=4

// changing s1 also changes original N:
s1[0]=9 // backing array N is now {9,0,0,0}

// append is weird -- it knows N has more room:
s1=append(s1, 5) // N is now {9,0,0,5}

s1 = append(s1, 2) // no change to N (s1 uses new array)

maps

Maps (hash tables) in GO work the usual way. The definition is strange, but it's consistant with the rest of GO:

var m1 map[string]int // type is map of string->int
// NOTE: m1 is nil
// assign using a map constructor:
m1 = map[string]int {"cow":1, "dog":2}

// cheap way to declare a usable empty map:
m2 := map[string]int{} // {} constructs a map, with no items

You may also see people getting a usable empty map with m3:= make(map[string]int). It's just an alternate syntax.

Assigning to one works in the usual way: m1["frog"]=7. Reading is also the usual way n := m1["frog"]. But checking whether an entry exists is done in a novel, clever way -- GO optinally returns a tuple where the second value is a boolean does-it-exist value:

// official way to search for an item in a map:
val, success = m1["frog"] // 7, true

v1, e1 = m1["pony"] // 0, false (assuming no ponies)
val = m1["pony"] // also legal (gives 0 if not found)

Normally you have to catch all returned values in a tuple, but GO map look-ups are an exception.

make is re-used for when you want your new map to have a capacity:

// a map from string->float with room for 50 entries:
m1 := make(map[string]float, 50)

casting, types

Casting uses C-like syntax: TYPE(val), for example int(f) (which drops the fraction).

As noted, GO has no character type, just ints. Examples:

// get a character from a sample string
n := "a"[0]
// it's not a char, just an int:
fmt.Printf("type is %T, val=%v", n, n)
// type is uint8, val=97

// rune is also an integer:
var ch rune = "a" // ch is 97
// ...even when we try to use it like a char:
fmt.Println("%v", "cat"+ch) // cat97

If we really need to display a rune as its actual character, we resort to casting it into a string: string(97), gives string "a".

Another fun fact, the shortcut types don't count as the same type as what they're shortcuts for. int is (probably) int32, but it counts as a different type:

var n1 int = 67
var n2 int16 = 89
var n3 int32 = 72
// int can't mix with int16 or int32, even though it is one:
n1=n2; n1=n3  // ERROR's -- cannot convert ???? to int
n1=int(n2); n1=int(n3) // use standard cast

The same goes for uint mixed with uint16 and uint32 -- different types as far as casting goes

Misc

includes, entry point, files

Every GO file begins, python-style, with that file's package name, like package catAndDog. I think the files may also need to have the same name as the package, or maybe a popular GO tool requires this. I'm not sure.

GO include statements use the keyword import. Things to include are listed in quotes, with the path (if needed):

import "fmt"

// import 3 things in 1 statement:
import ( // parens required
  "time"
  "sync"; "sync/atomic"
)

A simple import merely finds the package. It doesn't put the contents in scope (i.e.: we need to find println using fmt.Println). To pull the contents into your scope, add a dot before the name:

import . "fmt"

Println("dot means we don't need leading fmt")
fmt.Println("in fact, this is an error -- what's fmt?")

As mentioned before, importing something you don't use is an error. GO has a useless fix for this -- add an underscore in front of the filename: import _ "math/rand". You'd think this means "import only if needed", which might actually be useful, but no. It's a funny way to comment out an import.

The main entry point is C-style: func main(), in a package named main. Command line args are a bit funny. You don't change the signature of main to accept arguments. Instead GO puts them in a special global named os.Arg:

package main // official package name for entry point

import "os" // needed to read from os.Args

func main() { // no change, args are sent elsewhere
  numArgs := len(os.Args)
  progName :=os.Args[0] // as usual, 1st input is program name
  ...

Namespaces

Of course, GO doesn't have the namespace keyword or anything like it, since it uses package scope rules. Files are GO's namespaces. But as seen before, items starting with a capital letter are public, while everything else is private to the package.

typedefs, aliases

GO's has typedefs (keyword: type) which is nice. Even better, a typedef'd basic type counts as a new type. This is how it should work -- define catCount as an int and get errors when users improperly use dogCount's (or just ints) when you required a catCount:

// simple typedef for an alternate float32:
type loudType float32

// loadType and float32 don't mix:
var x loudType = 32.6 // this is fine
var f float32 = 45.2
x=f // ERROR -- float32 isn't loudType

We then get typical GO special rules for multiple typedef's in one line:

// pointless rule to define 2 at once (requires parens):
type ( catWeight_t float32; catAge_t int )

Sadly, GO's reverses the "counts as different" rule for compound types. They count as the same, for example a new "slice of strings" type is identical to a slice of strings. That seems confusingly inconsistant:

// an alias for "slice of strings":
type wordList []string
W1 := wordList{"cat","dog"}

// our new wordLists are identical to []string:
var W2 []string = []string{"goat","stoat","boat"}
W1 = W2 // wordList and []string feely mix

A work-around is adding a unique typedef for your basic type and making the compound typedef from that. Fun.

Go has another version of typedef where you never get a type mismatch error. This is done by adding an extra = and is called an "alias":

type size_t=float32 // not a typedef -- an alias!
// can freely mix size_t and float32

The docs say alias's are meant as a temporary hack for re-jiggerring programs. I suspect coders use it by accident in "real" code, mixing with the normal way, causing all sorts of confusion. Or maybe there's a "use of alias" warning.

IF's

GO's if's are mostly normal. Mostly. ()'s around the test are optional while {}'s around the block are required. Not a big deal. But moving into downright strange: an else can't start a new line. The work-around uses the open-curly to start the line:

// simple ifs:
if a==b { sameVal=true }
if n==1 || n==4 || n==8 { n+=5 }

// with an else:
if (a<10) { w="small"
} else { w="big" } // the "else" can't start a line

Go also gives us a new rule for getting a local-to-the-if variable: add a := declare before the test:

// n is a local var in this cascading if:
if n:=cats+dogs; n>10 { 
  fmt.Println("one %v\n", n)
} else if !(n<20) {
  fmt.Println("two %v\n", n)
} else { fmt.Println("three %v\n", n) }

In case you haven't seen this in other new languages, yes, this is a thing -- languages like adding special ways of making if and loop locals.

loops

GO gives us the basic loops: for, while and foreach. As with GO's if-statement, the test has no parens and body's requires curly-braces are required. The for-loop looks normal:

for n:=0;n<10;n++ { fmt.Println(n) } // for loop

n:=0 is the only way to make a local variable -- the long way, var n int = 0, isn't allowed.

While loops are also normal, but they re-use the FOR keyword:

for n<NUM { n*=2 } // while loop

The foreach loop also uses for as the keyword. Better than most, it lets us easily get the index and the value, or just the index or just the value. The range keyword makes it a foreach:

// an index and value foreach loop:
for i,ch := range "abcdef" { ... }
// i runs 0 to 5, ch runs 'a' through 'f'

// index-only foreach:
for i := range "abcde" { if i%2==0 ...

// hack for value-only foreach:
for _,ch := range "abcdef"

It makes a little sense to use "for" as a foreach, and some sense using "for" as a while. But deep down, you just know someone at Google said "hey, wouldn't it be cool if every type of loop used the same keyword?"

functions

GO functions are suprisingly primative. They must begin with the keyword func, which we realized was stupid 40 years ago. They can't be given optional parameters. They can't be overloaded. There are no template functions.

But otherwise, GO functions are normal. Syntax for a sample function:

func addsOne(n int) int { return n+1 }

The return type, which goes after, should be in parens, but that's optional when you have a single return value (so, yes, GO functions can return multiple values).

GO provides a fun shortcut for defining function parameters: multiple parameter declarations can share a type. Here a and b are both ints:

func sum(a,b int) int { return a+b }

As noted, GO functions can also return a tuple. In the prototype, write all of the types in parens, then return them using commas:


func sorted(a, b int) (int,int) {
  if a<b { return a,b }
  return b,a
}

n1,n2 := sorted(7,4)

n1,n2 = sorted(46,83)

Note how tuples are created simply by listing the values with commas between. The usual tuple-parens aren't even allowed. Then note how receiving them is the same multiple-assign syntax. It's an extension of the a,b=3,8 rule. So that's nice.

As a silly shortcut, GO allows us to name the return values (in the header) and assign to those names; with a required empty return to send them. Here result is the assigned-to return variable:

// "result" is declared in the header, simply assign to it:
func maximum(n1, n2 float32) (result float32) {
  if n1>n2 { result=n1 } else { result=n2 }
  return // auto-returns our return-var "result"
}

GO functions can return nothing. To do that, leave the return type empty (in other words, GO doesn't have or need a void keyword):

func abc() { fmt.Println("abc") }

GO has no pass-by-reference. You'll see silly people claiming a leading star creates a PbR parameter (such as num *int), but that's not a special GO rule; it's merely passing a pointer.

As a note, functions taking lists use slices (and not arrays, since they require preset sizes). Calling a slice-taking function with an array uses myArray[:] to convert it to a slice:

func doThingWithList(L []string) string { ... } // slice-taking

w := doThingWithList(myArray[:]) // call with an array

structs/classes

Syntax

GO doesn't use the keyword class, preferring struct. GO classes are typical, but with some odd changes. For one, all members are package scope (public in same file, else private). There's no private keyword and no way to make a member truely private. But you can make members globally public. It's not a keyword, instead, start their names with upper-case letters. That's a stupid choice for a C-like language.

GO also doesn't allow you to write constructors, but gives you 2 free ones. Out-of-the box, structs initialize everything to default values. Or you can use the free value-setting constuctor where you list all values: either annotated with names, or naked in the order they were declared. Constructor examples:

// a sample Point struct (a class):
type Point struct { x,y float32 }

// default-val constructor automatically used here:
var p1 Point // x and y are auto-set to 0

// using 2nd free default constructor:
p1 = Point{7,4} // matches 7 to x, 4 to y

// ...or list fields by name:
p1 = Point{y:5,x:9} // by name, any order
p1 = Point{y:5} // as usual, x is 0

But you can't write your own constructors. You're expected to write normal functions instead (for example, instead of having a string-to-Point constructor Point("7,12"), write a regular point-making function, such as makePoint("7,12")).

member functions

GO fakes member functions in the standard way -- the "caller" is named and treated as a normal parameter inside of the function. This means there's no this or self in GO. It's the same system used by javascript, or C# extension functions. GO member functions are written outside of the class. The calling type is written as a special parameter in parens, in front of the function name. For example, this defines Point::toString:

type Point struct { x,y int } // the Point struct

// define toString as a Point member function:
func (p Point) toString() string {
  return fmt.Sprintf("(%v, %v)", p.x, p.y)
}

We call this like p1.toString(), but p1 becomes a normal parameter, copied into p. We'll need to use p.x and p.y to examine "our" x and y members.

As a fun bonus rule, you can also write GO member functions for your typedefs, even ones for basic types:

type wholeNumber int

// defines wholeNumber.isEven():
func (n wholeNumber) isEven() bool { return n%2==0 }

cats wholeNumber := 7
evenCats := cats.isEven()

Self-modifying mem-funcs

GO's member functions have a weird problem. Since the caller is passed in by value, we can't write self-modifying member functions. We're changing a local copy. This includes useful functions like p.setTo(5,8).

GO's solution is a special syntax to say "use the caller as a pointer". We don't pass it as a pointer, since that's not possible -- GO just does it behind the scenes. Add an extra star before the type:

// a working Point member function which correctly sets x:
//      v---add this star
func (p *Point) setTo(newx int, newy int)  {
  p.x = newx
  p.y=newy
}

// despite new syntax, call it as normal:
p1.setTo(3,2)

There's no reason you can't just slap that star on every member function you write. But I think proper GO uses it to single-out just the self-modifying functions.

Inheritance

GO doesn't use inheritance for polymorphism (it uses Duck-typing), so GO inheritance is pretty simple, but syntactically unique: instead of writing the class(es) to inherit from on top, write them inside the {}'s. Here Sloth inherits from Climber (and has a normal string member, fur):

type Climber struct{ speed float32 }
type Sloth struct {
	Climber
	fur string
}

As usual, Climber's member vars are dumped directly into Sloth's. Sloth gets the s1.speed and s1.fur. But GO seems determinied to suck: to use the built-in constructor you need to navigate the nesting. Here we construct a Sloth; specifying "red" is normal, but to supply a speed of 0.52 we're required to say it comes from Climber:

// sadly, we need to specify the speed comes from Climber:
var s1 Sloth = Sloth{Climber{0.52}, "red"}

Inheriting member functions works as we'd think. Of course the subclass inherits them. We can also rewrite them in the subclass to overwrite. There's no syntax overloading, since it's not needed. There's also none for virtual functions, since interface ducktyping assumes everything is virtual anyway.

GO also naturally allows multiple inheritance: simply list more than one class name to inherit from.

first class and anonymous functions

Like any modern language, GO has variables which can point to functions. Declaring, assigning and using them is straightforward. Then type inference makes it even simpler:

var fp func(int) int // declare
fp = timesTwo // assign
n := fp(7) // 14 // use

// or implicit type:
sd := timesTwo // declare

As function parameters, they look fine:

// search a list using arbitrary compare function:
func find(W []string, w2 string, comp func(string,string) bool) 

The syntax for anonymous functions is sneaky -- write them as normal functions and let GO figure out the anonymous part from context. Here we save an anonymous compare function in variable checkWord:

checkWord := func(w string) bool { return len(w)==3 }

Since func isn't the first thing, this is obviously an anonymous function. Just for fun, here's checkWord used in the standard GO Any function, nothing funny is going on:

listHasLen3String := Any(myStringList, checkWord)

pointers

GO uses explicit pointers, the way a language written for adults should. No reference types here. The syntax is basic C-style: *int is a pointer to an int, *p dereferences, and &n creates a pointer to n. Ex's:

var n int = 8 // normal int var

var p *int // pointer to an int
p = &n // create pointer to n
(*p)+=7 // dereference

p=nil // sadly, GO uses "nil", not the superior null

GO adds the modern shortcut for pointers to members: dogPtr.name implicitly follows the pointer. It's a shortcut for (*dogPtr).name:

d1 := Dog{"spot",8} // a Dog (struct)
dPtr := &d1 // pointer to a Dog

(*dPtr).name+="ted" // official way to use
dPtr.age+=1 // implicit dereference shortcut

Obviously, Go doesn't have the dPtr->age dereference&member operation, since the dPtr.age shortcut is the same but shorter.

new & shortcuts

GO has the standard new operator. Of course, it's optional -- a struct can be local, or new'd to be a heap object. It also adds some shortcuts, so you won't actually type-out new that often. new examples:

var p1 *int // declare p1 as int-pointer, currently nil
p1 = new(int) // new heap int, initted to 0

// declare& init a pointer to a size-5 heap array:
W1 := new([5]string) // W1 points to {"","","","",""} on heap

Note the funny syntax where the type goes in parens. Of course, like all GO vars, the new item starts life initialized to default values. It turns out that's the only way to init them -- new can't use the good constructor.

The shortcut for structs, arrays and slices is to replace new with & and omit the parens. Not much of a shortcut, except the good constructor is once again usable:

p1:=&Point{4,7} // p1 is a *Point to (4,7)
// explicit declare (using the default (0,0)):
var p2 *Point = &Point{}

// pointer to a created len-5 string array:
a1:=&[5]string{} // *a1 is {"","","","",""}

// pointer to a len-5 slice {"","","","","cow"}:
var s1 *[]string = &[]string{4: "cow"}

This trick doesn't work with basic types. You can't have np:=&6 or wp:=&string("abc").

GO then re-uses the ampersand to provide a confusing shortcut to return pointers from functions. Instead of new, you can declare a local variable, return a pointer to it, and GO converts it on-the-spot to a heap variable. For example, this very basic function returns a pointer to a new integer:

func makeInt(n int) *int { n1 := n; return &n1 }
// same as { n1:=new(int); *n1=n; return n1 }

nPtr := makeInt(6) // nPtr is a pointer to heap value "6"

We especially like this trick with local structs, since we get to use the contructor. This function creates a heap Point:

func makeHeapPoint(x,y int) *Point {
  var p Point = Point{x,y} // a regular old local var
  return &p // GO safely moves p to the heap
}

returning functions, closures

Returning functions, including functions with closures works just fine. Here's a basic curried add:

func curryAdd(x int) (func(int) int) {
  return func(y int) int { return x+y }
}

// using it:
add4 := curryAdd(4) // add4 is a function that adds 4
n := add4(93) // n is 97

As usual, the returned function (func(y int) int { return x+y }) just has x bound to whatever it was when we made it (so 4, in our case)).

interfaces

GO interfaces are how it does polymorphism, which is a big surprise, but mechnically GO interfaces don't have many surprises. As usual, they list the required function signatures. We leave off the func keyword since it would be redundant. Here interface hasShow is anything with a show() function and bigInterface is more:

type hasShow interface { show() }

type bigInterface {
  computeSomething(float32, float32) float32
  checkList([]int) bool
}

Very surprisingly, GO uses "light" interfaces, where real classes don't need to mention what interfaces they think they support. Anything with a show() can use the hasShow interface, no extra mark-ups required. Below Cat and Dog are also hasShow's:

// 2 structs:
type Dog struct { name string; age int }
type Cat struct { name string; age int }

// each has it's own version of show:
func (d Dog) show() { fmt.Printf("dog %v is %v years\n", d.name, d.age) }
func (c Cat) show() { fmt.Printf("Cat: %v, %v year\n", c.name, c.age) }

As usual, we can declare a hasShow variable (an interface type) and assign and use it with either concrete type:

var ss hasShow
c1:=Cat(); d1=Dog()
ss=c1; ss.show() // runs the Cat show()
ss=d1; ss.show() // again, but with a dog

Likewise we can send Cats or Dogs to a function expecting a hasShow input:

func multiShow(hs hasShow, times int) { ...
  ...  hs.show()

// pass a Dog into the hasShow parm:
multiShow( Dog{"abc",5}, 2)

// or pass a Cat:
var c1 Cat; c1.name="Princess"; c1.age=6
multiShow(c1, 4) // displays Princess 4 times

In short, GO interfaces work just like any other interface/inheritance system

value and pointer type

In most languages, anything we want to act polymorphically needs to be a pointer. In GO, interfaces are magically both value and pointer types. This has the very minor benefit of making it a tad easier to declare interface variables. Then it has the drawback of being just plain confusing.

Here we declare ss1 as a regular variable (of an interface tyoe), but assign to it as if it were a pointer:

// declare interfaces as normal variables:
var ss1 hasShow

ss1 = &c1 // set to a pointer to a Cat
ss1 = d1 // or assign directly as a value-type (to a Dog)

This is such as odd concept, and is so confusing that surely it must hold some hidden usefulness. It does not. And the rules get worse. Remember back in member functions how we need a different star-syntax for a self-modifying member function? Just in case, here's a quick review:

// Point class and 2 member functions:
type Point struct { x,y int }
func (p Point) toString() { ... }
func (p *Point) changeX(newx int)  { p.x = newx }

GO says that if a class has any star-type members then it must be assigned as a pointer -- like ss1=&varName. Otherwise, you can choose (which I assume means to never use & unless you have to?)

Dynamic casting

It seems odd to see this under interfaces, but dynamic casting is for when you have polymorphism and interfaces are the way GO does that. The direct check "is this actually type X?" looks like: interfaceVariable.(typeToConvertTo). GO-style, it returns the casted object plus the success (and, again, failure gives the default value, even contructing a default struct if needed). Ex's of casting a hasShow into a Cat or Dog:

// this hasShow actually points to a Cat:
var s1 hasShow
s1 = Cat{"felix",7}

// cast into Cat works:
c1,b := s1.(Cat) // Cat{Felix,7}, true

// cast into Dog is legal, and fails:
d1 := s1.(Dog) // Dog{"",0}, (uncaught false)

An example of casting inside an IF with the local assign trick:

func takesAny(v interface{}) {
  if cc,success:=v.(Cat); success {
    // do stuff with the Cat, cc
  }

We also get a special type decoder for switches. Write the actual word "type" in the parens. This checks whether v is a Cat or Dog or something else:

  switch v.(type) {
    case Cat: ...
    case Dog: ...
    default: ...
  }

Asynch, producer/consumer vars

go function keyword

This is a suposed strength of GO, having easy-to-use built-in asychronous features.

Adding leading keyword go to a function call makes it asynchronous -- the caller won't wait for it to finish. Obviously, that only matters for a blocking function. And, of course, we call also call them without go, causing us to wait. Here we call slowPrint -- which blocks using Sleep -- with and without go:

// a function which blocks (for a second):
func slowPrint(w string) {
  time.Sleep(Time.Duration(1000000000) // one second
  fmt.Println(w)
}

// testing it:
  go slowPrint("sp1") // we immediately move to next line
  fmt.Println("A")
  slowPrint("sp2") // wait 1 second for this to finish
  fmt.Println("B")

// output: A (one second delay) sp1 sp2 B

The input to Sleep is of type time.Duration, which is really just uint64. The units are nanoseconds (one billionths of a second). That's why we need 1000000000 to get a 1-second delay. But GO defines time.Millisecond as a million (the number of nanoseconds in a millisecond), letting us write time in the more familiar milliseconds: Sleep(1000*time.Millisecond).

Since we're on time, to get intervals GO has time.Now() and time.Since(TIME_VAR). For example. this loop runs 40 times over 4 seconds:

t1 := time.Now()
// loop runs until 4 seconds have passed:
for time.Since(t1) < time.Duration(4000000000) {
  fmt.Println(int64(time.Since(t1))) // 100,000,000
  fmt.Println(time.Since(t1)) // 100ms
  time.Sleep(100*time.Millisecond)
}

Asynch variables

GO has two sorts of auto-blocking communication variables, which it calls channels. The simplest is a buffer. Filling it is instant, unless it's full -- they you wait. Reading from it is the same, except you only wait when it's empty. A basic buffer.

GO uses make to create them. Give it any size, even 1. Add and remove items using the special <- syntax:

// a new buffer with max size 10:
syncInt := make(chan int, 10)

// fill it with 64 and -47 (no waiting, yet):
syncInt <- 64
syncInt <- -47

var n = <-syncInt // pulls out the 64

Note this is the same make which can create slices and maps. GO chooses weird things to overload.

The other type of channel forces a synch between the sending and reading threads. They're like size 1 buffers, but more strict. Obviously, readers need to wait until someone writes something. But now writing to it makes you wait until someone else wants to read. It's like talking in person instead of leaving a note. The syntax is make, but with no size.

In this example, a thread attempts to write to an empty channel, but is forced to wait until the reader is ready:

// sends an int to a channel then tells us:
func slowSend(intChan chan int, sendMe int) {
  intChan <- sendMe // blocks until someone tries to read!
  fmt.Println("sent")
}

  // start our function sending us a 64:
  iChan := make(chan int)
  go slowSend(iChan, 64)
  fmt.Println("this prints first -- function is blocking on send")

  // wait 5 seconds before reading it:
  time.Sleep(5000*time.Millisecond)
  num := <- iChan
  fmt.Printf("this probably come BEFORE the function prints")

Of course, you can put these in functions and call them with go.

Mutex's

GO has a simple general-purpose mutex. You create variables of that type, and use them for whatever you want. When you call lock it either locks it and moves on, or waits for it to be free if it's already locked (which is basic mutex stuff). Here's an example where we cause an anonymous function to wait on mutex waiter while we pause for 2 seconds before unlocking it:

  var waiter sync.Mutex // a mutex
  // lock it now, for fun:
  waiter.Lock()

  // call anon function which also uses waiter:
  go (func() {
    waiter.Lock() // will wait here for main to Unlock
    fmt.Println("abc")
    waiter.Unlock()
  })()

  // let the function run after 2 seconds:
  time.Sleep(time.Second*2)
  waiter.Unlock()

GO provides some shortcut mutexs, in the atomic package (inside of sync). As we know, simply adding to a number is a load and store, which could be interrupted, so requires a mutex. Instead of making everyone use a mutex to change a number, we get an atomic add. atomic.AddInt32(&n, 3) quickly locks n somehow, adds 3, then unlocks it. atomic also provides a LoadInt32 function for weird set-ups where loading a value might be interrupted.

I feel like in practice, a class will be playing with a few members and you'd just lock write access with a mutex. But I suppose atomic.AddInt allows you to be more granular?

ANY object, dynamic casting

New languages need an "any" object type. Instead of having a list of some base class or interface, you have a list of "any" objects. Of course, you can't use them for anything without manually checking the type and branching. I don't get the appeal, but GO does, and has one of these.

Describing the "any" object is clever -- use an empty interface (interface{}). Everything confirms to that, so you can assign anything. For example:

// "any" type n can be an int:
var n interface{} // n has type "any"
n = 7
n = "cow"

func takeAnyTypeOfInput(n interface{}) { ...

// array of any types (interface{} is the type):
var AA [5]interface{} = [5]interface{}{}

Actually using an "any" type is obviously done through dynamic casting. Note that you're allowed to attemtp to cast into anything (int or string) -- the rule is the original variable must be an interface. Below we search a list of "any"'s, looking for ints and Cats:

func(L []interface{}) int {
  sum:=0 // sum of all integers and cat ages:
  for _,v := range L {
    if num,b := v.(int); b { sum+=num }
    if cc,b := v.(Cat); b { sum+=cc.age }
  }
  return sum
}

LinkedList

GO has a linked-list class, but it's painful to use. Since GO has no template system, it only holds generic objects (interface{}). It also doesn't work with the special foreach. You also need to call a set-up function since GO has no constructors:

import "container/list"

var L1 *list.List // work best as pointers
L1 = list.New() // creates and returns a heap linkList

L2 := list.New() // the easy way

We get the usual pushback and pushfront which, of course, return a pointer to the element they create:

L1.PushBack("cow")
e1 := L1.PushBack("bear")
var e2 list.Element = L1.PushFront("ant")

Once we have an element, we can use InsertBefore, or Remove. We even get functions to move an element to the front, back or next to another element. But without templates, we don't get any sort of Find operations -- we can't search for the word "cow".

These also don't work with GO's foreach. Linked-list loops need to do things the long way:

for e:=L.Front(); e!=nil; e=e.Next() { Println(e.Value) }