New slides about Go

bearophile bearophileHUGS at lycos.com
Thu Oct 14 15:06:50 PDT 2010


Found through Reddit, talk slides by Rob Pike, "The Expressiveness of Go":
http://go.googlecode.com/hg/doc/ExpressivenessOfGo.pdf

http://www.reddit.com/r/programming/comments/dr6r4/talk_by_rob_pike_the_expressiveness_of_go_pdf/

This time I think I have understood most of the contents of the slides :-)


Few interesting quotations:

>From Page 18:

There are pointers but no pointer arithmetic
  - pointers are important to performance, pointer arithmetic not.
  - although it's OK to point inside a struct.
    - important to control layout of memory, avoid allocation
Increment/decrement (p++) are statements, not expressions.
  - no confusion about order of evaluation
Addresses last as long as they are needed.
  - take the address of a local variable, the implementation
    guarantees the memory survives while it's referenced.
No implicit numerical conversions (float to int, etc.).
  - C's "usual arithmetic conversions" are a minefield.


>From page 19 and 20:

Constants are "ideal numbers": no size or sign, hence no L
or U or UL endings.

Arithmetic with constants is high precision.  Only when 
assigned to a variable are they rounded or truncated to fit.

A typed element in the expression sets the true type of the constant.


>From page 40:

Goroutines have "segmented stacks":
   go f()
starts f() executing concurrently on a new (small) stack.
Stack grows and shrinks as needed.
No programmer concern about stack size.
No possibility for stack overflow.
A couple of instructions of overhead on each function call, a 
huge improvement in simplicity and expressiveness.


>From page 46:

The surprises you discover will be pleasant ones.

--------------------

Some comments:

- In my D programs I sometimes use pointers, but pointer arithmetic is indeed uncommon.
- Turning x++; into statements seems harsh, but indeed it solves some problems. In practice in my D programs the ++ is often used as a statement, to avoid bugs.
- I think that "take the address of a local variable, the implementation guarantees the memory survives while it's referenced." means that it gets copied on the heap.
- Constants management in Go: seems cute.
- Segmented stack: allows to avoid some stack overflows at the price of a bit of delay at calling functions.
- The comment from 46 refers to a language that is orthogonal, and I think it is probably very correct. It's one of the main advantages of an orthogonal design, you are free to create many combinations.

--------------------

On the Go site there is a "playground", similar to what Ideone and Codepad sites offer for D2/D1. It contains some small programs, and you may modify them and compile almost arbitrary Go code.


A little Go example in the playground shows closures and the comma/tuple syntax similar to Python one:


package main

// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return b
	}
}

func main() {
	f := fib()
	// Function calls are evaluated left-to-right.
	println(f(), f(), f(), f(), f())
}


Something similar in D2, D lacks a handy unpacking syntax, and I think currently it doesn't guarantee that functions get evaluated left-to-right:


import std.stdio: writeln;

int delegate() fib() {
    int a = 0;
    int b = 1;
    return {
        auto a_old = a;
        a = b;
        b = a_old + b;
        return b;
    };
}

void main() {
    auto f = fib();
    // function calls are not surely evaluated left-to-right
    writeln(f(), " ", f(), " ", f(), " ", f(), " ", f());
}



Another example on the Go site, that shows the segmented stacks at work:


// Peano integers are represented by a linked list
// whose nodes contain no data (the nodes are the data).
// See: http://en.wikipedia.org/wiki/Peano_axioms

// This program demonstrates the power of Go's
// segmented stacks when doing massively recursive
// computations.

package main

// Number is a pointer to a Number
type Number *Number

// The arithmetic value of a Number is the count of
// the nodes comprising the list.
// (See the count function below.)

// -------------------------------------
// Peano primitives

func zero() *Number {
	return nil
}

func isZero(x *Number) bool {
	return x == nil
}

func add1(x *Number) *Number {
	e := new(Number)
	*e = x
	return e
}

func sub1(x *Number) *Number {
	return *x
}

func add(x, y *Number) *Number {
	if isZero(y) {
		return x
	}
	return add(add1(x), sub1(y))
}

func mul(x, y *Number) *Number {
	if isZero(x) || isZero(y) {
		return zero()
	}
	return add(mul(x, sub1(y)), x)
}

func fact(n *Number) *Number {
	if isZero(n) {
		return add1(zero())
	}
	return mul(fact(sub1(n)), n)
}

// -------------------------------------
// Helpers to generate/count Peano integers

func gen(n int) *Number {
	if n > 0 {
		return add1(gen(n - 1))
	}
	return zero()
}

func count(x *Number) int {
	if isZero(x) {
		return 0
	}
	return count(sub1(x)) + 1
}

// -------------------------------------
// Print i! for i in [0,9]

func main() {
	for i := 0; i <= 9; i++ {
		f := count(fact(gen(i)))
		println(i, "! =", f)
	}
}




It's easy to translate it to D:

import std.stdio: writeln;

struct Number {
    Number* next;
    this(Number* ptr) { next = ptr; }
}

// -------------------------------------
// Peano primitives

Number* zero() {
    return null;
}

bool isZero(Number* x) {
    return x == null;
}

Number* add1(Number* x) {
    return new Number(x);
}

Number* sub1(Number* x) {
    return x.next;
}

Number* add(Number* x, Number* y) {
    if (isZero(y))
        return x;
    return add(add1(x), sub1(y));
}

Number* mul(Number* x, Number* y) {
    if (isZero(x) || isZero(y))
        return zero();
    return add(mul(x, sub1(y)), x);
}

Number* fact(Number* n) {
    if (isZero(n))
        return add1(zero());
    return mul(fact(sub1(n)), n);
}

// -------------------------------------
// Helpers to generate/count Peano integers

Number* gen(int n) {
    if (n <= 0)
        return zero();
    return add1(gen(n - 1));
}

int count(Number* x) {
    if (isZero(x)) {
        return 0;
    }
    return count(sub1(x)) + 1;
}

// -------------------------------------

void main() {
    foreach (i; 0 .. 11) {
        int f = count(fact(gen(i)));
        writeln(i, "! = ", f);
    }
}


But compiled normally on Windows leads to a stack overflow, you need to add a -L/STACK:10000000

Bye,
bearophile


More information about the Digitalmars-d mailing list