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

Reply via email to