Go

Declarations Without a Typewriter

Go declarations looked backward to me for about ten minutes:

var retries int
var name string = "worker"
count := 12

Then I noticed how little punctuation I was reading. The name comes first, the type follows when it adds information, and := handles the common local case. My C-trained eyes complained briefly and then found something else to complain about.

The useful distinction is scope, not cosmetic shorthand. := declares at least one new variable in the current block. Assignment remains plain =:

line, err := readLine()
line, err = readLine()

Multiple results make this especially tidy. In C I would pass pointers or return a status and fill a buffer. Here the success value and os.Error can travel together without inventing a result structure.

There is also a practical trap. A short declaration inside an if can create a new variable and leave the outer one untouched. The compiler catches many unused-variable accidents, but it cannot infer my intention. Small scopes help.

This is from the current weekly snapshot, so details may move. The principle already feels sound: write the type when the reader needs it, let the initializer provide it otherwise, and keep declarations close to use. It is not revolutionary. Neither is a sharp screwdriver, and I still prefer one.

From Qt Slots to Small Tools

Most of my systems work still begins in C. If the job needs a user interface, it usually grows a Qt shell. If it needs to be finished before lunch, a Python script appears beside it and quietly becomes permanent.

That arrangement works, but every boundary charges rent. The C tool has ownership rules, the Qt side has objects and signals, and the Python helper has a different deployment story. I have spent an unreasonable amount of time explaining to machines where their libraries live.

I have been trying the experimental Go compiler from the weekly snapshots. It is plainly unfinished, and the language may change under this post, but it occupies an interesting spot: compiled programs, garbage collection, a small syntax, and concurrency that does not begin with a page of pthread ceremony.

My first useful experiment was the sort of filter I normally write in Python:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    in := bufio.NewReader(os.Stdin)
    for {
        line, err := in.ReadString('\n')
        if len(line) != 0 {
            fmt.Printf("%d %s", len(line), line)
        }
        if err == os.EOF {
            break
        }
        if err != nil {
            fmt.Fprintf(os.Stderr, "read: %s\n", err.String())
            os.Exit(1)
        }
    }
}

ReadString may return bytes together with os.EOF, so the line is handled before the error. That keeps a final line without a newline. EOF ends input normally; another os.Error gets reported instead of masquerading as a clean finish.

On my snapshot, I build that directly with 6g and 6l. A later snapshot may spell an import or an I/O call differently; that is currently part of the admission price.

The pleasant surprise is not that this is shorter than C. Python already wins that contest while barely getting out of bed. The surprise is that the result is a small native executable and the source still exposes the mechanics I care about. There is no class hierarchy between the input and me.

I do miss deterministic destruction from C++, especially around Qt resources. Garbage collection is not a substitute for closing a file. Go’s defer looks like the intended answer for many such cases, and I plan to abuse it scientifically.

I am not replacing working C or Qt code. That would be migration as a hobby, a particularly expensive hobby. I am moving the little glue programs first and watching where the new language becomes awkward. So far, it feels less like Python made fast and more like C with several recurring arguments removed.

Go Is an Interesting Experiment

Google announced a new language called Go last week, and I spent an evening rewriting a small concurrent program in it. This is not a review. Go is new and experimental, its tools are young, and its APIs are changing. Anything written today may become an historical document by next month.

The appealing part is its systems-shaped simplicity. It has compiled code, pointers and familiar control structures, but also garbage collection, interfaces, goroutines and channels. Starting concurrent work is cheap in syntax, while channels provide a way to pass values instead of sharing every structure behind a lock.

My first version translated threads directly into goroutines and retained all the old shared state. Unsurprisingly, new spelling did not improve the design. The better version gave one goroutine ownership of the state and sent it requests through channels. The useful idea was ownership, not the keyword.

There are rough edges, incomplete libraries and unanswered questions about performance and deployment. I would not build an important product around today’s interfaces. I would use it for experiments, particularly network services where its concurrency model can be exercised honestly.

I strongly like the direction, with emphasis on direction. Go is not yet a settled platform, and confidence would be silly after one week. Still, it has made me think differently about a program I already understood, which is a fine result for an evening and cheaper than another programming book.