LoginSignup
6
4

More than 5 years have passed since last update.

こうなるといいな、Go言語

Last updated at Posted at 2015-07-26

ライブラリでは改善できない内容なので、単に願望を書いてます。もうすぐ出るらしい1.5には間に合わないと思うけど、いつかそうなるといいな。
ジェネリックとか難しいところはまだわからないけど。

iferr が欲しい

(「後置の if が欲しい」改め)

三項演算子は可読性が微妙なせいかすぐ「いい」「悪い」でもめるけど、perlやrubyでおなじみの後置のifなら割りと文句なしによいと思う。Golangで欠かせないエラー処理を書くたびに何行も書くのがたるいのだけど、後置のifがあれば多くのエラー処理が1行で済むし、可読性も高い。特にテストが書きやすくなるはず。

Golangに限らず、コーディングに占めるエラー処理の量はばかにならない。Golangは可読性を重視 (「Go」という言葉には「そのまんま」「見たまんま」的な意味もどこかに込められている気がする: Rubyのような黒魔術には手を出さないという感じか)しているので、エラー処理も即物的で、処理がジャンプする例外処理よりも読みやすいのはありがたいけど、その量が多くて閉口する。

最初は後置のifでいいかと思ったけど、if err != nilと同等なシンタックスシュガーであるiferrがあればもっといいかもしれないと思えてきた(どっかのExcelの関数ではありません)。

現在のGoではたとえば以下のように書く:

variable, err := エラーを返す関数
if err != nil {
    エラー処理1
}

if文でブロックの使用が強制されるので、どうしても3行になってしまう。書きたいものは一行だけなのに。

さらにこの書き方だと、エラー用変数がif ok != nil のように人によってブレてしまう。正直、エラーをokのような変数に格納するのはどうもだめ。

iferr があれば、以下のようにすっきりできる。

variable, err := エラーを返す関数
    iferr エラー処理

このとき、iferr の行は gofmt で特別にインデントするようにする。Goのポリシーでは、エラー行はインデントすべきであり、それ以外の行は極力インデントすべきでないとのことなので。

iferr{}ブロックを伴っても伴わなくてもよいものとする。ブロックも含めてインデントする。ブロック内では通常のifも含め普通にコードを書ける。


variable, err := エラーを返す関数
    iferr {
        エラー処理1
        エラー処理2
    }

Goのポリシーに基づき、elseは続けられないようにする。

variable, err := エラーを返す関数
    iferr {
       エラー処理1
       エラー処理2
    }else{ // これはできないようにする
       通常の処理
    }

さらにgofmtで、直前にerrが代入されていないとiferrを呼べないよう制限をかける。

variable, err := エラーを返す関数
別の処理 // これは置けないようにする
    iferr エラー処理1

これならGoのポリシーにも適い、コード量も減らせ、エラー処理をもっと標準化できる。さらに既存のコードにも影響せず、使いたくない人は使わなくてもよい。errは予約済みローカル変数に昇格しなければならないだろうけど。

errはあくまでローカル変数なので、goroutineやパッケージ間で直接共有はされない。

ライブラリではこうしたインデントまで含めた処理はまかなえないので、やるなら言語仕様に含めるしかない。GoはJavaのような例外処理ではなく直接的なエラー処理を行う道を選んだのだから、エラー処理は少しでもやりやすい方がいい。

for 文だけ古臭いのを改めて欲しい

ループを for に統一するのは別によいのだけど、なぜここだけ値をセミコロンで区切るのか(値の中でカンマが必要になることがあるからといえばそうなのだけど)。

# Go の古臭い for
for i := 1; i < 10; i++ {
  iを使って繰り返し処理
}

それ以前に、カウンタ変数なんかより range のようなenumerationの方がデフォになるべきではないか。順序とか保証してくれなくてもいいので。今はいかにもrangeが後付けな感じ。

for i, _ := range testdata.a {
    f(i)
}

ruby のeachとまでいかなくても、せめてpythonのfor inのようなもっと洗練されたenumerationをデフォで使いたい。

a = ['cat', 'window', 'defenestrate']
for x in a:
    print x, len(x)

そういえば昔あったHyperTalkという言語ではすべてのループをrepeatで書いてた。

配列とスライスを統合して欲しい

配列とスライスの文法がすごく似通っていて、すぐどちらがどちらかわからなくなる。スライスに一本化して欲しい。どちらかというと配列かスライスかという選択より、レンジが固定か可変かの方が重要な気がする。

スライスに追加する append() が関数というのがかっこよくない。演算子を節約した結果なのだろうけど、C++のstreamみたいに<<でできたらいいのに。

<<>>はビットシフト演算子だけど、利用頻度を考えたらビット演算とかはもっとマイナーな記号の組み合わせにする方がよいのでは(<[[とか]]>みたいな)。演算子を無理にオーバーライドしようとして四苦八苦するよりも(と過去の都合も考えず書いてみる)。

ポインタ演算子 * をなくしてほしい

アドレス指定の演算子 & だけでいい。アホなこと言ってると思うけど、せっかくGolangでポインタ演算を追放したのだから、ポインタ演算子*も完全に廃止して欲しい。

そのためには、すべての変数/配列/スライスなどを内部的に間接参照として扱い、それでいて代入や引数は常に「値渡し」になるようにすればよいのでは。関数などを「参照渡し」にしたいときだけ、明示的に&を変数の前に付ける。

# 内部的には間接参照だが扱いは完全に通常の値渡し
a := "test"
b := a # 値渡し
a = "updated"
fmt.Println("a: ", a) #=> updated
fmt.Println("b: ", b) #=> test

# 唯一間接参照らしい振舞い
a := "test"
b := &a #&を付けたときだけ参照渡しになる
a = "updated"
fmt.Println("a: ", a) #=> updated
fmt.Println("b: ", b) #=> updated

これなら結果が同じなので今までどおり変数を使えるうえ、関数の引数で*を使う必要もなくなる。記号を2つも使っていたなんてもったいなさすぎる。

試したわけではないけど、コンパイラ言語ならすべての変数を間接参照にしてもオーバーヘッドにならないのでは。

メモ: Ruby の場合

逆に、Rubyではデフォルトで参照渡しになるのがむしろ紛らわしい。値渡しにするためにdupメソッドを使ったりなんかして。

Rubyの「シンボル」があるとうれしい

Rubyの「シンボル」はもっと他の言語にもあってよい機能だと思う。一意性が保証され、引用符の量を激減させてくれるし、コロンと一体化することで意味も明確になる。"symbol1":みたいな不細工な書き方より、symbol1::symbol1などと書きたい。

コンパイラ言語ならオーバーヘッドの心配もないはず。

その代わりコロンを他のことに使わないようにしないといけなくなるけど、十分報われると思う。Pythonはコロンを乱発しすぎていてその辺が残念。

非互換性を恐れず仕様を改善して欲しい

Golangには既に多くの資本が投下され、多くの資産も蓄積されている。仕様を変えるのは簡単ではないのはわかるけど、それこそ今のうちにやっておいて欲しい。

せっかくコンパイラ言語なのだから、バージョンごとの非互換性はコンバーターで対応すればよい。一度コンバートすれば済むし、コンパイラに仕込むと遅くなるので外部の方がいい。

おまけ: 書こうと思ったらライブラリで見つけたもの

  • pp -- フォーマット出力。今までreflectでえっちらおっちらinspectしてたのよりずっと楽。
6
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4