wiki:Languages/Go

Go

Go is good, as good as a statically typed C-descendant could be. It has been made by the world's best C and OS experts and it *is* definitely a better C without becoming a "farce" after the "comedy" of C++ (and tragedy of Java).

It was also an effort to reduce the set of feature to a well-defined, most essential and complementary smallest possible set (typed pointers, but no pointer arithmetic is the canonical example). High-order first-class functions, duck-typing and emphasis on structures and interfaces instead of classes gives you almost a statically typed CLOS (without classes).

The comparison with Common Lisp is not arbitrary. Like Common Lisp, Go is a prototyping-and-implementation language (unlike, say, Python, which is a prototyping language or C++, which is an implementation language)

Go is to C what Scheme is to MacLisp. Like Scheme, it is based on a few fundamental philosophical choices which lead to fundamental design decisions, which in turn gave us clarity and conciseness to a language. It has strong emphasis on removing features which could lead to ambiguity, confusion or undefined behavior. It is a product of applied philosophy of reduction to the esense, when there is nothing more to remove (otherwise the complementary set of features, which when done right becomes more than a mere list of features, will be broken).

Go is a better C. Learning Go makes you a better [C] programmer. The same is not true for C++ - leaning C++ makes you worse! The forced discipline and uniformity of Go applied to a plain C will do the trick.

uniform "literal" notation (in-place definition of structured data - like a lambda expression for data)

db := database{"shoes": 50, "socks": 5}

first-class functions (restricted by static typing)

func square(n int) int { return n * n }
f := square
fmt.Printf("%T\n", f)

anonymous functions

fmt.Printf("%T\n", func(x int) int { return x * x })
fmt.Println(strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000"))

lexical closures (restricted by static typing)

func makeAdder(x int) func(int) int {
    return func(n int) int { return n + x }
}

nested procedures (only anonymous procedures could be nested), recursive anonymous procedures (via referencing and re-assignment)

var visitAll func(items []string)

visitAll = func(items []string) {
    for _, item := range items {
        if !seen[item] { 
            seen[item] = true
            visitAll(m[item])
            order = append(order, item)
        }
    }
}

variable number of arguments (of the same type)

func sum(vals ...int) int {
    total := 0
    for _, val := range vals {
        total += val
    }
    return total
}

this treats arguments as a single slice, like in case of a func([]int) int, while the actual type is func(...int) int

func sum(vals ...int) int {
    if len(vals) == 0 {
        return 0
    }
    return vals[0] + sum(vals[1:]...)

splicing operation is v... which extracts elements from a slice or array and uses them explicitly.

    values := []int{1, 2, 3, 4}
    fmt.Println(sum(values...))

strictures

type Tree struct {
    Value       int
    Left, Right *Tree
}

typed pointers and methods

func (t *Tree) Scale(f float64) {
...
}

duck-typing (which is how Mother Nature works)

type I interface {
...
}

and multi-methods on interfaces (which are syntactic sugar for an implicit argument)

func (v I) DoSomething() T {
...
}

and typed channels (for synchronous/blocking message-passing) with explicit select statement

select {
case i := <-c:
    // use i
default:
    // receiving from c would block
}

as the basic building blocks

This is, essentially, a statically typed tiny CLOS without defclass (only structures and interfaces)

Pros:

  • Well-crafted uniform minimalistic syntax.
  • First-Class procedures, High-Order procedures.
  • Duck-Typing via structures and interfaces.
  • Dynamic stack (but no TCO) - no stack overflows in recursion.
  • Methods on types (syntactic sugar for an implicit parameter).
  • Well-crafted standard library (batteries included).
  • Compiles to a statically linked native code with almost no external dependencies.
  • Proper C FFI.
  • Channels (synchronous message passing).

Cons:

  • Static typing (homogeneous containers)
  • func inc(int) int and func inc(int64) int64 cannot coexist.
  • No generics (a separate adder function for each type)
  • mapping as a special case (of a typed mapping)
  • goroutines (cooperative multitasking) - shared stack.

type f1 func(int) int

func compose(f f1, g f1) f1 {
    return func(x int) int {
        return f(g(x))
    }
}
type f2 func(int, int) int

func flip(f f2) f2 {
    return func(x int, y int) int {
        return f(y, x)
    }
}
Last modified 2 years ago Last modified on Nov 4, 2017, 5:06:05 AM
Note: See TracWiki for help on using the wiki.