Programming in Scala Fifth Edition

以前アーリーアクセスで購入して手をつけていなかったScala3対応のProgramming in Scala Fifth Edition、ようやく完成したようなので最近発売されたForth Editionの日本語版と見比べながらざっと目を通してみました。

Scala言語の入門書としては以前よりもだいぶ整理されて真っ当な作りになっていると思います。すでに標準ライブラリから外れて久しいXML、パーサコンビネーター、Swingはもちろん、存在がいまいち謎だった最後のスプレッドシートの章もまるっと削除されていますし、既存の章の順番も見直されています。元々Programming in Scalaの構成はだいぶ謎な気がしていたので、今回Scala3へのアップデートにあわせて一新されたのはよかったのではないかと思います。

個人的にはJavaとの相互運用やFutureの章が削除されてしまったのは残念かなと思うのと、この書籍の出自を考えると仕方ない気もするのですが、assertはともかくテストについては要らないんじゃないかなぁとは思いました。それまで全くsbtに触れてないのに突然sbtの設定とか出てくるし…。

また、サンプルコードが全編Scala3で新たに導入されたインデント構文(quiet syntax)になっており、Scala2と一切互換性がないという点はかなり気になりました。Scala3の新機能は一応それとわかるように書かれているのですが、逆にScala3で廃止された機能については特に明記されていません。つまり、本書はScala3から新たにScalaを始める人向けの書籍であるということです。

すでにScala2をバリバリ使いこなしている人はこの本を読まなくてもScala3に追従するためのリソースはオンラインで入手できると思うのでいいとして、これからScalaを学習するという人のうち、このタイミングでScala3で学習し始めて、Scala2は一切触らないという人が一体どれだけいるのか…。数年後、Scala業界の大半がScala3に移行した後であればよいと思うのですが、今入門書として読むのは結構微妙かもという気がしました。

世の中的にScala3への移行はなかなか一筋縄ではいかなそうな気がしていますし、正直脱落する個人や企業も出てくるのではと思っているのですが、特に新規参入者の方がこの本で混乱してしまわないことを祈るばかりです。

その他のScala3対応書籍

オライリーのProgramming ScalaもScala3対応にアップデートされているようなのですが、こちらはどんな感じなのでしょうか。目次を見ると一応Scala2からScala3へのマイグレーションについての記述があるようですし、型クラスについてもScala2とScala3双方での解説があるようなので、今読むのであればこちらの方がいいかもしれません。

日本語でのScala入門書

さて、日本では先日Forth Editionの翻訳版が発売されました。何気なく書籍情報を眺めていたら監訳者に前職の同僚氏が!これはお布施せねば!ということで早速クリックしました。こちらはScala 2.13対応ですが、まだまだ現場では当面Scala2が主流で使われると思われるので、今Scalaを学習するのであればこちらもオススメです。

日本語だと技評さんの実践Scala入門も実用的でいいですね。

Trino/Prestoの互換性調査補助ツールを作った

Trino/Prestoをバージョンアップする際には事前に動作の互換性検証などを行なっているのですが、検証作業自体は以前Presto Conference Tokyo 2020でも紹介させていただいたquery-simulatorという内製のツールを使って自動化されているものの、実際に非互換の挙動を発見した後の原因調査(原因のコミットを特定してバグかどうかの判断をする)については引き続き地道な作業が必要な状態でした。

Trino/Pretsoは開発が非常にアクティブで、1回のリリースに数百のコミットが含まれます。1年程度バージョンアップを怠っているだけでも変更が巨大すぎてコードの変更履歴から原因を特定するのは非常に困難になります。そこで、まずは複数バージョンのTrino/Prestoでクエリの実行結果を比較することで変更が導入されたバージョンを特定し、その後、そのバージョンのコミットの中から原因となったコミットを調査するという作業を行なうことになるわけですが、このうちバージョンを特定する部分の作業は自動化できそうということでツールを作ってみました。

