kubell Creator's Note

ビジネスチャット「Chatwork」のエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

Scala 3に備える社内勉強会の取り組みについて

みなさん、こんにちは!Chatworkの原田 (@shinharad) です。

今回は、最近弊社で取り組んでいる Scala 3 の社内勉強会について、このような流れでご紹介したいと思います。

  • 最初に Scala 3 の今の状況をざっと確認
  • Chatworkでは Scala 3 の社内勉強会を継続的に開催している
  • 勉強会で使っている資料、情報源のご紹介
  • 勉強会でやったことをいくつかご紹介

Scala 3 の状況

まずは、Scala 3 の今の状況について軽く確認しておきましょう。

待ちに待った Scala 3 は、この記事を執筆している時点での最新版が Scala 3.0.0-RC3 で、大きな問題がなければこの5月中にも安定版がリリースされる予定です。

Scala 3 は、Scala 2 で不便だった機能や、煩雑で分かりづらかった機能が大幅に見直されました。

具体的には、代数的データ型(Algebraic Data Types)の定義が enum で簡潔に書けるようになったり *1trait にパラメータを持たせられるようになったり *2、制約が厳しくていまいち使いづらかった値クラス (Value classes) の代替手段として Opaque Type Aliases が導入されたり *3 *4、新しい型として Union Types が追加されたり *5、一つで様々な役割を持っているが故に初学者が混乱しがちだった implicit が大幅に整備されたり *6、拡張メソッドや型クラスに専用の構文が追加されたり *7 *8、今までコンパイラプラグインで実現していたことが標準でできるようになったり *9、いつまでも experimental のままだった Scala 2 のマクロは廃止され Scala 3 の将来のバージョンと互換性のあるマクロが追加されたり *10 ...

(はっ、いけない...Scala 3 への愛が溢れすぎてついたくさん書きすぎてしまった…)

今挙げた例はほんの一部です。このように Scala 3 は非常に大きなアップデートとなりました。

ワクワクしますよね?自分はとてもワクワクしています。

ChatworkではScala 3の社内勉強会を開催しています

Scala 3 をプロダクトで使い始める時期はまだ未定ですが、このように非常に大きなアップデートになったので、事前に社内のメンバーとキャッチアップしておきたいなと考え、Chatwork ではだいたい2〜3週間に1度くらいのペースで Scala 3 の社内勉強会をオンラインで開催しています。もちろん任意参加で。

勉強会のモチベーションとしては、単純に Scala 3 の新機能に今からでも触れておくことで、Scala 3 で何ができるようになったのかを知っておきたいということと、将来的な Scala 3 への移行のために今からでもできる準備をしておきたいなと考えています。

今からでもできる準備といえば、例えば Scala 3 では、インデントベース *11 や "quiet" シンタックス *12 など、Scala の新しい記述スタイルがオプショナルとして追加されました。それによって、今までの書き方をそのまま継続するのか、新しい書き方を採用するのかを選択する必要があります。それ以外に、Scala 2 で書かれている既存プロダクトを今後 Scala 3 へ移行するにしても、今の段階でどのような準備が必要なのか、またはどのような移行方法やツールがあるのかを事前に知っておくことで、今後チームで議論する際の判断材料として活かせればなと。

個人的な感触として、Scala 3 を実際のプロダクトで使うときは、事前にチームで議論が必要だったり、開発していく中で様々な選択を迫られる場面が、移行初期段階では多くなりそうだなと感じています。

勉強会の資料、情報源など

Scala 3 の情報源としては、現段階では公式サイトやその周辺のサイトが良さそうです。つまりこの辺り。

一応書籍の方も確認してみましたが、Scala 3 に対応している、もしくは今後内容が Scala 3 にアップデートされる予定のものはこの辺りがあるかなと思います。

ただし、現段階でいずれも Early Release となっており、今後内容が変わる可能性があるのと、更新されるまでのラグが公式サイトよりも大きいことを考えると、勉強会の資料としてはまだまだ使いづらいかなという印象です。

そのため勉強会では、先に挙げた公式サイトとその周辺のサイトを中心に扱うことにしています。

ただ、ひとつ問題があって、これだけ大きなアップデートなので、公式サイトの例えば Reference を見ても、どこから見て良いのかが分かりづらいという点です。一応カテゴリ分けはされていますが、上から順番に見ていったとしても難易度はバラバラなので、どの順番で見ていくのかは悩ましいところです。

そこで、勉強会でこれらを扱いやすくするために、Scala 2 経験者向けの Scala 3 公式サイトのガイド的なものを趣味で作りました。

github.com

こちらは日々更新していますが、特徴としてはこんな感じです。

  • 公式サイトやその周辺のサイトから、個人的なおすすめとしてどの順番で見れば分かりやすいかを案内している
  • 対応するサンプルコードが含まれているので、手元の環境ですぐに動かすことができる

勉強会はこれを使って進めています。

勉強会はとても刺激的

勉強会では、毎回 Scala 3 の特定の新機能にスポットを当てて、参加者で議論したり、モブプロで「こんな書き方をしたら Scala 3 コンパイラどうなる?」のような実験みたいなこともしています。

ここで、いくつか勉強会の中で実際にやったことをご紹介したいと思います。

インデントベースと "quiet" シンタックスどうする問題

先ほども軽く触れましたが、今まで Scala 2 に触れてきた方が、Scala 3 でまず驚くのが、このインデントベースの書き方ではないかなと思います。公式サイトでは Optional Braces でこの辺りのことが記載されていますが、Scala 3 では中括弧 {} が省略可能になって、このようにインデントベースで書けるようになりました。

trait Element:
  def above(that: Element): Element =
    val this1 = this.widen(that.width)
    val that1 = that.widen(this.width)
    elem(this1.contents ++ that1.contents)
