Goに関するアレやコレや

Go CLI

先日のGoConの素晴しい発表に続いて、別に優れた発表資料が公開されていた。

CLI ツール開発を支える技術 2019春

CLI ツール開発を支える技術 2019春 / Techniques that support building CLI tools, 2019 Spring - Speaker Deck

この資料はまとめからさらに発展して筆者のお勧めを紹介して下さっている。 現状だとCobraとViperが良い模様。 最初にpflagから始めて拡張してくのが確実な道なようだ。

ただ、自分は結構、以下のkingpinを気に入ってしまった。

GitHub - alecthomas/kingpin: A Go (golang) command line and flag parser

まだどれを使うかはわからないし、上記のようなライブラリが必要な規模のプログラムも書いていないのだけれど、必要な時に、優れた資料2つに出会えた偶然をとても有り難く感じている。

Quizの件

先日のGoConのQuizの件、ちょっと調べていたのだが以下のような記載が見つかった。

Golang Packages - golangbot.com

A package can have multiple init functions (either in a single file or distributed across multiple files) and they are called in the order in which they are presented to the compiler

訳してみると

1つのpackageは複数のinit関数を持つことができる。(単一のファイル内でも、複数のファイルに分散されても)。複数のinit関数はコンパイラに現れた順に呼び出される。

この上の英文だがやたらとコピペされているがオフィシャルな文書では発見することができなかった。一応、現状には合っているのだが、できれば引き続き公式な定義を探したいとは思う。

Go is Google's language, not ours

Chris's Wiki :: blog/programming/GoIsGooglesLanguage

Goにgenericsがどうしても欲しい人がOpenGoでも作らないかとの嘆きを漏らしたのに対し、GoはGoogleの言語であり、コミュニティの物ではないとの記事。筆者はGoにおけるversioning、moduleの仕様策定に関し大きな心の傷を負った模様だ。

以前からGoogleOSSOSSではないとの議論は存在する。Androidが3でソースの公開を製品発表以後まで延期し「なんちゃってOSS」と評されたのはもうかなり過去の話だが、それ以外にもGoogleは常に決定権は自分達だけで所持し、コミュニティには口出しをさせなかった。この記事のコメント欄でもChromeのエンジンについての記載があるが、Chromeの変更では常にGoogleの一存による変更に対し大きな反対意見が起ころうとも無視されてきた。例えばabout:blankを廃止し新タブにGoogle検索を常に表示するように変更されたり*1chromeの頭に「あなた」との表示を始めたり*2、Chrome69ではゾンビクッキーを開始して大顰蹙を買ったりしている*3Googleという企業がGo言語の仕様をGoogle第一で考えるのはあり得る話だし、今後も気を付けなければならないのは事実だ。tensor flowがGoogleのサービスを第一に考慮しているのと同じだ。

JavaもかつてSunの言語に過ぎなかったのがあまりにも人気が出過ぎてコミュニティを作らざるを得なくなった経緯があった。その場合にもSunは商標を握り続け、また非商業に限定的なGPLとの二重ライセンスを維持。Sunを買収したOracleによりGoogleJava API著作権にて大火傷を負ったのは記憶に新しい。記事にて指摘されているがGo言語の商標はGoogleが握っている*4。Go言語自体のコードはBSDJavaよりはマシだ。しかしGoogleがAlphabetの集金組織としてよりマネタイズを重視した経営を続けている現状、いつ心変わりを起こしたりするかはわからない*5。いつでも依存を断ち切れるように不断の努力を行っておくことは重要だと考える。

標準ライブラリってどれほど必要なんだろうか?

Python Software Foundation News: Amber Brown: Batteries Included, But They're Leaking

Pythonの話なんだけれども面白い話だった。

According to Brown, “the standard library is where code sometimes goes to die,” because it is difficult and slow to contribute code there.

彼女が問題にしているのは標準ライブラリが大き過ぎる点、そして標準ライブラリにコードを追加することはイノベーションを阻害するという点だ。

確かにここ最近の開発はどの現場でも外部ライブラリを使うのが当たり前だ。ここ数日Go言語のCLI開発環境を調べるだけでも、標準のFlagsはあまり使い勝手が良くなく、外部のpackageを使うのが普通のようだ。恐らくSI屋で金融等の厳しい環境では今でも標準ライブラリを使うことしか認めない現場もあるのだろう。しかしJavaですらmaven経由でSpringを使うことがほぼ当たり前になった昨今、外部ライブラリを使わないことなどあるだろうか。外部ライブラリを受け入れない現場はイノベーションを否定し、開発効率を犠牲にしている。彼女の言う、標準ライブラリはコードが死ぬ場所だという主張は非常に的を射ているように感じる。

