Catatan Anonim

Catatan anonim adalah agregat sederhana dari nilai bernama yang tidak perlu dideklarasikan sebelum digunakan. Anda dapat mendeklarasikannya sebagai struct atau jenis referensi. Jenis ini berjenis referensi secara default.

Sintaks

Contoh berikut menunjukkan sintaks catatan anonim. Item yang dibatasi sebagai [item] bersifat opsional.

// 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; ...|}) ...

Penggunaan dasar

Catatan anonim paling baik dianggap sebagai jenis catatan F# yang tidak perlu dideklarasikan sebelum instansiasi.

Misalnya, di sinilah bagaimana Anda dapat berinteraksi dengan fungsi yang menghasilkan catatan anonim:

open System

let getCircleStats radius =
    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

Contoh berikut melakukan perluasan pada yang sebelumnya dengan fungsi printCircleStats yang mengambil catatan anonim sebagai input:

open System

let getCircleStats radius =
    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

Memanggil printCircleStats dengan jenis catatan anonim yang tidak memiliki "bentuk" yang sama dengan jenis input akan menyebabkan kegagalan kompilasi:

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"]'

Catatan anonim struct

Catatan anonim juga dapat didefinisikan sebagai struct dengan kata kunci opsional struct. Contoh berikut memberi peningkatan kepada yang sebelumnya dengan membuat dan memakai catatan anonim struct:

open System

let getCircleStats radius =
    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

Inferensi tanpa struct

Catatan anonim struct juga memungkinkan "inferensi tanpa struct" saat Anda tidak perlu menentukan kata kunci struct di situs panggilan. Dalam contoh ini, Anda mengesankan kata kunci struct saat memanggil printCircleStats:


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 |}

Pola terbalik - menentukan struct saat jenis input bukan merupakan catatan anonim struct - akan menyebabkan kegagalan kompilasi.

Menyematkan catatan anonim dalam jenis lain

Sangat berguna untuk menyatakan gabungan yang didiskriminasi yang kasusnya adalah catatan. Tetapi jika data dalam catatan berjenis sama dengan gabungan yang didiskriminasi, Anda harus menentukan semua jenis sebagai rekursif timbal balik. Menggunakan catatan anonim menghindari pembatasan ini. Berikut ini adalah contoh jenis dan fungsi yang cocok dengan pola di atasnya:

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

Menyalin dan memperbarui ekspresi

Catatan anonim mendukung konstruksi dengan ekspresi salin dan perbarui. Misalnya, berikut ini adalah cara membuat instans baru catatan anonim yang menyalin data yang sudah ada:

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

Namun, tidak seperti catatan bernama, catatan anonim memungkinkan Anda membuat bentuk yang sangat berbeda dengan ekspresi salin dan perbarui. Contoh berikut mengambil catatan anonim yang sama dari contoh sebelumnya dan memperluasnya ke dalam catatan anonim baru:

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

Dimungkinkan juga untuk membuat catatan anonim dari instans catatan bernama:

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

Anda juga bisa menyalin data ke dan dari referensi serta membuat catatan anonim struct:

// 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 |}

Properti catatan anonim

Catatan anonim memiliki sejumlah karakteristik yang penting agar sepenuhnya memahami bagaimana mereka dapat digunakan.

Catatan anonim bersifat nominal

Catatan anonim berjenis nominal. Mereka paling baik dianggap sebagai jenis rekaman bernama (yang juga nominal) yang tidak memerlukan deklarasi di muka.

Pertimbangkan contoh berikut dengan dua deklarasi catatan anonim:

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

Nilai x dan y memiliki jenis yang berbeda dan tidak kompatibel dengan satu sama lain. Keduanya tidak setara dan tidak sebanding. Untuk mengilustrasikan ini, pertimbangkan catatan bernama yang setara:

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

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

Tidak ada yang secara inheren berbeda tentang catatan anonim jika dibandingkan dengan rekaman bernama yang setara ketika berhubungan dengan kesetaraan atau perbandingan jenis.

Catatan anonim menggunakan persamaan dan perbandingan struktural

Seperti jenis catatan, catatan anonim secara struktural sama dan sebanding. Ini hanya berlaku jika semua jenis konstituen mendukung kesetaraan dan perbandingan, seperti halnya jenis catatan. Untuk mendukung kesetaraan atau perbandingan, dua catatan anonim harus memiliki "bentuk" yang sama.

{| 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|}

Catatan anonim dapat diserialisasikan

Anda dapat membuat serial catatan anonim seperti yang Anda bisa lakukan dengan catatan bernama. Berikut adalah contoh penggunaan Newtonsoft.Json:

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}"

Catatan anonim berguna untuk mengirim data ringan melalui jaringan tanpa perlu menentukan domain untuk jenis serial/deserialisasi Anda di muka.

Catatan anonim beroperasi dengan jenis anonim C#

Dimungkinkan untuk menggunakan .NET API yang memerlukan penggunaan jenis anonim C#. Jenis anonim C# sepele untuk dioperasikan dengan menggunakan catatan anonim. Contoh berikut menunjukkan cara menggunakan catatan anonim untuk memanggil kelebihan beban LINQ yang memerlukan jenis anonim:

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}"

Ada banyak API lain yang digunakan di seluruh .NET yang memerlukan penggunaan transmisi dalam jenis anonim. Catatan anonim adalah alat Anda untuk bekerja dengannya.

Batasan

Catatan anonim memiliki beberapa batasan dalam penggunaannya. Beberapa melekat pada desainnya, tetapi yang lain dapat berubah.

Batasan dengan pencocokan pola

Catatan anonim tidak mendukung pencocokan pola, tidak seperti catatan bernama. Ada tiga alasan:

  1. Pola harus mempertanyakan setiap bidang catatan anonim, tidak seperti jenis catatan bernama. Ini karena catatan anonim tidak mendukung subjenis struktural - catatan anonim merupakan jenis nominal.
  2. Karena (1), tidak ada kemampuan untuk memiliki pola tambahan dalam ekspresi pencocokan pola, karena setiap pola yang berbeda akan menyiratkan jenis catatan anonim yang berbeda.
  3. Karena (2), pola catatan anonim apa pun akan lebih verbose daripada penggunaan notasi "titik".

Ada saran bahasa terbuka untuk memungkinkan pencocokan pola dalam konteks terbatas.

Batasan dengan mutabilitas

Saat ini tidak dimungkinkan untuk menentukan catatan anonim dengan data mutable. Ada saran bahasa terbuka untuk memungkinkan data yang dapat diubah.

Batasan dengan catatan anonim struct

Tidak dimungkinkan untuk mendeklarasikan catatan anonim struct sebagai IsByRefLike atau IsReadOnly. Ada saran bahasa terbuka untuk catatan anonim IsByRefLike dan IsReadOnly.