APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

mercari.go #6 参加レポート

はじめに

先進サービス開発事業部の山岡です。

今日はいつもと趣向を変え、先日参加させて頂いたmercari.go #6のレポートを書きたいと思います。

 ※資料は後日公開されるとのことなので公開されたらそちらへもリンクを貼ります

JavaとGo言語を比較した話 @kentanさん

  • Fake Google SearchをJavaで書いた
    • channelが無いので汚くなるかと思ったが、CompletableFutureを使うことで案外奇麗に書けた
    • ただJavaだと複数のクラスを理解しないといけないので、初見だとGolangより手間がかかる
      • channelだけ知っていれば同等のコードが書けるGolangのシンプルさ凄い
  • オブジェクト指向ではない
    • 構造体の埋め込み等を使えばそれっぽく書けるけど、闇が多そう
    • わざわざオブジェクト指向で書く必要は無いんじゃないかな
  • 質疑
    • ジェネリクスが欲しくなることはありますか?
      • 月に何度もあるが、Golangに入ってはGolangに入ろうと思っている

真面目にある程度時間かけて向き合った言語はGolangだけで他の言語をよく知らないので興味深かったです。自分でも言語による書きっぷりの違いを体験したくなったので、Golang以外で 比 較 的 書いたことがある言語のPythonでFake Google Searchを書いてみようと思います。

GAEやDatastore使っててもよく感じますが「アレができない、他所のプロダクトではできるぞ」だけでなく、なぜそれができないのか・できない事によるメリットが何かについて意識を向けてみるのが良いのではと思っています。

「機能が豊富 ⇒ 優れている」は必ずしも成り立ちません。

YAML をテストする @babarotさん

  • Terraform, k8s Manifestをチェックするツールを作った
  • Policy as Code(設定ファイルにおける「こうあるべき」を残す) 例: HashiCorp Sentinel
    • 制約項目(リミット系)
    • レビュー項目(スタイルガイド系)
    • 例えば「デプロイするサーバーの数は30~100の間であること」と定義すればタイポや見逃しによる事故を防止できる
  • Sentinelのようなことをk8sのManifestでもやりたかった
    • Stein
    • HCLでルールを定義しそれを逸脱する内容があった場合は失敗(リターンコード1)する
    • 例えばk8sで metadata.namespace のように「書式上省略できるけど記述必須にしたい」といったケースで使える
    • これを毎回人力で注意深くチェックするのはしんどいので機械的に指摘できるようにした
  • なぜGolang?
    • GolangにHCLのライブラリがあるのでDSLを作りやすい
    • 続きはBlog記事で!

Policy as Codeという概念は何度か聞き覚えがあり、自動化・効率化の話題でよく耳にする「機械にできることはなるべく機械にやらせて価値を生む時間を最大化しよう」を実現する方法の一つであると思います。

GolangからHCLを扱えるライブラリがあるのは知らなかったので今度何かツールを作る機会があれば選択肢の一つに加えてみます。

sliceのコピーで起きた不思議な話 @t-naritomiさん

  • sliceにappendする際、cap0で作ったものとそれ以外で作ったもので挙動が違うケースがある
  • capを指定してint型のsliceを定義(s1) -> 別の変数に := でコピー(s2) -> s1に 5 をappend -> s2に 6 をappend -> fmt.Println(s1, s2) で中身を確認

自分の理解を深めることも兼ねて少し私なりに解説します。

まずGolangの配列である array の長さは不変なもので、何らかの事情で長さが不足する場合はもっと長い別の配列を作り既存の内容をそこへコピーしなければなりません。しかしそれを毎度自分で書くのは不便なので slice というarrayを可変長配列かのように扱える機能が提供されています。

このsliceは「arrayへのポインター」「長さ (len)」「容量 (cap)」の3つから構成されており、特段指定しない作り方をした場合はlenとcapが同じサイズで作られます(下記参考コード参照)。append()という関数を使うとsliceを拡張する(つまり内部のarray末尾へデータ追加する)ことができるのですが、capを超える拡張を行おうとした場合は新しいarrayが生成されポインターがそちらに変更されます。

[参考]sliceの作り方2パターン

// len, capを指定する
s1 := make([]int, 3, 5)
fmt.Printf("s1: len %d, cap %d\n", len(s1), cap(s1)) // s1: len 3, cap 5

// len, capを指定しない
s2 := []int{0, 1, 2}
fmt.Printf("s2: len %d, cap %d\n", len(s2), cap(s2)) // s2: len 3, cap 3

https://play.golang.org/p/8_IBYZNaMN4

そしてGolangの引数は基本的に値渡し(コピー)でありsliceも例に漏れませんが、このコピー時点では内部で持っているarrayへのポインターは同じアドレスになります。そしてappend時にcap不足でarrayが再生成されると元々同じarrayを参照していた2つのsliceが別のarrayを指すようになってしまうため、こういう挙動が発生します(この辺が文章だと大変ややこしいのですが……)。

 ※冒頭にも書きましたが後日資料が公開されるそうなので詳しくはそちらを参照して下さい

この手のわかりにくい挙動は「どうすると何が起こるか?」よりも「どうしてこうなるのか?」をきちんと理解することが非常に重要です。そういったストックが沢山あるほどハマった時に救われやすくなるので積極的に情報収集したいと思います。

Writing a Compiler in Go @kitasukeさん

技術的に高度過ぎてついていけませんでした……。申し訳ありません。精進します。

mapとheapを自作してみた話 @dqneoさん

本題の内容とは逸れますが、何か難しいことへ挑戦する際に開始のハードルを下げるというのはとても有効なアプローチだと思います。実際に自分も「任意の型のkey/valueを任意の数扱う」だとかなりハードルが高く頭で動作のイメージを作ることができませんでしたが「intのkey/valueを2つの固定長で扱う」に絞った場合の解説はすんなりと理解することができました。

こういった問題の分割・単純化は学習を進めていく上で重要な手法だと思います。

「Golang純正コンパイラーではビルドエラーが出るコードだけど細かいことは気にしない。俺のコンパイラーでは動く!」はこの日一番の名言だったと思います(笑)学習においてはこうやって楽しんで進めるのが継続性という面で重要なのではないかと気が付きました。

ポメラニアン専用Goコンパイラ @tenntennさん

  • メルカリ社内Slackに (´・ω・`) の顔文字を自分専用だ、と主張するreactionがある
  • Golangのコンパイラーを改造して (´・ω・`) という文字列を それ僕専用のやつです(´・ω・`) と自動的に置換するようにした

内容はネタ系でしたがGolangコンパイラーのことを少しだけ知ることができ、技術的にも面白かったです。

コンパイラーについてはプログラミング入門書の先頭に書いてあるくらいのざっくりとした知識しかありませんでしたが、その中身の動作を追ってみるのも面白そうです。

コンパイル前にソースコードの文字列を置換するという単純な方法ではなく、コンパイル時の最適化処理でこれを行うという技術力の無駄遣い感が凄く好きです。

自作のエラーハンドリングライブラリを宣伝します @morikuniさん

前半はこれまでのエラーハンドリングパターンと課題、既存の解決方法について振り返りがされておりとても丁寧で網羅的な導入でした。全体的に話が非常に整理されておりコンサルティングの提案でも受けているかのような感覚がありました。

私自身まだ既存のハンドリングしかしたことがないのですが、新しいエラーを作るときにイチイチ型を作ったりしなくていいことや failed to do something 地獄が無いのは良さそうだと感じました。

xerrorsも含め実際に自分で書いてみてどんな課題があるのか、どのような感触なのか探ろうと思います。