...

その他、"quiet" シンタックスも追加されました。これは、New Control Syntax に記載されていますが、例えば if であれば、if x < 0 then ... else ... のように、条件式の括弧 () が省略可能で、その後に then を持ってくる書き方が追加されました。

if x < 0 then
  "negative"
else if x == 0 then
  "zero"
else
  "positive"

これらはいずれもオプショナルなので、採用しても良いし、従来の書き方をしても良いということになっています。つまりこれらは自由に組み合わせることができるのです。

クラシックシンタックス*13 インデントベース
クラシックな制御構文 OK OK
新しい制御構文
("quiet" シンタックス)
OK OK

ただし、1つのコードベースでこれらのスタイルが混在するのは良くないので、いずれはどの組み合わせにするのかをチームで話し合って決める必要があります。

これについては弊社でも色んな意見が出ていて、

  • 「オフサイドルールは受け入れられない」
  • 「Scala 3 コンパイラが一括変換してくれるのだから *14、新しい書き方に統一すれば良いのでは?」
  • 「いいや、インデントベースは嫌だ。-rewrite -noindent & -rewrite -old-syntax して従来の書き方に戻しちゃうよ?」
  • 「でも慣れるとインデントベース書きやすいんですよね」

など様々です。

勉強会はあくまでも結論を出す場ではなく、今後チームで議論するための準備をする場と考えているので、今のうちから各自思っていることを吐き出しておくのは良い事かもしれないです。

ただ、ひとつ言えることは、Scala 3 の公式サイト も書籍の Programming in Scala, Fifth EditionProgramming Scala, 3rd Edition もインデントベース& "quiet" シンタックスで書かれているということです。オプショナルと言いつつも意外と本気なんですよね...。つまり、これから Scala を学び始める人は、インデントベース& "quiet" シンタックスで入門するケースが多くなりそうですね。そうなったときに、チームの方針としてどうするのかは、議論が必要そうです。

Scala 3 の新機能の有用性を確認しつつも色々実験してみる

勉強会では、毎回 Scala 3 の特定の機能にスポットを当てて、その有用性を確認しているわけですが、参加者の中には実験大好きな人がいて、「普段はこういう書き方はしないだろうけど、実際そう書いたらどうなる?」みたいな流れで実験が始まります。実はこれが毎回楽しかったりします。

ひとつ例を挙げたいと思います。Scala 3 の新機能で、Trait Parameters *15 が追加されました。これは個人的に待ち望んできた機能でもあるのですが、この Trait Parameters に対してこのような実験をしてみました。

以下は、公式サイトから抜粋したサンプルコードになります。

// trait の Greeting には name というパラメータを設定できる
trait Greeting(val name: String):
  def msg = s"How are you, $name"

// Greeting のパラメータを省略して FormalGreeting に extends する
trait FormalGreeting extends Greeting:
  override def msg = s"How do you do, $name"

この FormalGreetingclassextends する場合は、Greetingname を指定した形でこのように書くのですが、

class A extends FormalGreeting, Greeting("Bob")

@main def foo(): Unit =
  println((new A).msg)
  // => How do you do, Bob

では、Greetingnameoverride して、それを classextends した場合の msg メソッドの結果はどうなるでしょうか?

trait FormalGreeting2(override val name: String) extends Greeting:
  override def msg = s"How do you do, $name"

class B extends FormalGreeting2("Bill"), Greeting("Bob")

答えはこうなります。override してるので、まあそりゃそうだよねという挙動ですが、こういう紛らわしい実装もできてしまうようです(まあ、やらないですけど)

@main def foo(): Unit =
  println((new B).msg)
  // => How do you do, Bill

おわりに

今回は、最近弊社で取り組んでいる Scala 3 勉強会のご紹介でした。

勉強会自体は今後もしばらく継続する予定で、取りあえず Scala 3 の新機能の中でも基本的なものと、Contextual Abstractions はやっておきたいなと。それと、Scala 3 の移行方法についても議論していきたいなと考えています。その先の、メタプログラミングや型レベルプログラミングについては、別の機会にやるかもしれません。

こんな感じで、Chatwork では Scala 3 の準備を着々と進めています。

では。

*1:Algebraic Data Types: https://dotty.epfl.ch/docs/reference/enums/adts.html

*2:Trait Parameters: https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html

*3:Opaque Type Aliases: https://dotty.epfl.ch/docs/reference/other-new-features/opaques.html

*4:代替手段が追加されたからといって値クラスが廃止されるわけじゃなくて、今後 project Valhalla によってJVMでネイティブにサポートされるようになれば、また使用する機会が訪れるかも

*5:Union Types: https://dotty.epfl.ch/docs/reference/new-types/union-types.html

*6:Contextual Abstractions: https://dotty.epfl.ch/docs/reference/contextual/motivation.html

*7:Extension Methods: https://dotty.epfl.ch/docs/reference/contextual/extension-methods.html

*8:Implementing Type classes: https://dotty.epfl.ch/docs/reference/contextual/type-classes.html

*9:Type Lambdas: https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html、Kind Polymorphism: https://dotty.epfl.ch/docs/reference/other-new-features/kind-polymorphism.html など

*10:Metaprogramming: https://dotty.epfl.ch/docs/reference/metaprogramming/toc.html

*11:Optional Braces: https://dotty.epfl.ch/docs/reference/other-new-features/indentation.html

*12:New Control Syntax: https://dotty.epfl.ch/docs/reference/other-new-features/control-syntax.html

*13:"クラシック" という表現は Scala 3 Syntax Rewriting を参考にしています

*14:Scala 3 Syntax Rewriting: https://scalacenter.github.io/scala-3-migration-guide/docs/tooling/scala-3-syntax-rewrites.html

*15:Trait Parameters: https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html