Bagikan melalui


Generalisasi Otomatis

F# menggunakan inferensi jenis untuk mengevaluasi jenis fungsi dan ekspresi. Topik ini menjelaskan bagaimana F# secara otomatis menggeneralisasi argumen dan jenis fungsi sehingga berfungsi dengan beberapa jenis jika hal ini memungkinkan.

Generalisasi Otomatis

Pengkompilasi F#, ketika melakukan inferensi jenis pada fungsi, menentukan apakah parameter tertentu dapat generik. Pengkompilasi memeriksa setiap parameter dan menentukan apakah fungsi memiliki dependensi pada jenis parameter tertentu. Jika tidak, jenisnya disimpulkan sebagai generik.

Contoh kode berikut mengilustrasikan fungsi yang disimpulkan pengkompilasi menjadi generik.

let max a b = if a > b then a else b

Jenis disimpulkan menjadi 'a -> 'a -> 'a.

Jenis menunjukkan bahwa ini adalah fungsi yang mengambil dua argumen dari jenis yang tidak diketahui yang sama dan mengembalikan nilai dari jenis yang sama. Salah satu alasan bahwa fungsi sebelumnya dapat umum adalah bahwa operator yang lebih besar dari (>) itu sendiri generik. Operator yang lebih besar dari memiliki tanda tangan 'a -> 'a -> bool. Tidak semua operator bersifat umum, dan jika kode dalam fungsi menggunakan jenis parameter bersama dengan fungsi atau operator non-generik, jenis parameter tersebut tidak dapat digeneralisasi.

Karena max umum, dapat digunakan dengan jenis seperti , , floatdan sebagainyaint, seperti yang ditunjukkan dalam contoh berikut.

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

Namun, dua argumen harus berjenis yang sama. Tanda tangannya adalah 'a -> 'a -> 'a, bukan 'a -> 'b -> 'a. Oleh karena itu, kode berikut menghasilkan kesalahan karena jenisnya tidak cocok.

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

Fungsi ini max juga berfungsi dengan jenis apa pun yang mendukung operator yang lebih besar dari. Oleh karena itu, Anda juga dapat menggunakannya pada string, seperti yang ditunjukkan dalam kode berikut.

let testString = max "cab" "cat"

Pembatasan Nilai

Pengkompilasi melakukan generalisasi otomatis hanya pada definisi fungsi lengkap yang memiliki argumen eksplisit, dan pada nilai yang tidak dapat diubah sederhana.

Ini berarti bahwa kompilator mengeluarkan kesalahan jika Anda mencoba mengkompilasi kode yang tidak cukup dibatasi untuk menjadi jenis tertentu, tetapi juga tidak dapat digeneralisasi. Pesan kesalahan untuk masalah ini mengacu pada pembatasan ini pada generalisasi otomatis untuk nilai sebagai pembatasan nilai.

Biasanya, kesalahan pembatasan nilai terjadi baik ketika Anda ingin konstruksi menjadi generik tetapi pengkompilasi memiliki informasi yang tidak mencukupi untuk menggeneralisasinya, atau ketika Anda secara tidak sengaja menghilangkan informasi jenis yang memadai dalam konstruksi nongenerik. Solusi untuk kesalahan pembatasan nilai adalah memberikan informasi yang lebih eksplisit untuk lebih sepenuhnya membatasi masalah inferensi jenis, dengan salah satu cara berikut:

  • Batasi jenis menjadi nongenerik dengan menambahkan anotasi jenis eksplisit ke nilai atau parameter.

  • Jika masalahnya menggunakan konstruksi yang tidak dapat diregeneralisasi untuk menentukan fungsi generik, seperti komposisi fungsi atau argumen fungsi kurir yang tidak lengkap diterapkan, coba tulis ulang fungsi sebagai definisi fungsi biasa.

  • Jika masalahnya adalah ekspresi yang terlalu kompleks untuk digeneralisasi, buatlah menjadi fungsi dengan menambahkan parameter tambahan yang tidak digunakan.

  • Tambahkan parameter jenis generik eksplisit. Opsi ini jarang digunakan.

Contoh kode berikut mengilustrasikan masing-masing skenario ini.

Kasus 1: Ekspresi yang terlalu kompleks. Dalam contoh ini, daftar counter dimaksudkan untuk menjadi int option ref, tetapi tidak didefinisikan sebagai nilai yang tidak dapat diubah sederhana.

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

Kasus 2: Menggunakan konstruksi yang tidak dapat diubah untuk menentukan fungsi generik. Dalam contoh ini, konstruksi tidak dapat diubah karena melibatkan aplikasi parsial argumen fungsi.

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

Kasus 3: Menambahkan parameter tambahan yang tidak digunakan. Karena ekspresi ini tidak cukup sederhana untuk generalisasi, pengkompilasi mengeluarkan kesalahan pembatasan nilai.

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

Kasus 4: Menambahkan parameter jenis.

let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)

Dalam kasus terakhir, nilai menjadi fungsi jenis, yang dapat digunakan untuk membuat nilai dari berbagai jenis, misalnya sebagai berikut:

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

Lihat juga