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 fullNamemelalui mapmetode 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 nameListadalah objek yang menyediakan keduanya firstNamedan lastNameproperti. 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 Maybetipe (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) fullNamefungsi asli kita dengan satu baris kode, berdasarkan lagi pada mapmetode, 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->bdari satu tipe ake tipe lainnya b.
Misalnya, anggap asebagai Stringtipe, btipe Number, dan ffungsi memetakan string menjadi panjangnya:
// f :: String -> Number
f = str => str.length
Di sini a = Stringmewakili himpunan semua string dan b = Numberhimpunan semua angka. Dalam pengertian itu, baik adan bmewakili 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 fsini 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
Arraydapat berarti banyak hal, tetapi hanya satu hal yang merupakan Functor - konstruk tipe, memetakan tipe amenjadi tipe [a]semua array tipe a. Misalnya, Arrayfunctor memetakan tipe String ke dalam tipe [String](himpunan semua array string dengan panjang sewenang-wenang), dan mengatur tipe Numberke 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 ake 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 ArrayFunctor! Dari sudut pandang functor ini, purehanya fungsi seperti yang lain, tidak ada yang istimewa.
Di sisi lain, ArrayFunctor memiliki bagian kedua - bagian morfisme. Yang memetakan morfisme f :: a -> bmenjadi morfisme [f] :: [a] -> [b]:
// a -> [a]
Array.map(f) = arr => arr.map(f)
Berikut arrini 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 fentri arr. Untuk membuatnya berfungsi, hukum matematika pemetaan identitas ke identitas dan komposisi untuk komposisi harus dipegang, yang mudah diperiksa dalam Arraycontoh ini .