皆さんは「Go」というプログラミング言語を聞いたことがあるでしょうか。
Goは2009年に登場したとても新しい言語です。にも関わらずここ数年で急速に広がりを見せていて、有名なところではGunocyというニュースまとめサービスがGoで作られています。
ここまで新しい言語がGunocyのような巨大サービスに採用されるなどということは、普通はなかなかありません。これはGoが非常に筋の良い言語である証明です。それもそのはず、実はGoはあのGoogleが開発している言語なのです。
このエントリでは、まずGoを俯瞰して特徴を押さえてから、簡単なWebサーバを作ってみましょう。
なおGoについてGoogleなどで検索する際は「Golang」あるいは「Go言語」という語を使うようにするのがおすすめです。「Go」ではあまりにも一般的な用語すぎて絞り込めませんので。。。
Goの特徴
クロスコンパイル可能なコンパイル言語
GoはコンパイルしてOSネイティブな実行ファイルを作るタイプの言語です。これに加え、大きな特徴としてクロスコンパイルが可能です。これは「たとえ対象のOSの上で無くてもそのOS向けの実行ファイルを作ることができる」という意味です。
例えばMac OS X上でWindows向けのexeを作ったりすることができます。
ネイティブな実行ファイルができるという意味は、別途ランタイムをインストールする必要がないということです。例えばJavaの場合、実行ファイルはどのようなOS上でもclassファイルという形式であり、このためOS毎にJVMをインストールする必要があります。これが手間となるのですが、Goの場合はこの手間は発生しません。
このため、広く配布するコマンドラインツールの作成にGoを用いることがあります。
表記が揺れないための工夫
Goは、多人数で長期間メンテナンスされるプログラムを想定して設計されています。このため、プログラムの表記が人によってずれないような工夫がされています。
例えばループ構文はforの1種類しかありませんし、三項演算子はありません。読みやすさを重視したプログラムになるよう、意図的に言語仕様をコンパクトに抑えているのです。
またSDKにはコード整形ツールが備わっており、スタイルを強制的に統一することができます。
豊富なライブラリ
生まれて10年にも満たない言語ということが信じられないくらい、ライブラリが豊富です。標準ライブラリもさることながら、サードパーティによるライブラリも非常に充実しています。
これにはライブラリが手軽に公開できる、ということが大きく寄与していると思われます。GitHubなどのリポジトリをライブラリとして参照することができるため、公開と利用のハードルがとても低いのです。
ライブラリの一覧ページ
を見ると、あまりの豊富さにびっくりすると思います。
言語仕様
ここからは具体的な言語仕様を見ていきましょう。全てを紹介するのは無理なので、特徴的なものに絞ってご紹介します。
プログラムの開始はmain関数から
Goによるプログラムのエントリポイント(開始位置)はmainという名前の関数です。最小のサンプル、いわゆるHello Worldを見てみましょう。
[code]
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, World")
}
[/code]
funcというのが関数を定義するキーワードです。関数はGoにおいてとても大事な存在です。
型はあるが変数の型宣言は不要(型推論)
変数に型宣言は必要ではありませんが、明らかにおかしい代入はコンパイル時にエラーとして報告してくれます。このような機能を型推論といい、現代的なプログラミング言語の多くが備えている便利な機能です。
例えば以下のプログラムを実行すると「9行目はなんだか型がおかしいよ」というエラーを報告してくれます。
[code]
package main
import (
"fmt"
"strings"
)
func main() {
i := i()
s := strings.Trim(i, " ")
fmt.Printf("%s", s)
}
func i() int {
return 1
}
[/code]
実行結果は次の通りです。
./sandbox.go:9: cannot use i (type int) as type string in argument to strings.Trim
関数はファーストオブジェクト
関数を変数に代入したり関数の引数に渡すことができる性質を「関数がファーストオブジェクトである」と言います。例えばJavaScriptでは関数がファーストオブジェクトです。
近年は関数型プログラミング言語の有用性が叫ばれていて、この流れから最近のプログラミング言語は関数をファーストオブジェクトして扱えることが多いです。Go言語の関数ももちろんファーストオブジェクトです。
以下は関数を引数に取る関数の例です。12行目のs関数がそれです。
[code]
package main
import (
"fmt"
)
func main() {
f := func() string {
return "Hoge"
}
fmt.Println(s(f))
}
func s(f func() string /* 引数なし、string型を戻す関数 */) string {
return "Hello, " + f()
}
[/code]
関数からは複数の値を返せる
Goは関数から複数の値を返せます。これは地味に便利な機能です。
最も一般的な用途はエラーを返すことです。正常に処理が終了しなかった場合にエラーを返すようにするのがGoの関数の作法です。例外もありますが、Goでは余程のことがない限り利用は推奨されません。
下記は標準パッケージに含まれる文字列を数値に変換する関数の型です。
[code]
func Atoi(s string) (i int, err error)
[/code]
この関数を使う側はエラーの有無で正常終了かどうかを判定します。
[code]
package main
import (
"fmt"
"strconv"
)
func main() {
i, err := strconv.Atoi("a")
if err != nil {
fmt.Println("error ! -> ", err)
} else {
fmt.Println(i)
}
}
[/code]
言語によっては例外を使うところですが、Goは「エラーチェックは明示的に」という方針のもと、このような関数設計をするのが一般的です。
Goでオブジェクト指向
最近の言語は何らかの形でオブジェクト指向を備えていて、多くはクラスを作ることで実現しています。Goにクラスはありませんが、構造体を使ってクラスっぽいことができます。
下記の例ではPersonという構造体にFullNameというメソッド(のようなもの)を作っています。
[code]
package main
import (
"fmt"
)
type Person struct {
FirstName string
FamilyName string
}
func (e *Person) FullName() string {
return e.FirstName + " " + e.FamilyName
}
func main() {
p := Person { FirstName: "tomo", FamilyName: "go" }
fmt.Println(p.FullName())
}
[/code]
Goルーチンによる並行処理
Goは複数の処理を同時に実行する「並行処理」がとても簡単に書けます。
アプリケーションの速度を上げるにはCPUパワーを使い切ることが大事な要素の1つですが、単体のCPUの性能が頭打ちになりつつある現在ではマルチコアに対処したプログラムを書くことが肝となっています。つまり並行処理が大事ということで、Goはこの点でとても有利です。
下記の例では1秒待ってコンソールに出力する処理を2回並行処理しています。goというキーワードに続けて関数呼び出しを書くだけ、という手軽さです。
[code]
package main
import (
"fmt"
"time"
)
func main() {
go ope(1)
go ope(1)
time.Sleep(2*time.Second)
}
func ope(s time.Duration) {
time.Sleep(s*time.Second)
fmt.Println(s, "秒待って起きた!")
}
[/code]
手軽に並行処理を書けるだけではなく、Goでコンパイルした実行ファイルは実際にCPUをしっかりと使ってくれるそうです。「ボケて」というサービスでは、最近一部処理をGoに移行したCPUリソースをキッチリ使いきってくれて消費メモリも既存アプリと比べて少ない
以上、Goの言語仕様をかいつまんでご紹介しました。全体的に意外性の少ない無難な言語仕様を持っていて、優等生的な言語だと思います。これは「多人数で長期間メンテナンスされるプログラムを想定した」設計方針の表れでしょう。
Goによるアプリケーションの実例として、静的なファイルを返す簡単なWebサーバを作ってみましょう。Webサーバと聞くと複雑なプログラムを想像するかもしれませんが、Goの優秀なライブラリを使えばほんの少しのコードを書くだけで済んでしまいます。論より証拠、実際のプログラムを見てみましょう。
[code]
package main
import (
"log"
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir("static")))
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
[/code]
本質的な部分はmain関数の中の5行です。
staticというディレクトリを/というパスにマッピングしています。例えばstaticディレクトリの中に次のようなHTMLを作ります。
[code]
<!doctype html>
<head>
<meta charset="UTF-8" />
<title>Go Web Server</title>
</head>
<body>
<h1>Hello, Go Web Server!!</h1>
</body>
[/code]
次のURLをブラウザのアドレスバーに入力すると、HTMLが表示されます。簡単ですね。
http://localhost:8080
Goのさらに詳しい情報については以下のようなサイトをご覧ください。
公式サイト(The Go Programming Language)とその日本語訳サイト
このサイトでSDKのダウンロードとインストール手順を見ることが可能です。またGoの基本を押さえるための「A Tour of Go」というコンテンツがありますので、一度見ておくと良いでしょう。
golang.jp
日本語のGo情報サイトです。多くのコンテンツが公式サイトの日本語訳ですがかなり精度高く翻訳されているので、重宝します。普段はこのサイトから情報を得るのが良いでしょう。
生まれて間もないGoですが、言語仕様をシンプルに押さえつつ先進的な機能を取り込んでおり、後発ならではの強みを発揮しています。人気も高まっており日本語の情報も多いので、エンジニアの方はぜひ一度触ってみてGoの世界を体験してみてください。