レコードはコレクション的に使ったら良いのではと思った


F# のレコードのコピーおよび更新の式の使いどころがいまいちわからなかったのですが,なんとなくわかった気がするのでメモ程度に。

レコードのコピーおよび更新の式は以下のような記法で,レコードのフィールドの値を変更できます。

type Person = { Name : string; Age : int; Gender : string }
let taro = { Name = "Taro"; Age = 26; Gender = "male" }
let taro2 = { taro with Gender = "female" }  // 性転換した

mutable が付いた可変フィールドと異なり,元のオブジェクトと新しいオブジェクトは異なるものです。不変オブジェクトであるということが大きなメリットです。クラスで不変オブジェクトを作成しようとすると,フィールドの更新が結構面倒になるのですが,レコードを使うととっても簡単です。

例えばコマンドライン引数の処理が次のように容易に書けるようになります。

type Args = { InputPath : string option; Help : bool; FreeArgs : string list }
let rec loop args = function
   | [] -> args
   | "--help" :: _ | "-h" :: _ ->
      // help が true のときはヘルプを表示して終わるのでループをこれ以上続けない
      { args with Help = true }
   | "--input" :: path :: rest when File.Exists (path) ->
      loop { args with InputPath = Some (path) } rest
   | "--input" :: _ ->
      failwith "input file not found"
   | x :: rest ->
      loop { args with FreeArgs = x :: args.FreeArgs } rest
let args =
   let commandArgs = Environment.GetCommandLineArgs () |> List.ofArray
   // 最初の要素は実行ファイルのパスなのでいらない
   let args = loop { InputPath = None; Help = false; FreeArgs = [] } commandArgs.Tail
   { args with FreeArgs = List.rev args.FreeArgs }

類似した書き方で,再帰処理の結果をリストに積んでいくようなことを行うことがあると思います。レコードは,これをもう少し辞書的にしたものと見ることもできるような気がします。