ネストしたレコードの更新が楽になるかも?

F# Advent Calendar 2020の9日目のエントリーです。

今日は(も?)、入ると嬉しいRFCの話です。

RFC FS-1049

RFC FS-1049は、 ネストしたレコードのフィールドを with で書き換えられるように拡張しよう、というものです*1

F#のレコードは with を使うことで、一部のフィールドのみを更新した新しいレコードが作れます。

type Customer =
  { Name: string
    Address: string
    Age: int }

// NameとAddressはそのままにAgeをインクリメントする関数
let incrAge customer =
  { customer with Age = customer.Age + 1 }

しかし、ネストしたレコードを更新するためには with もネストして使う必要があります。

例えば、

type A = { X: B }
and B = { Y: C }
and C = { Z: string }

let a = { X = { Y = { Z = "str" } } } 

こういうレコードがあった場合に Z を更新するには、

let a' = { a with X = { a.X with Y = { a.X.Y with Z = "updated" } } }

のようにしなければなりません。

RFC FS-1049は、こう書けるようにしようという提案です。

let a' = { a with X.Y.Z = "updated" }

とてもシンプルになりましたね。

ただこれ、フィールドが option とかだとおそらく結局ネストが必要です。

type A = { X: B }
and B = { Y: C option }
and C = { Z: string }

let a = { X = { Y = Some { Z = "str" } } } 
let a' = { a with X.Y = a.X.Y.map(fun y -> { y with Z = "updated" }) }

まぁ、以前より便利になることは確実なので、入ってほしい拡張ではありますね。

*1:もともとの提案は、「ファーストクラスのLens入れようぜ!」というものだったので大分おとなしいところに着地した感はあります。