Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

レコード

💡 お知らせ: このドキュメントは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 } は、レコード p1x ラベルを新しい値で更新します。ラベルの更新には、そのラベルがレコード上に存在していることが必要である点に注意してください。レコードは新しいラベルで更新することはできませんが、後ほど見るように、新しいラベルで拡張することはできます。

レコードの拡張

既存のレコードに新しいラベルを追加するには、次のようにします。

let p1 = { x = 1, y = 2 };
let p2 = { +z = 3 | p1 };
p1#x + p1#y + p2#z

ここで式 { +z = 3 | p1 } は、レコード p1 を新しいラベル z で拡張し、その結果は xyz の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 のシグネチャは xy というちょうど2つのラベルを持つレコードを要求するからです。このとき、レコード r は*閉じている(closed)*といいます。

行多相を使うことで、この制約を緩和できます。

def g(r: {x = Int32, y = Int32 | s}): Int32 = r#x + r#y

この関数は、Int32 型の xy ラベルを持ってさえいれば、任意のレコードで呼び出せます。このとき、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?")

これは上記と等価です。