🦜

Goへのヘイトに対する考え方

2021/09/17に公開
11

https://www.kbaba1001.com/entry/2021/09/17/073149
(該当記事が削除されました)

  • RubyのサービスをGoで置き換えるのは3倍人手がかかる
  • 何するにも機能不足
  • JSONの読み書きにわざわざ構造体書くの面倒
  • 同僚がGoを選ぼうとしたら愚かな選択ですねと答える
  • サーバーサイド開発にGoを使うのは危険

っぽい内容だったかと。

だいぶGoの特徴や既存の言語との考え方の違いが広まってきてるのかなぁと思っていた矢先だったので十年くらい前のような指摘をあえて今されていてびっくりした。

正直、ここに書かれたようなヘイト項目は既出すぎるので、もし影響の大きい項目を多くの人が同様に嫌っているならばGoはここまでの人気のある処理系になることはなかったと思う。(もしくは多くの人が嫌ってはいるが影響の小さい項目ということ)

Goは出た当初、こういうヘイトが世界中のブログに書かれました。しかし、それから十年Goは明らかにマイナー言語とは言えない状況になって多くの著名なプロダクトを支えるまでに育ちました。このことが、ヘイトの内容が実際の実装に悪影響を及ぼしていない証拠です。

Goは既存言語のアンチテーゼ

Goの発案者のひとり、Rob Pike氏はあらゆる既存のプログラミング言語が互いに発案した特徴を取り込みあって、あらゆる言語が同じような言語に向かっていることに異を唱え、そうではない言語を作ろうとして(既存の言語のアンチテーゼとして)生まれたのが「Go」です。つまり、既存の言語がこれまでのあらゆるフィーチャーを何らかの形で取り込んで使えるようにしてきたのとは異なり、実用的な実装を必要十分に書けるように既知のアイディアの中からフィーチャーを厳選して設計されました。

つまり、Goのアイデンティティのひとつに「厳選された必要十分な機能セット」というものがあり、過去ここを壊すような多くの提案には「No!」が突き付けられてきました。(当時、Rob氏はめったに首を縦に振らないRob軍曹という呼び名がついたほど)

オープンソースプロダクトには「No is temporary, Yes is forever.」ということわざがあります。Rob氏はこのことをおそらく知っていて、Yesとする項目を厳選してきたのだろうと思います。そうして生まれたのがGoという言語処理系なのです。

ちなみに最近はGo1互換を保った改善とそれ以外をGo2プロポーザルに分けて要望しましょうというスタンスになってますが、やはりGo2プロポーザルも「厳選された必要十分な機能セット」をぶち壊しがちな提案は片っ端からクローズされています。

既存言語はトレードオフのある機能をデメリットが多少あっても取り込んでいくスタンスが多いのですが、Goは「厳選された必要十分な機能セット」というアイデンティティのもとに積極的に取り込むことはしません。つまり「変化の乏しい言語仕様」なんですが、これもまたGoの特徴です。

Goはある機能を実装するのにAともBともCとも書けるという選択肢を狭めるような設計になっています。これは既存の言語とはかなり異なるスタンスです。PythonのZenの思想をもっとストイックに実現しようとする考え方です。Goで小さな機能を異なる人が実装したとき、おおむね似たようなコードになる傾向が強いのが特徴です。他の言語では全く異なるイディオムで書かれたりなど個人の好みがコードに表れやすい傾向があります。

Goの評価は二分する

ヘイトが多いのは人気の高さを示していると思いますが、ヘイトが多いことがその言語の良し悪しの判断に大きく役立つのかというとそうではないんじゃないかなということをお伝えしておきます。

なぜなら、現在僕はGoを気にいっていますが、Goを触った当初これらのヘイトを実際に同じように実感して嫌いになりかけました。「Goのこの挙動はあかんやろ」とすら思ったこともあります。しかし、Goで実装を書いたりしていくうちによく考えられた言語仕様や痒い所に手が届く標準ライブラリであることが見えてきて、僕のお気に入りの言語処理系になりました。つまり、既知の言語に慣れていればいるほど初見でGoをいったん嫌いになるのはGoを習熟する上で多くの人が通る通過点なのです。

もちろんレアケースですんなり嫌いになることなくGoを学べる人もいますし、嫌いになったままGoに触れることをやめてしまう人もいますが、これらのヘイトを減らすためにGoの開発者たちがGoに何かの改良や機能を追加するということはしません。

