Ruby 3.2の新機能Data.defineについて(翻訳)
Victor ShepelevことZverokが、RubyにData.define
という実に便利な機能をもたらしました。以下はマージ済みのプルリクです。
Rubyでの議論は以下です。
- issue: Feature #16122: Data: simple immutable value object - Ruby master - Ruby Issue Tracking System
では、このData.define
はどういう機能で、どんなことに使えるのでしょうか?これより見ていくことにします。
Data.define
とは何か
手短に言うと、Data.define
が作成するのはイミュータブルでStruct
ライクな型であり、位置引数またはキーワード引数で初期化できます。
Point = Data.define(:x, :y)
origin = Point.new(0, 0)
north_of_origin = Point.new(x: 0, y: 10)
原注
これはStruct
を継承しません。
しかも、このAPIではパターンマッチングもサポートされているので、以下のようなことも引き続き存分にやれます。
case origin
in Point[x: 0 => x, y] # 右代入、パターンマッチング
Point.new(x:, y: y + 5) # ハッシュ構文のショートハンド
else
origin
end
また、必要であれば、Struct
と同様にブロックを渡してメソッドを追加することも可能です。
Point = Data.define(:x, :y) do
def +(other) = new(self.x + other.x, self.y + other.y)
end
Structは使わないの?
Struct
も間違いなくこれまで通り使えます。であれば、Data
を使うべき理由とは何でしょうか?
その理由は、Data
の方がより厳密で、生成される値がイミュータブル(不変)だからです。Rubyで関数型のパターンがもっと増えてくれば、これは非常に便利に使えるようになるでしょう。
たとえばRactorでのデータ渡しではイミュータブルなステートが必要となるため、Data
はValue Objectパターンや、いずれ次世代となるであろうPumaなどのWebサーバーにおけるメッセージ渡しで非常に効果的です。
dry-rbは使わないの?
興味深いことに、dry-rbの人たちはこの点に関して「非常に」現実的です。実を言うと、dry-rbの一部のコアメンバーは、既にこの新機能を自分たちのイミュータブルな構造体にラップすることについて議論中です。つまり、この新機能と独立してdry-rbのイミュータブル構造体を構築するのではなく、この新機能の上に構築しようということです。
We will probably reimplement dry-struct using Data. Remember that dry-struct supports typed attributes.
— @solnic@ruby.social (@solnic29a) October 5, 2022
既にdry-rbの人たちはパターンマッチングでもそうしましたし、言語が公式に採用した機能の上に構築されるところを見ることができたら素晴らしいでしょう。
皆さんはどうお考えですか?
私個人ですか?大好きです。私は、REPLで完全なStruct
を書かずにパターンマッチングを手早く使えるデータ型が欲しくなったときに多用するつもりです。私の場合はミューテーションするつもりがないことが多く、keyword_init
フラグを覚えておくのも少々面倒だからです。
この新機能を見ていると、Scala言語のcase class
を強く連想します。
case class Book(isbn: String)
val frankenstein = Book("978-0486282114")
Kotlinのdata class
も思い出されます。
data class User(val name: String = "", val age: Int = 0)
つまり、これは目新しい概念ではなく、Rubyで採用されることが有用な概念です。
最後に
近年のMatzはツール周りを重視していることもあって、言語の新機能については確かにテンポが緩やかになっていますが、それでも言語のコアには興味をそそられる動きがいくつか目に付きます。もちろんツール周りにも魅力的なものがたくさんありますし、それらが実現していく様子を目にするのは心が踊ります。おそらく近いうちに、これについても記事を書くつもりです。
それとともに、本記事に目を通してくれたZverokおよび参加してくださった皆さんにお礼を申し上げたいと思います。
概要
原著者の許諾を得て翻訳・公開いたします。
参考: class
Data
(Ruby 3.2 リファレンスマニュアル)