Mengapa Efek Aljabar Dibutuhkan
(antelang.org)- Efek aljabar adalah fitur bahasa yang menangkap dan menangani alur kontrol seperti pengecualian yang dapat dilanjutkan kembali, merupakan fitur inti Ante, dan juga digunakan secara sentral di bahasa riset seperti Koka, Effekt, Eff, dan Flix
- Dengan mekanisme yang sama, generator, pengecualian, async, coroutine, diferensiasi otomatis dapat dibuat di tingkat pustaka, dan berkat polimorfisme efek, fungsi seperti
mapcukup ditulis sekali tanpa bergantung pada jenis efeknya - Jika injeksi dependensi dan penyampaian konteks seperti akses database, output, logging, dan penerusan status diubah menjadi efek, mock untuk pengujian, pengumpulan output, dan pemfilteran log dapat ditangani dengan mengganti handler
- Jika efek seperti
can IO,can Print,can Failterlihat di signature fungsi, hal itu menguntungkan untuk jaminan kemurnian, pencatatan/pemutaran ulang, dan audit keamanan, tetapi efek yang sudah diizinkan dapat tanpa sengaja merambat ke handler yang sudah ada - Kelemahan tradisionalnya adalah kekhawatiran soal efisiensi, tetapi bahasa-bahasa terbaru mengurangi biayanya lewat optimisasi efek tail-resumptive, evidence passing, pembatasan
resumetunggal, dan spesialisasi handler
Model dasar efek aljabar
- Efek aljabar juga disebut effect handlers, dan dapat dipahami dengan model “pengecualian yang dapat dilanjutkan kembali”
- Dalam pseudocode Ante, fungsi efek dideklarasikan, dan signature fungsi menandai dengan
canbahwa efek tersebut dapat digunakan- Memanggil fungsi efek seperti
say_message: Unit -> Unitmenjadi bentuk “melempar” efek - Fungsi pemanggil menampilkan kemungkinan penggunaan efek itu di signature, seperti
foo () can SayMessage
- Memanggil fungsi efek seperti
- Ekspresi
handlemenangkap efek miriptry/catch, lalu melanjutkan komputasi yang terhenti dengan memanggilresume- Jika handler
say_messagemenjalankanprint "Hello World!"lalu memanggilresume (), komputasi aslinya berlanjut dan mengembalikan42
- Jika handler
- Nama “algebraic” sebagian besar adalah istilah yang tersisa; dalam praktiknya, effect handlers lebih akurat sebagai istilah, tetapi nama efek aljabar tetap dipakai karena lebih akrab bagi pengguna
Alur kontrol yang ditentukan pengguna
- Efek aljabar memungkinkan berbagai fitur bahasa diimplementasikan dengan satu mekanisme
- generator
- pengecualian
- async
- coroutine
- diferensiasi otomatis
- Polimorfisme efek mengurangi masalah what color is your function
map (input: Vec a) (f: a -> b can e): Vec b can emenyatakan bahwa apa pun efekeyang dilakukan fungsi masukanf,mapjuga melakukan efek yang samamapyang sama dapat digunakan bersama output stdout, pemanggilan fungsi asinkron, yield stream, dan lain-lain- Banyak bahasa effect handler menghilangkan variabel efek
esehingga dapat ditulis dalam bentuk yang familier sepertimap (input: Vec a) (f: a -> b): Vec b
- Pengecualian dapat diimplementasikan saat menangani efek dengan tidak memanggil
resume- Definisikan
throw: a -> never_returnsuntuk efekThrow a - Jika terjadi pembagian dengan 0, panggil
throw "error: Division by zero!", dan handler mencetak pesan lalu tidak melanjutkan komputasi
- Definisikan
- Generator dapat diimplementasikan dengan
yield: a -> Unitdari efekYield a- Saat menelusuri elemen vektor, panggil
yield elem - Handler
filtermemanggil lagiyield xjika nilai yang di-yield memenuhi kondisi, lalu melanjutkan ke elemen berikutnya denganresume () - Handler
my_for_eachmenjalankan fungsifuntuk setiap nilai yang di-yield lalu terus berjalan denganresume ()
- Saat menelusuri elemen vektor, panggil
- Scheduler kooperatif juga dapat dibuat dengan efek
yield: Unit -> Unit, di mana handler mengambil alih kontrol lalu beralih ke eksekusi fungsi lain- Contoh scheduler di Effekt menunjukkan pola ini
- Banyak efek dapat saling terkomposisi dengan baik, dan ini dianggap sebagai keunggulan kegunaan dibanding abstraksi efek lainnya
Injeksi dependensi dan kemudahan pengujian
- Efek juga dapat digunakan untuk injeksi dependensi dalam aplikasi bisnis umum
- Alih-alih langsung mengoper objek database sebagai argumen fungsi, efek
Databasedapat didefinisikan- Bentuk lama menerima objek DB sebagai argumen seperti
business_logic (db: Database) (x: I32) - Bentuk berbasis efek menjadi
business_logic (x: I32) can Database, dan di dalamnya memanggilquery "..."
- Bentuk lama menerima objek DB sebagai argumen seperti
- Pemilihan database konkret ditangani oleh handler di bagian atas stack pemanggilan
- DB produksi bisa diganti ke DB lain atau ke mock DB untuk pengujian
- Handler
mock_databasedapat mengabaikan pesanquerydan selaluresumedengan mengembalikanDbResponse.Ok
- Output juga bisa diperlakukan sebagai efek agar selama pengujian tidak menulis langsung ke stdout, melainkan dikumpulkan sebagai string
- Handler
print_to_stringmenangkap pemanggilanprint msgdan mengakumulasikannya ke stringall_messagesbersama karakter baris baru output_messagesdapat memverifikasi nilai balik1234dan string pesan tanpa output nyata
- Handler
- Logging dapat diubah menjadi output bersyarat dengan efek
LogdanLogLevellog_handlermemanggilprint msgjika level pesan setidaknya sama dengan ambang yang ditetapkanfoo () with log_handler Errorhanya mencetak log error
API yang lebih rapi dan penerusan konteks
- Efek aljabar dapat mengekspresikan pola objek Context yang diteruskan ke seluruh program atau pustaka sebagai efek
- Efek
Use adapat dipandang sebagai efek state, denganget: Unit -> adanset: a -> Unit- Handler
statemenyimpan state awal, mengembalikan konteks saatget, dan memperbaruinya ke konteks baru saatset - Definisi
statepada contoh mengabaikan aturan ownership, dan implementasi nyata mungkin memerlukan batasanCopy a
- Handler
- Contoh yang menyimpan string di dalam vektor dan meneruskan indeks sebagai semacam kunci menunjukkan biaya penerusan konteks
- Tanpa efek,
push_string,get_string,append_with_separator,example, dan lainnya harus terus menerimastringssebagai argumen - Dalam implementasi berbasis efek, operasi primitif
push_stringdanget_stringmemanggilget/set, dan kode tingkat atas tidak perlu lagi meneruskanstringssecara langsung
- Tanpa efek,
- Pendekatan ini cocok ketika pustaka membungkus penerusan konteks internal
- Pengguna pustaka tidak perlu memikirkan detail internal cara konteks diteruskan
- Jika tidak ingin terikat ke tipe konteks tertentu, fungsi-fungsi yang dibutuhkan dapat diabstraksikan sebagai interface
Pengganti variabel global dan gaya langsung
- API yang tampak stateless seperti pembuatan bilangan acak atau alokasi memori, tetapi sebenarnya memerlukan state, dapat diekspresikan sebagai efek alih-alih variabel global
- Contoh pembuatan bilangan acak menunjukkan beban harus meneruskan objek
Prngsecara langsung ke seluruh programPrngglobal memang nyaman, tetapi memunculkan kelemahan nilai global seperti kebutuhan thread safety- Dengan
random: Unit -> U8dari efekRandom, pengguna hanya perlu menyatakan inisialisasi melalui handler di suatu titik pada stack pemanggilan atas - Setelah itu, untuk mengganti ke
/dev/urandomatau sumber acak lain, cukup ganti handler tanpa mengubah sisa kode pada stack pemanggilan
- Alokasi memori juga dapat diekspresikan sebagai efek
Allocateallocate: (size: Usz) -> Alignment -> Ptr afree: Ptr a -> Unit- Sebagian besar pemanggilan dapat memakai allocator global, lalu di tight loop, handler dapat ditambahkan ke body loop untuk menggantinya dengan arena allocator
- Efek memungkinkan gaya langsung dibanding cara yang meneruskan hasil dalam nilai yang dibungkus khusus
- Saat memakai
Maybe t, jalur sukses harus dirangkai denganand_then,map - Gula sintaks seperti
?di Rust adalah perangkat untuk berfokus pada jalur yang baik get_line_from_stdin (): String can Fail, IOdanparse (s: String): U32 can Failberbasis efek dapat ditulis seperti kode sekuensial biasa:line = ...,x = ...,x * 2
- Saat memakai
- Penanganan kegagalan dapat dilakukan dengan menerapkan handler agar keluar dari jalur baik
get_line_from_stdin () with default "42"menangani efekFaildengan nilai default
- Tipe error yang berbeda juga terkomposisi secara alami sebagai daftar efek
LibraryA.foo (): U32 can Throw LibraryA.ErrorLibraryB.bar (): U32 can Throw LibraryB.Errormy_functiondapat mendeklarasikanThrow LibraryA.Error,Throw LibraryB.Error, danThrow MyErrorbersama-sama- Jika pengulangannya menjadi panjang, type alias seperti
AllErrors = can Throw ...dapat dibuat - Efek
Throw Stringyang sama akan digabung jadi satu, dan jika ingin dipisahkan maka diperlukan tipe pembungkus sepertiMyError
Kemurnian, dapat dijalankan ulang, dan audit keamanan
- Sebagian besar bahasa effect handler, kecuali yang setara dengan OCaml, menggunakan efek di tempat yang dapat menimbulkan side effect
- Di Ante, side effect tidak dapat digunakan kecuali ditandai seperti
can Print,can IO - Definisi
externtidak dapat diperiksa kompilator sehingga definisi tipenya harus dipercaya - Menjalankan efek
IOhanya pada mode debug untuk mempertahankan keamanan efek pada mode rilis adalah fitur yang direncanakan
- Di Ante, side effect tidak dapat digunakan kecuali ditandai seperti
- Beberapa fungsi memerlukan fungsi murni sebagai input
- Saat membuat thread, thread yang dibuat tidak boleh dapat memanggil lewat handler yang dimiliki thread saat ini
spawn_all (functions: Vec (Unit -> a pure)): Vec a can IOmenerima hanya fungsi murni, menjalankan semuanya sebagai thread, lalu menunggu hingga selesai
- Software Transactional Memory (STM) adalah teknik konkurensi yang memerlukan fungsi murni
- Banyak fungsi dijalankan secara bersamaan, lalu jika nilai berubah oleh thread lain selama transaksi, transaksi itu dimulai ulang
- Implementasi proof-of-concept di Effekt tersedia di effekt-stm
- Kemurnian juga dapat memberi kemungkinan record/replay mirip utilitas debugging
rr- Dua handler,
recorddanreplay, menangani efek tingkat atas yang dikeluarkanmain, biasanyaIO recordmencatat terjadinya efek dan hasilnya, lalu meneruskannya lagi ke handlerIObawaan untuk pemrosesan nyatareplaytidak melakukanIOnyata dan memakai hasil dari log efek- Jika pencatatan dijadikan default pada build debug, debugging deterministik dapat diperoleh
- Dua handler,
- Daftar efek pada signature fungsi membantu audit keamanan, mirip Capability Based Security
get_pi: Unit -> F64menunjukkan bahwa fungsi itu tidak diam-diam melakukanIOdi belakang layar- Jika setelah pembaruan pustaka
get_pi: Unit -> F64 can IO, maka akan muncul error pada kode pemanggil kecuali fungsi pemanggil itu memang sudah memerlukanIO - Sebaiknya mendeklarasikan efek seminimal mungkin, misalnya hanya
Printalih-alih seluruhIO - Menambahkan efek baru diperlakukan sebagai perubahan yang melanggar semantic versioning
- Materi terkait mencakup Capability Based Security dan Designing with Static Capabilities and Effects
Batasan dan strategi implementasi
- Salah satu batasan pendekatan efek adalah kemungkinan penanganan yang tidak disengaja
- Jika sebuah fungsi baru mulai memerlukan
IO, mungkin tidak muncul error jika fungsi pemanggil sudah mengizinkanIO - Efek
Failjuga demikian; fungsi pustaka yang sebelumnya tidak pernah gagal bisa saja nanti dapatFail, lalu merambat ke handlerFailyang sudah ada - Perilaku ini kadang dapat diterima, tetapi jika yang diinginkan adalah penanganan terpisah seperti pemberian nilai default, hasilnya bisa berbeda dari niat semula
- Jika sebuah fungsi baru mulai memerlukan
- Kekurangan utama tradisional adalah kekhawatiran soal efisiensi, tetapi keluaran kompilasi efek belakangan ini jauh membaik
- Banyak bahasa efek aljabar mengoptimalkan efek tail-resumptive menjadi pemanggilan closure biasa
- Efek tail-resumptive adalah efek di mana handler memanggil
resumedi bagian akhir - Sebagian besar efek nyata termasuk kategori ini, dan sebagian besar contoh di artikel ini juga demikian
- Pengecualian diklasifikasikan sebagai kasus istimewa karena sama sekali tidak memanggil
resume
- Efek tail-resumptive adalah efek di mana handler memanggil
- Strategi optimisasi juga berbeda menurut bahasa
- Koka memakai evidence passing dan mengangkat efek hingga ke handler untuk mengompilasi ke C tanpa runtime
- Ante dan OCaml membatasi
resumeagar dipanggil paling banyak satu kali- Batasan ini mengecualikan beberapa efek seperti nondeterminisme
- Sebagai gantinya, penanganan resource menjadi lebih sederhana dan continuation internal bisa diimplementasikan lebih efisien dengan cara seperti segmented stacks
- Effekt sepenuhnya menspesialisasi handler lalu menghapusnya dari program
- Pendekatan ini punya batasan yang membuat sebagian besar fungsi menjadi second-class
- Fungsi first-class dapat diperoleh dalam bentuk boxed dan dialihkan dengan model pay-as-you-go
- Materi terkait mencakup dokumentasi captures Effekt dan makalah
Belum ada komentar.