レコード
💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。
Flix は、行多相(Row Polymorphism)で拡張可能なレコードをサポートしています。
Flix のレコードはイミュータブル(不変)です(ただし、内部にミュータブルな参照セルを持つことはできます)。
レコードリテラル
レコードリテラルは波括弧を使って記述します。
{ x = 1, y = 2 }
これは次のレコード型を持ちます。
{ x = Int32, y = Int32 }。
レコード内のラベルの順序は問いません。したがって上記のレコードは、次のものと等価です。
{ y = 2, x = 1 }
これは型 { y = Int32, x = Int32 } を持ちます。この型は { x = Int32, y = Int32 } と等価です。言い換えると、レコード型におけるラベルの順序は問わないということです。
ラベルアクセス
ハッシュ記号を使って、レコードのラベルにアクセスできます。
let p = { x = 1, y = 2 };
p#x + p#y
型システムによって、存在しないラベルにはアクセスできないことが保証されます。
レコードはイミュータブルです。いったん構築されると、レコードのラベルの値を変更することはできません。
ラベルの更新
レコードはイミュータブルですが、ラベルの値を更新した新しいレコードを構築することはできます。
let p1 = { x = 1, y = 2 };
let p2 = { x = 3 | p1 };
p1#x + p2#x
式 { x = 3 | p1 } は、レコード p1 の x ラベルを新しい値で更新します。ラベルの更新には、そのラベルがレコード上に存在していることが必要である点に注意してください。レコードは新しいラベルで更新することはできませんが、後ほど見るように、新しいラベルで拡張することはできます。
レコードの拡張
既存のレコードに新しいラベルを追加するには、次のようにします。
let p1 = { x = 1, y = 2 };
let p2 = { +z = 3 | p1 };
p1#x + p1#y + p2#z
ここで式 { +z = 3 | p1 } は、レコード p1 を新しいラベル z で拡張し、その結果は x、y、z の3つのラベルを持ちます。これらはすべて Int32 型です。
レコードの制限
レコードの拡張と同様に、レコードからラベルを取り除くこともできます。
let p1 = { x = 1, y = 2 };
let p2 = { -y | p1 };
ここでレコード p2 は、y ラベルが取り除かれている点を除いて、p1 と同じラベルを持ちます。
行多相
関数は、2つのラベルを持つレコードを要求すると指定できます。
def f(r: {x = Int32, y = Int32}): Int32 = r#x + r#y
この関数はレコード { x = 1, y = 2 } や { y = 2, x = 1 } で呼び出せますが、レコード { x = 1, y = 2, z = 3 } では呼び出せません。f のシグネチャは x と y というちょうど2つのラベルを持つレコードを要求するからです。このとき、レコード r は*閉じている(closed)*といいます。
行多相を使うことで、この制約を緩和できます。
def g(r: {x = Int32, y = Int32 | s}): Int32 = r#x + r#y
この関数は、Int32 型の x と y ラベルを持ってさえいれば、任意のレコードで呼び出せます。このとき、r のレコード型は*開いている(open)*といいます。
名前付きパラメータ
関数が同じ型の複数のパラメータを持つ場合、正しい引数の順序が分からなくなりがちです。例えば、String.contains("Hello","Hello World") は何を返すでしょうか? String.contains("Hello World", "Hello") は何を返すでしょうか?
この問題に対するよくある解決策が、*名前付きパラメータ(named parameters)*を使うことです。Flix はレコードを基盤とした名前付きパラメータの一形態をサポートしています。例えば、ある言語から別の言語へ翻訳する関数 translate を次のように書けます。
def translate(src: {src = Language}, dst: {dst = Language}, text: String): String = ???
この関数は次のように呼び出せます。
translate({src = English}, {dst = French}, "Where is the library?")
このような冗長な書き方は煩雑になるため、次の糖衣構文を使うこともできます。
translate(src = English, dst = French, "Where is the library?")
これは上記と等価です。