外部ライブラリを利用する場合にもバージョン管理やセキュリティ、ライセンス等、大きな問題が残る。Goでもやっとこさモジュール管理が搭載される。しかし今後も多分、外部ライブラリに依存する開発は続くと思われるので新規の言語には最小限の標準ライブラリと最初からの標準モジュール管理を期待したい。標準ライブラリはシステムコールのラッパー程度で良いのかも。

Goのパッケージとディレクトリ名

相変らず駆け出しのGo開発者で基本的なことで躓いているけれども、Goのディレクトリの命名規則

いきなり正解だが、

How to Write Go Code - The Go Programming Language

Go's convention is that the package name is the last element of the import path: the package imported as "crypto/rot13" should be named rot13.

Goではpackage名はインポートするpathの最後の部分だ。packageが"crypt/rot13"とimportされるなら、rot13と名前付けされるべきだ。

さてここで問題なのがこれshouldな点。そのため当然、違うpackage名を付けてもbuildできてしまう。しかし、importする時点で記載が面倒になるので素直に同じにすることが最善な模様。

例外が存在し、

Executable commands must always use package main.

実行命令ではpackageはmainにすることと釘を刺している。ここがより厳しいmust。

コンパイラがさらに厳しいのはpackage名の指定がディレクトリ内で常に一つである必要がある件だ。これ、package mainを作るなら別の独立したディレクトリを必ず作成しなければならないということ?go buildには-oオプションがあって出力名を変更できるのだけれども微妙に嫌な仕様だ。

パッケージ名の衝突に関しては気にするな、importする側で別名が付けられるとの記載

Effective Go - The Go Programming Language

import時での別名の利用法は言語仕様に

The Go Programming Language Specification - The Go Programming Language

"import ."でパッケージ名を省略可能になるがテスト以外の目的で使うなとの話。ハイ。

実践Goとエラー処理

これ書いとくの忘れた。

Practical Go: Real world advice for writing maintainable Go programs

とんでもなく良い記事ですが、長いので一気に読むのはかなりつらい。

最初のほうは割と当たり前?な内容なんですが、段々と実践的になってくる。特に関心したのはエラー処理のお話。

Practical Go: Real world advice for writing maintainable Go programs

最初のリンク先も面白いです。type assertionとか使ってます。

https://golang.org/ref/spec#Type_assertions

errorに対してmethodのinterfaceでduck typingを行うと。 上級者の匂いがしますね。

で、エラー処理を減らしたいならより良いユーティリティを使えという話。ReaderよりもScannerを使えとのお話。こういうの標準ライブラリのドキュメントにも書いておいて欲しいんですが。

実際、自分もpathをなめるのにdirectoryをOpenしてからReaddirnamesとか使っていたのですが、ioutilにReadDirとかあったりするんですね。こういうのの選択だけでエラー処理の記述が大分減るというのは厳しい話です。ドキュメント舐めるの重要。

さらにこの記事で紹介されるerrWriterは面白いですよね。ループ分関数呼出は行われるのでパフォーマンスとか気にしちゃいますが。

しかしGoのエラー処理は難しい。Goto文でまとめて蹴散らすのがベストなんだろうか?心理的抵抗があるけど。抽出しろ、設計が大事とのお話なんですが。

バッファリング

先日、ちょろっと書いたioのバッファリング、やっぱりbufioを通さないとダメなようですね。こ辺はJavaと同じか。

bufioのNewReaderの引数がReaderしかないので自前でやるしかない模様。ioutilのReadAllだとbufioは噛ませてないですが、binary.Bufferを噛ませていると。

src/io/ioutil/ioutil.go - The Go Programming Language

この辺りのお作法を覚えるのは大変ですね。自分もbinaryをいじる必要があるのですが、binary.bufferでなくbufioになるのは読み込みをstreamingで行う必要があるのと、書き込みがserialに書けないからなんですが。

バッファリングについて調べていて見つけた記事が面白かったんですが、

Go言語で競技プログラミングするときに使ってる入出力 - くじらにっき++

Goで競技プログラミングをされている方の記事。これを読むとやはり競技プログラミングのような大量なデータを処理する場合に、やはりScannerだけでは遅く、bufioを使う必要があると書かれていますね。

また出力でも標準出力はバッファリングされていないのでfmt.Printのみでやるとかなり遅くなってしまうと。やはりbufferingは大切ですね。

Goで競技プログラミングに関してはこんな面白い記事もありました。

競プロのテストを「go test」でやりたかったので作った - Qiita

やぁ、まだtestまで全然行けてないですわw

分割しようよ

はてダと違って1日に1投稿という制限が無いんですよね。もっと分割して投稿するべきなんですが、まだブログになれていないな :-)

*1:これは最近、about:blankが使えるように戻った模様だ

*2:これは削除された

*3:このせいでChromeを使うのを完全に止めた。今はVivaldiやBraveをFirefoxと併用している

*4:Go言語はGoogleの前から別の物が存在していたが

*5:実際、Androidでは多くの機能がサービス化され非公開となった