Ikhtisar Kasar
Dalam pemrograman fungsional, functor pada dasarnya adalah konstruksi mengangkat fungsi unary biasa (yaitu yang dengan satu argumen) ke fungsi antara variabel tipe baru. Adalah jauh lebih mudah untuk menulis dan memelihara fungsi-fungsi sederhana antara objek-objek biasa dan menggunakan functors untuk mengangkatnya, kemudian untuk menulis fungsi-fungsi secara manual antara objek-objek kontainer yang rumit. Keuntungan selanjutnya adalah menulis fungsi sederhana hanya sekali dan kemudian menggunakannya kembali melalui fungsi yang berbeda.
Contoh functors termasuk array, "mungkin" dan "baik" functors, futures (lihat misalnya https://github.com/Avaq/Fluture ), dan banyak lainnya.
Ilustrasi
Pertimbangkan fungsi membangun nama orang lengkap dari nama depan dan belakang. Kita dapat mendefinisikannya fullName(firstName, lastName)
sebagai fungsi dari dua argumen, yang bagaimanapun tidak akan cocok untuk functors yang hanya berurusan dengan fungsi dari satu argumen. Untuk memperbaiki, kami mengumpulkan semua argumen dalam satu objek name
, yang sekarang menjadi argumen tunggal fungsi:
// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName
Sekarang bagaimana jika kita memiliki banyak orang dalam array? Alih-alih membaca daftar secara manual, kita cukup menggunakan kembali fungsi kita fullName
melalui map
metode yang disediakan untuk array dengan satu baris kode pendek:
fullNameList = nameList => nameList.map(fullName)
dan menggunakannya seperti
nameList = [
{firstName: 'Steve', lastName: 'Jobs'},
{firstName: 'Bill', lastName: 'Gates'}
]
fullNames = fullNameList(nameList)
// => ['Steve Jobs', 'Bill Gates']
Itu akan bekerja, setiap kali setiap entri di kami nameList
adalah objek yang menyediakan keduanya firstName
dan lastName
properti. Tetapi bagaimana jika beberapa objek tidak (atau bahkan tidak objek sama sekali)? Untuk menghindari kesalahan dan membuat kode lebih aman, kita dapat membungkus objek kita ke dalam Maybe
tipe (mis. Https://sanctuary.js.org/#maybe-type ):
// function to test name for validity
isValidName = name =>
(typeof name === 'object')
&& (typeof name.firstName === 'string')
&& (typeof name.lastName === 'string')
// wrap into the Maybe type
maybeName = name =>
isValidName(name) ? Just(name) : Nothing()
di mana Just(name)
sebuah wadah hanya membawa nama yang valid dan Nothing()
merupakan nilai khusus yang digunakan untuk yang lainnya. Sekarang alih-alih menyela (atau lupa) untuk memeriksa validitas argumen kita, kita dapat menggunakan kembali (mengangkat) fullName
fungsi asli kita dengan satu baris kode, berdasarkan lagi pada map
metode, kali ini disediakan untuk jenis Maybe:
// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)
dan menggunakannya seperti
justSteve = maybeName(
{firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})
notSteve = maybeName(
{lastName: 'SomeJobs'}
) // => Nothing()
steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')
notSteveFN = maybeFullName(notSteve)
// => Nothing()
Kategori Teori
Sebuah Functor di Teori Kategori adalah peta antara dua kategori menghormati komposisi morphisms mereka. Dalam Bahasa Komputer , Kategori utama yang diminati adalah yang objeknya adalah tipe (set nilai tertentu), dan yang morfismenya berfungsi f:a->b
dari satu tipe a
ke tipe lainnya b
.
Misalnya, anggap a
sebagai String
tipe, b
tipe Number, dan f
fungsi memetakan string menjadi panjangnya:
// f :: String -> Number
f = str => str.length
Di sini a = String
mewakili himpunan semua string dan b = Number
himpunan semua angka. Dalam pengertian itu, baik a
dan b
mewakili objek dalam Set Kategori (yang terkait erat dengan kategori jenis, dengan perbedaan yang tidak penting di sini). Dalam Kategori Set, morfisme antara dua set adalah semua fungsi dari set pertama menjadi yang kedua. Jadi fungsi panjang kita di f
sini adalah morfisme dari himpunan string ke himpunan angka.
Karena kita hanya mempertimbangkan kategori yang ditetapkan, Fungsi yang relevan dari itu ke dalam dirinya sendiri adalah peta yang mengirimkan objek ke objek dan morfisme ke morfisme, yang memenuhi hukum aljabar tertentu.
Contoh: Array
Array
dapat berarti banyak hal, tetapi hanya satu hal yang merupakan Functor - konstruk tipe, memetakan tipe a
menjadi tipe [a]
semua array tipe a
. Misalnya, Array
functor memetakan tipe String
ke dalam tipe [String]
(himpunan semua array string dengan panjang sewenang-wenang), dan mengatur tipe Number
ke dalam tipe yang sesuai [Number]
(himpunan semua array angka).
Penting untuk tidak membingungkan peta Functor
Array :: a => [a]
dengan morfisme a -> [a]
. Functor hanya memetakan (mengaitkan) tipe a
ke dalam tipe [a]
sebagai satu hal ke hal lainnya. Bahwa setiap jenis sebenarnya adalah serangkaian elemen, tidak ada relevansinya di sini. Sebaliknya, morfisme adalah fungsi aktual antara set tersebut. Misalnya, ada morfisme alami (fungsi)
pure :: a -> [a]
pure = x => [x]
yang mengirimkan nilai ke array 1-elemen dengan nilai itu sebagai entri tunggal. Fungsi itu bukan bagian dari Array
Functor! Dari sudut pandang functor ini, pure
hanya fungsi seperti yang lain, tidak ada yang istimewa.
Di sisi lain, Array
Functor memiliki bagian kedua - bagian morfisme. Yang memetakan morfisme f :: a -> b
menjadi morfisme [f] :: [a] -> [b]
:
// a -> [a]
Array.map(f) = arr => arr.map(f)
Berikut arr
ini adalah array dengan panjang sewenang-wenang dengan nilai tipe a
, dan arr.map(f)
merupakan array dengan panjang yang sama dengan nilai tipe b
, yang entrinya merupakan hasil dari penerapan f
entri arr
. Untuk membuatnya berfungsi, hukum matematika pemetaan identitas ke identitas dan komposisi untuk komposisi harus dipegang, yang mudah diperiksa dalam Array
contoh ini .