# 匿名のレコード

## 構文

// Construct an anonymous record
let value-name = [struct] {| Label1: Type1; Label2: Type2; ...|}

// Use an anonymous record as a type parameter
let value-name = Type-Name<[struct] {| Label1: Type1; Label2: Type2; ...|}>

// Define a parameter with an anonymous record as input
let function-name (arg-name: [struct] {| Label1: Type1; Label2: Type2; ...|}) ...


## 基本的な使用方法

たとえば、匿名レコードを生成する関数と対話する方法を次に示します。

open System

let d = radius * 2.0
let a = Math.PI * (radius ** 2.0)
let c = 2.0 * Math.PI * radius

{| Diameter = d; Area = a; Circumference = c |}

let r = 2.0
let stats = getCircleStats r
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
r stats.Diameter stats.Area stats.Circumference


open System

let d = radius * 2.0
let a = Math.PI * (radius ** 2.0)
let c = 2.0 * Math.PI * radius

{| Diameter = d; Area = a; Circumference = c |}

let printCircleStats r (stats: {| Area: float; Circumference: float; Diameter: float |}) =
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats


printCircleStats r {| Diameter = 2.0; Area = 4.0; MyCircumference = 12.566371 |}
// Two anonymous record types have mismatched sets of field names
// '["Area"; "Circumference"; "Diameter"]' and '["Area"; "Diameter"; "MyCircumference"]'


## 構造体の匿名レコード

open System

let d = radius * 2.0
let a = Math.PI * (radius ** 2.0)
let c = 2.0 * Math.PI * radius

// Note that the keyword comes before the '{| |}' brace pair
struct {| Area = a; Circumference = c; Diameter = d |}

// the 'struct' keyword also comes before the '{| |}' brace pair when declaring the parameter type
let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats


### 構造の推論


let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
r stats.Diameter stats.Area stats.Circumference

printCircleStats r {| Area = 4.0; Circumference = 12.6; Diameter = 12.6 |}


## 他の型の中への匿名レコードの埋め込み

ケースがレコードである判別共用体を宣言すると便利です。 ただし、レコード内のデータが判別共用体と同じ型である場合は、すべての型を相互に再帰的に定義する必要があります。 匿名レコードを使用すると、この制限を回避できます。 パターンが一致する型と関数の例を次に示します。

type FullName = { FirstName: string; LastName: string }

// Note that using a named record for Manager and Executive would require mutually recursive definitions.
type Employee =
| Engineer of FullName
| Manager of {| Name: FullName; Reports: Employee list |}
| Executive of {| Name: FullName; Reports: Employee list; Assistant: Employee |}

let getFirstName e =
match e with
| Engineer fullName -> fullName.FirstName
| Manager m -> m.Name.FirstName
| Executive ex -> ex.Name.FirstName


## コピーおよび更新式

let data = {| X = 1; Y = 2 |}
let data' = {| data with Y = 3 |}


ただし、名前付きレコードとは異なり、匿名レコードを使用すると、コピーおよび更新式を使用して、まったく異なる形式を構築できます。 次の例では、前の例と同じ匿名レコードを取得し、それを新しい匿名レコードに展開します。

let data = {| X = 1; Y = 2 |}
let expandedData = {| data with Z = 3 |} // Gives {| X=1; Y=2; Z=3 |}


type R = { X: int }
let data = { X = 1 }
let data' = {| data with Y = 2 |} // Gives {| X=1; Y=2 |}


// Copy data from a reference record into a struct anonymous record
type R1 = { X: int }
let r1 = { X = 1 }

let data1 = struct {| r1 with Y = 1 |}

// Copy data from a struct record into a reference anonymous record
[<Struct>]
type R2 = { X: int }
let r2 = { X = 1 }

let data2 = {| r1 with Y = 1 |}

// Copy the reference anonymous record data into a struct anonymous record
let data3 = struct {| data2 with Z = r2.X |}


## 匿名レコードのプロパティ

### 匿名レコードは標準である

2 つの匿名レコード宣言がある次の例について考えてみます。

let x = {| X = 1 |}
let y = {| Y = 1 |}


xy の値は型が異なり、相互に互換性がありません。 これらに等値性はなく、比較できません。 これを説明するために、同等の名前付きレコードについて考えてみます。

type X = { X: int }
type Y = { Y: int }

let x = { X = 1 }
let y = { Y = 1 }


### 匿名レコードでは構造的等価性と比較が使用される

レコードの型と同様に、匿名レコードは構造的な等値性があり、比較可能です。 これは、レコード型の場合と同様に、構成するすべての型によって等式と比較がサポートされる場合にのみ当てはまります。 等式または比較をサポートするには、2 つの匿名レコードの "シェイプ" が同じである必要があります。

{| a = 1+1 |} = {| a = 2 |} // true
{| a = 1+1 |} > {| a = 1 |} // true

// error FS0001: Two anonymous record types have mismatched sets of field names '["a"]' and '["a"; "b"]'
{| a = 1 + 1 |} = {| a = 2;  b = 1|}


### 匿名レコードはシリアル化可能

open Newtonsoft.Json

let phillip' = {| name="Phillip"; age=28 |}
let philStr = JsonConvert.SerializeObject(phillip')

let phillip = JsonConvert.DeserializeObject<{|name: string; age: int|}>(philStr)
printfn $"Name: {phillip.name} Age: %d{phillip.age}"  匿名レコードは、シリアル化または逆シリアル化された型のドメインを事前に定義せずに、ネットワーク経由で軽量データを送信する場合に便利です。 ### 匿名レコードは C# 匿名型と相互運用可能 C# の匿名型を使用する必要がある .NET API を使用することができます。 C# の匿名型は、匿名レコードを使用すると相互運用が簡単です。 次の例は、匿名レコードを使用して、匿名型を必要とする LINQ オーバーロードを呼び出す方法を示しています。 open System.Linq let names = [ "Ana"; "Felipe"; "Emilia"] let nameGrouping = names.Select(fun n -> {| Name = n; FirstLetter = n[0] |}) for ng in nameGrouping do printfn$"{ng.Name} has first letter {ng.FirstLetter}"


.NET 全体で使用される他の多くの API では、匿名型を渡す必要があります。 匿名レコードは、それらを操作するためのツールです。

## 制限事項

### パターン マッチングに関する制限事項

1. 名前付きレコードの型とは異なり、匿名レコードのすべてのフィールドをパターンで考慮する必要があります。 これは、匿名レコードでは構造的サブタイプがサポートされていないためです。これらは標準型です。
2. (1) のため、パターン マッチ式に追加のパターンを含めることはできません。これは、個別のパターンがそれぞれ異なる匿名レコードの型を示すためです。
3. (2) のため、匿名レコード パターンは、"ドット" 表記を使用する場合よりも詳細です。