KEIS BLOGは株式会社ケイズ・ソフトウェアが運営しています。

KEIS BLOG

Goでlo.Mapやlo.Filterは本当に読みやすいのか?

Go言語は、その設計哲学から「シンプルさ」や「明快さ」を重視しています。その一方で、Goコミュニティには公式思想からやや外れた「便利ツール」や「シンタックスシュガー的な関数」を提供するサードパーティライブラリが数多く存在します。その代表格の一つが、samber/lo ライブラリです。

lo、結構実務では使ってるんですが、簡単に言うと、Go標準にはないMapやFilterといった関数型プログラミング風のユーティリティ関数を提供するものです。これらは、一見するとコードを短く・明快にできるように思えるかもしれませんが、本当に「読みやすい」と言えるのでしょうか?

ということで、使っておいてから考察するのもあれなんですが、考えてみました。

Go言語の基本スタイルとlo関数群

Goは基本的に以下のスタイルを推奨しています。

明示的なforループとif条件分岐:

Goでは、コレクションの走査はfor ... rangeで行うことが基本です。

エラー処理の明示性:

Goは関数型のチェーンを多用するよりも、途中でエラーチェックを行いながら処理を進めることが多いです。
こうしたコードスタイルは、初学者にとってもわかりやすく、ベテランにとってはロジック追跡が明瞭になるという利点があるわけですが、

一方で、lo.Mapやlo.Filterは以下のように、書けるわけです。

// 例: []intを2倍にする
doubled := lo.Map([]int{1, 2, 3}, func(x int, i int) int {
return x * 2
})

// 例: 負数をフィルタリング
positives := lo.Filter([]int{-1, 0, 1, 2}, func(x int, i int) bool {
return x > 0
})

一連の処理を関数の引数として渡すことで、ループと条件分岐をひと塊で表現できます。関数プログラミング的なスタイルになり、コードがスマートに見えるかもしれません?

読みやすさの評価ポイント

「読みやすいかどうか」は、結局以下のような観点から評価されます。

Goの開発文脈との整合性:

Goはforループで回しながらifで条件分岐する、という手続的な処理を書く文化が強固です。その中でlo.Mapやlo.Filterを導入すると、一部だけ関数型スタイルになります。
一貫したスタイルが保たれていないと、コードリーダーは頭のモードを切り替える必要が出てきます。その結果、かえって読みづらく感じる人もいると思います。

チームの合意:

「読みやすさ」は客観的なものではなく、プロジェクトやチームメンバーの慣習や好みに大きく左右されます。普段から関数型スタイルやloを積極活用しているチームなら、lo.Mapやlo.Filterは自然で読みやすいかもしれません。
しかし、Goの公式ドキュメントや標準ライブラリに慣れた人には、loの関数型ユーティリティは「Goらしくない」書き方に映り、「このコード何をしているんだっけ?」と戸惑う場面が増えるかもしれません。

トレードオフの認識:

lo.Mapやlo.Filterを使うとコード行数が減ったり、処理が一箇所にまとまったりして、一見シンプルに見えることがあります。しかし、匿名関数を渡すというスタイルは、慣れていないと逆に理解コストを上げます。
さらに、デバッガーを使ったステップ実行や、途中の状態をログ出力するなど、手続き的なデバッグがしづらくなる場合もあります。可読性は短さだけでは決まらず、保守やデバッグの容易さも「読みやすさ」の一部です。

結局、選択は利用者次第

まあ結局、程度問題なのかなあと。使い散らかすと読みにくくなるんですよ。要所で使うと行数圧縮にもなって読みやすく感じます。

Goらしさを重視するなら:

forループとif、あるいは素のappendを組み合わせて処理を書く方が、誰が読んでも「Goっぽい」コードになります。新たな抽象化や関数型構造を持ち込まないことで、一般的なGo開発者の期待に沿った読みやすいコードが書けます。

関数型スタイルに慣れた開発者には:

lo.Mapやlo.Filterは、処理を「データ変換」という観点で統一的に記述でき、関数の合成などがしやすくなります。これに慣れ親しんだチームなら、逆にloを使わないほうが冗長に感じそうです。

まとめ

lo.Mapやlo.Filterなどの関数は、「Go本来の手続的スタイル」を離れた関数型プログラミング的な表現力を提供します。これらが読みやすいかどうかは、

  • チームのコーディング規約や文化
  • 開発者の経験・好み
  • デバッグ・保守のしやすさ

によって左右されます。Goは言語仕様上関数型スタイルを禁じてはいませんし、サードパーティによる拡張を自由に許容しています。しかし、その結果として生まれたloの関数群を使うかどうかは、「読みやすさ」や「整合性」を考慮して判断すべきでしょう。最終的には、チームで話し合い、適切なスタイルを選択するってことになるんでしょうね。