Goの開発者たちはあくまでGoの改良や機能追加は実利のために行おうとします。つまり、これらのヘイトに対し改善のようなものが一向に出てこないのにはちゃんとGoなりの理由があります。それを知ろうとするか否かでGoの評価は真っ二つに分かれるのはGoのユーザーにとって既知の事象なのです。

Goとヘイト

なぜGoにヘイトが向けられやすいのかという主な理由は「既存の言語で便利なX機能がGoでは使えないし、X機能を取り込もうともしない」というものです。(また、お気に入りのX機能がないGoの人気が拡大していることなどが、X機能が否定されているように感じてX機能をお気に入りの人たちのアイデンティティを刺激しているのだという指摘もあります。)

数年後にうんざりしてGoの人気は地に落ちると十年前から言われてきましたが、結局この十年でそういう方向になることはありませんでした。(期待しすぎた人たちがいくらか離れたりはあったとは思いますが)

Goの見えにくいメリット

「厳選された必要十分な機能セット」と「変化の乏しい言語仕様」の価値は理解しにくいとは思いますがその利点は以下のようなものかなと考えています。

  • 実現したいことに対し採用できるやり方は限られているので迷わず書ける
  • ミニマムな機能セットだからこそ (外部のプロジェクトに依存することなく) 短期間の間に多くのプラットフォームに処理系を提供できた
  • ミニマムなことはユーザーがGo自体の開発に入り込む障壁を低くする効果もあったと思う
  • 静的解析ツールを作ろうなんて既存言語では思いもよらなかったがGoならできそうに思える
  • 変化の乏しい言語仕様のおかげで静的解析ツールのメンテコストが大きく上昇したりしない
  • 十年前のコードが今も実行でき、今風に修正する際、修正ポイントがほとんどない

これらは他の言語ユーザーからするとデメリットを裏返しに解釈して良い評価をひねり出しているだけに見えて、「なにをいっているんだおまえは?」っていう感想になりがちですが、この特性は現代の多くの言語にはないユニークな特徴であり、この状態がGoの目指した設計でもあります。

静的解析ツールを作ろうなんて既存言語では思いもよらなかった

ここを笑うコメントをいくつか見たけども、そのコメントした人の中で既存言語で静的解析ツールをちゃんと維持し続けている人はいるのでしょうか?(作った経験だけならありますが、動くことを維持するのが大変だから作ろうと思わなかったんですが・・・。)

逆張り解釈

しかし、ヘイトの多くもこれなんです。Goにとって良いことを後ろ向きに評価しちゃってるだけなんですよね。

JSONの構造体定義なんかもそう。あれGoのユーザーは楽したいしメリットがあるから喜んで定義するんですよ。記述の面倒さなんてワンタッチでJSONから構造体定義を作れるツールもあるので苦にならないし。また無名のネスト構造定義は非推奨ですし(ツールもバラバラに定義を生成する機能があります)、JSON構造の一部を変動するものととらえる場合の技法もちゃんとGoなりの方法があります。

深いJSON構造をmap[string]interface{}なんかでデコードしちゃったら、、、やってみればわかるけどツラさしかありません。そういうところに「構造体定義しなきゃならない苦痛」だけを強調しても共感してもらえるのってGo未経験者ぐらいじゃないかなぁ。

「あれもできない、これもできない、こう書くしかないのか」これはGoにとって誉め言葉です。これは裏を返せば「ベストじゃなくてもいい、たった一つのベターな書き方ができればOK」ということなのです。

Goの良さを伝えたいが・・・

  • Goの良さはスペックからは読み取りにくい
  • その裏にある思想や哲学が良さの源泉なんだけどこれを文書化するのが難しい
  • 懸命に訴えても「カルトっぽい」とか「信者かよ」とか言われやすい
  • あれもこれもできないのが良いところなんだよといっても「お前は何を言っているんだ?」状態

興味を持たれたらここやその他の記事を読んでみてください。