github.com

まだ、きちんとしたインターフェースを作っていないのでScalaコードを書いて実行する必要があるのですが、以下のようなコードで指定した範囲のバージョンのTrino/Prestoをdockerで起動し、クエリの実行結果を比較しつつ二分探索で変更が導入されたバージョンを自動的に検出してくれます。

import io.github.takezoe.trino.checker._

val checker = new BinaryVersionChecker(new DockerQueryRunner())
val results = checker.check(Range.inclusive(317, 350), "SELECT null GROUP BY 1, 1")
printResults(results)

実行結果はこんな感じになります。このケースでは339が変更が導入されたバージョンということになります。

✅ 317: Right(37a6259cc0c1dae299a7866489dff0bd)
❌ 350: Left(java.sql.SQLException: Query failed (#20210526_154140_00004_yzz4q): Multiple entries with same key: @38f546e: null=expr and @38f546e: null=expr)
✅ 334: Right(37a6259cc0c1dae299a7866489dff0bd)
❌ 342: Left(java.sql.SQLException: Query failed (#20210526_154251_00003_2km75): Multiple entries with same key: @63f11e0f: null=expr and @63f11e0f: null=expr)
✅ 338: Right(37a6259cc0c1dae299a7866489dff0bd)
❌ 340: Left(java.sql.SQLException: Query failed (#20210526_154338_00002_2xtvz): Multiple entries with same key: @76615946: null=expr and @76615946: null=expr)
❌ 339: Left(java.sql.SQLException: Query failed (#20210526_154358_00002_jdnni): Multiple entries with same key: @4aca599d: null=expr and @4aca599d: null=expr)

本当はコミットを特定するところまで自動化できると楽なのですが、バージョンを絞り込めばコミット履歴からあたりがつくこともありますし、実際に動かして動作確認するにしてもTrino/Prestoをソースからビルドする必要があるのでIDE上で実行したほうが早いかなという感じです。ニッチなツールなのでさほど頻繁に利用する機会はないかもしれませんが、こういった簡単なツールでも作業の効率化に役立つので、もっと早く作っておけばよかったなぁと思っています。

ユニコーン企業のひみつ ―Spotifyで学んだソフトウェアづくりと働き方

話題のユニコーン企業本を早速読んでみました。分量もそれほどでもありませんし、日本語なので1日で読み切ることができました(翻訳ありがたい)。

内容については散々色んなところで書かれていますので今更ここで書くこともないかと思いますが、この本に書かれている内容はあくまで成功したテック企業のひとつの事例として捉える必要があるかと思います。

ソフトウェア開発プロジェクトの方法論であるアジャイルと比べ、エンジニア組織論はスコープが広く影響を及ぼす要素が多い割にサンプル数が少なく、本来はなかなか一般化して語ることの難しい領域だと思います。その割にこの書籍では(話をわかりやすくするために敢えてやっているのだと思いますが)大企業×スタートアップという二元論的な書き方や、「エンタープライズ」「ユニコーン」「テック企業」など言葉のチョイスからして煽り気味なところもあり、読者層によっては誤った受け止められ方をされ兼ねないと感じました。

エンジニア組織の話については思うところが多々あるのですが、ありすぎてこれ以上の感想をここで書くことはできそうにありません。また、こういった話題からは当面距離を置いてソフトウェアエンジニアリングに集中したいという個人的なモチベーションもあり、今のタイミングでこの本を読んだのは失敗だったかもしれないという気持ちもあります。良い本だとは思うのですが、残念ながら自分はピュアな気持ちで読むことができなかったです。

仕事ではじめる機械学習 第2版

トレジャーデータでの同僚である@chezouさんが著者に名を連ね、名著と名高い「仕事ではじめる機械学習」。恥ずかしながら第1版は読んでいなかったのですが改訂版が出たとのことでGW休暇中に読んでみました。

仕事ではじめる機械学習 第2版

仕事ではじめる機械学習 第2版

噂通りの名著というか怪著ですw タイトルの通り、機械学習アルゴリズムだけでなく、学習結果の評価方法やデータの集め方、システム構成やモニタリングなど運用にあたって留意すべきことなど「業務として」機械学習に取り組みにあたって留意すべき事項が書かれています。果ては機械学習の導入にあたっての立ち回り方など割と身も蓋もない(けれども実際のところ重要な)ことまで堂々と書かれていて、日本ならではの面もあるかもしれませんが、オライリーからこんな本が出せるのかと感動すら覚えてしまいました。

第1版は読んでいないので正確な差分はわからないのですが、この第2版ではML Ops機械学習モデルの検証、バンディットアルゴリズム、オンライン広告での機械学習といった章が新規に書き起こされているようです。ML Opsの章に関してはこの書籍でも書かれている通り、なかなかこれを使っておけばOKというものがなく(クラウドサービスが提供するサービスはガバナンス上の問題で使いにくかったり、OSSのものは既存インフラとの連携面で問題があったり等々…)内製してしまうパターンも多いのではないかと思いますが、今後この分野がどのように発展・定着していくかは興味深いところです。

全編通して著者の方々の知見に満ち溢れており、現場でのご苦労が偲ばれると共に読み物としても面白いので、業務で機械学習に関わりのないソフトウェアエンジニアの方が読んでも面白いのではないかと思います。今のところ日本語でしか読めない書籍ですので、日本人であることに感謝しつつ読ませていただきました。

NetflixがOSS化したScala/Spark用ノートブックPolynoteを試してみる

もう1年以上前の話になりますが、NetflixがSpark対応のScala用ノートブックPolynoteをOSS化したという話がありました。

netflixtechblog.com

既存のノートブックではScalaを使っていてもコード補完などがあまり効かないものが多く、まとまったコードを書くときは結局IDEを使うという感じになりがちなのですが、PolynoteはScala第一言語としてサポートする珍しいノートブックで、コード補完などの機能も充実しているようなので遅ればせながら試してみました。

インストール

Sparkを使う場合、まずは先にSparkをインストールしておく必要があります。とりあえずローカルモードで動かすだけであればSparkのリリースディストリビューションをダウンロードして適当なディレクトリに展開しておくだけでOKですが、Polynoteは内部的にspark-submitコマンドを実行するので、最低限の設定としてSPARK_HOME/binディレクトリを環境変数PATHに追加しておく必要があります。

続いてPolynoteのリリースページからpolynote-dist.tar.gzをダウンロードして適当なディレクトリに展開します。

github.com

必要なPythonライブラリをインストールしてからpolynote.pyを実行するとhttp://localhost:8192/でノートブックが起動します。

$ pip install -r requirements.txt
$ ./polynote.py

Sparkを使うにはノートブックの設定でspark.masterを設定しておく必要があります(設定の変更後は「Save」もしくは「Save & Restart」をクリックして設定を反映する必要があります)。

f:id:takezoe:20210503133722p:plain

使ってみる

ノートブックのセルにはMonaco Editorが使われており、確かにScalaのコード補完もばりばり効きますし、変数のインスペクションができたりと、Scala向けに作られているだけのことはあります。SparkのDataFrameだけでなくScalaのコレクションをプロットすることもできます。

f:id:takezoe:20210503134157p:plain

f:id:takezoe:20210503133802p:plain

これはSparkを使わなくてもちょっとしたScalaコードを動かしたりするのにいいかも…と思ったのですが、少し使っているとかなり編集がもっさりしてきてカーソルを動かすだけでも一瞬間が開いてしまう感じになってしまいました。

ローカルで動かしていたのでマシンスペックの問題もあるのかもしれませんが、ちょっとしたScalaコードを1セル内で編集しているだけでこのもっさり感だとちょっとPolynote上でScalaコードを編集するのは厳しいかもしれないという気がしました。機能的にはよさげなだけに勿体ないなぁという気持ちです。

Polynoteの実装について

PolynoteはScalaで書かれているようなので軽くコードも読んでみました。

まとめ

Polynote、もうちょっとサクサク動くようになってほしい!

Apache Spark用のスタンドアロンJDBCドライバ

SparkにJDBCでアクセスするにはThriftserverを入れたりHive Metastoreが必要だったりで色々面倒なのですが、単体で利用可能な方法はないのかなと思って探してみたところ、以下のものを見つけたので試してみました。

github.com

このJDBCドライバは以下のようなURLでJDBC経由でSparkSQLを使うことができます。

com.zensolution.jdbc.spark:/Users/foobar/temp/console?format=csv&csv.header=true&csv.delimiter=;

SQL内でアクセスされているテーブルはクエリの実行前にテンポラリビューとして自動的に登録されます。たとえば以下のようなSQLを実行したとします。

SELECT * FROM people

このJDBCドライバはまずクエリをパースし、このクエリの実行にpeopleテーブルが必要であることを認識します。そして/Users/foobar/temp/console/peopleディレクトリ配下に保存されているCSVファイルを参照するDataFrameを作成し、SparkSQLからアクセスできるようにテンポラリビューとして登録します。前述の通りこのプロセスはクエリの実行前に自動的に行われます。

このJDBCドライバには動作するのですが、2つの不満点がありました。1つはSparkのローカルモードでの実行しかサポートしていないこと、もう1つはJDBC URLで単一のデータソースしか指定できないことです。そこでこれらの不満点を改善すべく、フォークして変更を加えてみました。

github.com

まずはリポジトリをクローンしてJDBCドライバのjarファイルをビルドします。以下のコマンドでbuild/libs/spark-jdbc-all.jarが生成されるので、このファイルをクラスパスに追加します。

./gradlew clean shadowJar

次に以下のような設定ファイルを作成します。

{
  "tables": [
    {
      "name": "people",
      "path": "examples/src/main/resources/people.csv",
      "format": "csv",
      "options": {
        "header": "true",
        "inferSchema": "true",
        "delimiter": ";"
      }
    },
    {
      "name": "users",
      "path": "examples/src/main/resources/users.orc",
      "format": "orc"
    }
  ]
}

以下のようなURLで任意のスタンドアロンSparkクラスタ(もしくはローカルモード)へのJDBC接続が可能です。

実用的かどうかはさておき、一応動くものができたので個人的には満足ですw

Spark in Action, Second Edition: Covers Apache Spark 3 with Examples in Java, Python, and Scala

こちらもManningの元旦全品半額セールで購入して積んでおいたものです。

かなりページ数はあるものの、あまり効果的とは言えないような図が多用されていたり、丁寧というよりは冗長な記述が多かったりと、ページ数の割に肝心の内容は薄い印象です。コードの説明もまず実行結果を示してからコードの説明をするという流れで、自分の好みの問題もあるかもしれませんが読みにくいことこの上なかったです。もちろん英語だからということもあるとは思いますが、個人的には読み進めるのが苦痛な感じでした。

また、サブタイトルに「Examples in Java, Python, and Scala」とありますが、書籍内のサンプルはすべてJavaで書かれているのも意表を突かれる感じです。正直Spark3対応のSpark入門書であればオライリーのLearning Sparkの方がおすすめです。

takezoe.hatenablog.com

ただ、独自データソースの作り方などカスタマイズ部分にも触れられているのは本書のアドバンテージと言えるかもしれません。また、JavaからSparkを使いたいという場合も本書のサンプルコードが参考になるのではないかと思います。

全体的にはかなり初心者向けのSpark入門書で、内容の薄さやサンプルコードがJavaで書かれているという特殊性もあり、わざわざ英語でこの本を読まなくてもいいかなという感想です。改訂版も出ているということはFirst Editionもそれなりに売れたのではないかと思うのですが、Manningにしては若干期待外れの一冊でした。