Goをちょっと使ってみた

いきなり大人気のgoをすこし触る。Rob PikeやKen Thompsonとかが作っているというだけですごいと思ってしまいます。

ビルド

ubuntuの場合、 http://golang.org/doc/install.html に書いてあるとおりすればコンパイラやリンカなどのツールのバイナリができあがります。

コンパイラやリンカはアーキテクチャごとに名前が違い、

  • 386用: 8g, 8l
  • amd64用: 6g, 6l
  • arm用: 5g, 5l

のようです(FAQによると、Plan9の伝統らしい)。

また、環境変数GOROOT,GOOS,GOARCHは、コンパイラやリンカの実行時にも必要なものです(生成バイナリの実行には不要)。.bashrcにかいとくとか、sourceコマンドなどで簡単に取り込めるようにしとくといいでしょう。

emacsモード

ソース中のmiscにemacsvimMacXcodeのためのgoモードが入ってます。

emacsの場合、go-mode-load.elとgo-mode.elがあります。このふたつのファイルを~/.emacs.d/の直下にコピーし、~/.emacs.elに

; ~/.emacs.d/に野良モードなどを置けるようにする設定。
(setq load-path (cons (expand-file-name "~/.emacs.d/") load-path))

(require 'go-mode-load) ; 実質go-modeのために書くのはこれだけ

とかけば拡張子が".go"なファイルを開けば、goモードになります。

例プログラム: exp/evalで文実行

最初のほうに書くプログラムというのは、その人の特徴がでてるかもしれません。自分の場合、evalがあるかどうかを調べています(つまり最初はアプリにしない)。

// 8g doeval.go
// 8l -o doeval doeval.8
package main

import "exp/eval"

func main() {
        world := eval.NewWorld();
        code, err := world.Compile(`
                print("Hello\n");
        `); // does not supoort package

        if err != nil {
                panic(err.String(), "\n");
        } else {
                if value, err := code.Run(); err != nil {
                        panicln(err.String());
                } else println(value);
        }
}

以下、注目した特徴

コーディングスタイル

まず、goのコーディングスタイルでのインデントはハードタブのようです。ソース付属のテストコードもそうだし、その設定はきっちりツールgofmtのデフォルトやemacsモードファイルでもなされています。

exp/evalパッケージ

exp/evalには簡単な文インタプリタが実装されています。この機能ではinterfaceやpackage/importの解釈は実装されていません。

パッケージ名

パッケージ名は単一識別子のみで、使う側がimport時に名前を変えて割り当てることで、使い分ける仕組みのようです。たとえば以下のようなかんじでしょうか:

import fooDom "foo/dom"
import barDom "bar/dom"

また、main.mainという関数が、コマンド実行で起動される関数になっています。

変数宣言の簡略形式

変数宣言は複数の形式があります。

Cのように同名の変数をかぶせて宣言できるようです。

複合文のセミコロン省略

文を区切るセミコロンはいくつかの場面で省略可能です。
rubyなどとちがい、"}"と宣言での")"のだけととても限られています。

つまり、例コードだとpanic、panicln, printlnのあとのセミコロンは省略可能です。

文字列リテラル

バッククオートでくくる文字列リテラルは、改行やハードタブも含めそのままの内容が文字列になります。

if文

if文はオプションで条件の前に代入文などの単純文を一つ埋め込めます。

もう一つ変わってるのは、if文の真部はblock文にしなくてはいけないが、偽部は任意の文が使えるというところもでしょうか。

条件式

条件式は"bool"値("true"/"false")のみつかえます。そのため(ポインタ型やスライス型の)変数に値があるかどうかの判定では、"nil"と比較することになります。

組み込み関数

"print"や"panic"は組み込みの関数です。

ずっと残るかどうかは保証しないようですが。

その他仕様で注目した点

"++"/"--"や代入が式ではなく文になっている

そのかわりifやswitchでオプションで文を一つ書けるようにしたという感じでしょうか。

リフレクションなどがあるunsafeパッケージ
interface型

型システム上は、メソッドセットの包含関係でチェックする。

興味深いのはstructに埋め込むことができる点で、そのまま同interfaceをもつことになるようだ(structどうしでもinterfaceどうしでもできるけど)。

あと細かい点だと、レシーバはポインタですが、interfaceの場合、structポインタと違いnil値に対してもメソッドが呼べないでした。

パッケージのinit関数

こういった機能は最近の言語では普通だけど。

設計判断の多くはFAQにもあるので必見です。