まとめ

  • Go言語の開発スタンスは既存の言語の開発スタンスとは異なります(アンチテーゼなので)
  • Goが目指すのは極力必須の機能だけで組み立てられた極力冗長さのないコンパクトで必要十分な仕様と機能セット
  • 既存言語は他の言語の機能を取り込もうとするが、Goはそうはしない
  • Goと既存言語との違いにはちゃんとした理由があり、それに関心があるかどうかでGoの評価は二つに割れる
  • Goにヘイトを向けるのはGoを学ぶ上での通過点として多くのユーザーが通る道
  • 願わくばその先の疑問をコミュニティにぶつけていってほしいと思う
  • Goの人気が数年内にフェードアウトするというのは昔から言われつつもそうはなっていません
  • 件の記事が言わんとしている「RubyのサービスをGoで置き換えるのは3倍人手がかかる」は納得しがたいものがありますが、
  • 「RubyのRoRのようなサービスをGoで置き換えるのは3倍人手がかかる」なら納得です
  • GoにRoRを置き換えられるほどのコンポーネントは充実していないのが理由ですが、GoユーザーにはRoRのようなサービスをGoで実現したいという需要があまりないので今後も充実するかどうかというと微妙です
  • なんとなく「サーバーサイド開発=RoRのような開発」という固定概念があるような気がしました
  • Goユーザーの考えるサーバーサイド開発はもっと広義なので「サーバーサイド開発にGoを使うのは危険」と言われると「お前は何を言っているんだ?」となってしまいます

素朴な疑問

  • とはいいつつも誰もGoでは使われていないPythonのような複素数を採用したのはなんでだろう?(GoがPythonの考え方を多く取り入れていることは想像できるんだけど・・・)

Discussion

NoboNoboNoboNobo

反応の一部に誤解を招いているかもしれないが、Goの開発側もコミュニティも「このままでいいんだ」とは考えてはいないですよ。優先順位が違うのです。いたずらに言語フィーチャーを増やす前にほかに優先すべきことがあるし、Go2で入る変更は多岐にわたる変更ではなく数点に絞り込まれる予定だということです。

また、DjangoやRoRを置き換えるものは目指してはいないが、もっとDBを堅牢かつ安全に扱えるように作られたライブラリは少しづつ成果を上げつつあります。

tkmrtkmr

本筋から逸れて恐縮ですが、「(言語)処理系」という言葉が言語自体を表すのに使われているようで、混乱を避ける意味でも出来れば修正した方が良いかな、と感じました。

NoboNoboNoboNobo

置き換えてみて確かにしっくりきました!指摘ありがとうございます!

banchobancho

GoとPythonの比較について、聞いてみたく思いました。私は、Pythonは良くない言語だと感じていますが、それが何に起因するものなのかわからす、いつも、もやもやしています。Pythonで書くならば、NodeJS使いませんか、と思います。

NoboNoboNoboNobo

僕はPythonも好きですよ。PythonとGoは根底にある哲学が似た傾向にあるように思います。Pythonも過去にやたらと重箱の隅をつつくようなヘイトにさらされていた時期があります。
僕はPythonもNodeJSもシングルコア性能自体は似たレベル(双方ベストを尽くせばですが)でマルチコア性能があまり伸びない処理系だと思っていますので性能よりも簡単に書けることが重要ならGoも含めて適したライブラリのある処理系を選びます。マルチコア性能を確保したいときはGoを使います。

NoboNoboNoboNobo

よく、「今時の新進気鋭の言語処理系のフィーチャーを併せ持つ言語処理系が出たらいいよね」っていうのをよく見聞きするのですが、それがGoのアイデンティティとは真逆であって、それはGoの良さの一部を失った何かでしかなく、Goユーザーから見ると残念な言語処理系ということなのです。

lndcltlndclt

Goを使いはじめて2週間くらいのものです。

ネスト構造定義は非推奨ですし

とおっしゃっていますが、この根拠や言及している記事はありますでしょうか?私自身structの中にunexportedなstructを入れて、その構造体のテストする際に、ディープコピーしたいけど面倒くさい、などと感じております…

NoboNoboNoboNobo

まさにそれが理由です。部分的に差し替えしようとしたときにあらかじめ定義が宣言されていないと操作が面倒なのです。言及している部分はちょっと探してみます。

lndcltlndclt

ありがとうございます!ちなみに、もしよろしければネストを解決する方法としてはどのような手法が用いられるのが一般的か教えて頂けないでしょうか?

NoboNoboNoboNobo

もしかするとイメージしている「ネスト構造体定義」が違うかもしれません。
避けましょうというのは、冒頭の記事中で指摘された「ネストされた 無名の 構造体定義」のことです。ネスト構造体に「型名を付与」して定義すればOKです!
https://mholt.github.io/json-to-go/ このサイトの「Inline type definitions」チェックを外した状態であればOKだとおもいます!

lndcltlndclt

なるほど!文脈的にもJSONをパースした時に出てくるmapの構造、という話でしたからね、納得しました!

ありがとうございましたm(_ _)m