2013-04-13

Go Conference 2013 Spring

Go Conference 2013 Spring 略して GoCon に行ってきました。

A Tour of Go

公式チュートリアルであるところの A Tour of Go というのがあります。1ページにコードと文書があって、Run ボタンをクリックすると実行結果が見られます。以前にこれを読んだことがあったのですが、よく分からないまま Run ボタンを押して、どんどんページを送っていって、結局何も分かっていない、という状態になりました。そこで今回は写経することにしました。

ダウンロードサイト から、OS X のバイナリをダウンロード、インストールして写経をしていたら、主催者の @ymotongpooGo Playground を教えてくれました。ブラウザで Go のコードを書いて、その場で実行できます。実行時間が短く、サードパーティのライブラリを必要としないコードであれば、これで簡単に実行できます。そういうわけで、Go Playground でチュートリアルをやっていきました。

ついでに書いたコードを簡単に共有するための URL も生成できます。

変数とポインタ

Go では変数とポインタが明確に区別されます。普段つかっている Python では名前にオブジェクトをバインドするモデルで、事実上すべてがポインタみたいなものです。ここはちょっと注意が必要でした。Go では x = y と書くと x に y のコピーを代入します。ポインタ(というか参照というか)は明示的に & を使います。

package main
import "fmt"

type Foo struct {val int}

func main() {
     f := Foo{0}
     g := f   // 変数なので f のコピー
     h := &f  // ポインタ
   
     f.val++

     fmt.Println(f.val)  // => 1
     fmt.Println(g.val)  // => 0
     fmt.Println(h.val)  // => 1
}

一方 Python では x = y と書くと、x に y のポインタ(というか参照というか)を代入します。コピーを代入するには明示的に copy モジュールを使うという考え方です。

from copy import copy

class Foo(object):
    def __init__(self, val):
        self.val = val

f = Foo(0)
g = copy(f)  # copy
h = f        # pointer

f.val += 1

print(f.val) # => 1
print(g.val) # => 0
print(h.val) # => 1

goroutine

並列処理/並行処理を Go は言語レベルでサポートしていて、go foo(..) と書くと、foo() 関数の呼び出しが非同期で実行できます。

トイレで「goroutine ってコルーチンなんだなぁ」と思っていたのですが、席に戻った瞬間 @Jxck_ さんの発表 で「goroutine はコルーチンではありません」と電撃発言です。どうやら OS のスレッド上で動作する並行ルーチンとして実装されているようです。

実装はおいといて、コルーチンを使ったトランポリンみたいなことが簡単に書けそうです。ちなみにトランポリンの理解は非常に曖昧です。

goroutine として呼び出す時でも、関数は通常の引数をとるので、ポインタを渡すことができます。ということは、複数の goroutine で同じオブジェクトを参照するという事態が起こるんだろうなぁと考えて、簡単なコードを書きました。

package main

import (
  "fmt"
  "time"
)

type Foo struct {
  value int
}

func main() {
  f := Foo{0}  // 初期値は 0
  fmt.Println(f.value)

  go Incr(&f, 1, 1)  // +1
  go Incr(&f, 2, 200) // +2

  time.Sleep(1 * time.Second)
  fmt.Println(f.value)  // => 2
}

func Incr(f *Foo, delta int, wait time.Duration) {
  oldValue := f.value  // 古い値を取得して
  time.Sleep(wait * time.Millisecond)  // しばらく休んで
  f.value = oldValue + delta  // 上書き
}

プロセスをまたいでいる時にどうなるかは不明です。こんなときにスレッドセーフじゃないようなコードを書くなという話なわけです。

並列処理といえば…

これは明らかに誤解なのですが、気分がよかったので、Twitter 上では訂正せずにいました。

以前の職場で開発環境/ツールのマーケティングをしていて、そのラインナップに FPGA 用にコードを書くツールが含まれていました。もともとグラフィカルに記述すると、PC 用に実行形式のバイナリを吐き出すというツールで、簡単に並列処理を記述できたのです。そこ発展して FPGA を搭載した I/O ボードの処理をデプロイできる、というツールです。

そんなわけでハードの設計は、まったくできません。ただFPGAを使うときにはプログラムの書き方が、大きく変わる、というのは感じています。Go も並列処理を念頭においた言語らしいので、あらためて並列処理を考える時期にきているようです。