Cepat Fungsional

Apa yang menyatukan "currying", "monads", "tipe data aljabar"? Bukan hanya fakta bahwa beberapa pengembang berusaha mengelak dari kata-kata ini, tetapi juga pemrograman fungsional. Di bawah bimbingan Yevgeny Elchev yang cermat, kami terjun ke paradigma fungsional dan memahami hampir semua hal. Jangan takut sebelumnya, jangan ragu untuk membaca transkrip podcast AppsCast edisi kesepuluh.



Daniil Popov: Halo semuanya. Hari ini, tamu kami adalah Evgeny Elchev dari Krasnoyarsk yang cerah. Eugene, katakan padaku apa yang kamu lakukan dan bagaimana kamu sampai pada pemrograman fungsional?

Evgeny Elchev: Halo semuanya. Saya seorang pengembang iOS di Redmadrobot, seperti orang lain, saya melukis tombol, kadang-kadang saya menulis logika bisnis.

Saya pertama kali berkenalan dengan pemrograman fungsional melalui artikel. Saya, tidak mengerti maksudnya, berpikir itu semacam pemrograman prosedural, tanpa kelas. Ketika saya membaca salah satu artikel lebih dekat, saya menyadari bahwa saya salah dan mulai menggali. Ini bukan untuk mengatakan bahwa saya baru saja datang ke pemrograman fungsional, karena pengikut sejati akan meletakkan tulang mereka untuk itu dan menulis di Haskell, menggunakan monad sedapat mungkin. Saya hanya terjun dan menggunakannya hanya dalam produksi.

Daniil Popov: Jadi, monad sudah pergi.

Evgeny Elchev: Sudah sulit?

Daniil Popov: Saya mencoba untuk pergi dengan cara yang sama, tetapi saya membuka artikel, melihat kata-kata "currying", "monad" dan segera menutupnya, berpikir bahwa saya belum layak. Apakah saya punya kesempatan?

Evgeny Elchev: Tentu saja. Anda mungkin tidak tahu ini sama sekali.

Dengan kata sederhana tentang fungsionalisme


Daniil Popov: Mari kita berikan definisi sederhana bagi mereka yang belum pernah mendengar tentang paradigma fungsional.

Evgeny Elchev: Semua orang memahami paradigma dengan cara mereka sendiri. Jika kita mengambil penjelasan dari Wikipedia, ini adalah penggunaan fungsi matematika, di mana seluruh program ditafsirkan sebagai fungsi matematika.

Pendekatan fungsional (FP) adalah ketika Anda menggunakan fungsi dalam pekerjaan Anda yang hanya memiliki argumen input dan nilai output. Jika seluruh program terdiri dari fungsi-fungsi seperti itu, maka ini adalah program fungsional.

Daniil Popov: OOP adalah kelanjutan logis dari pemrograman prosedural yang biasa dan memecahkan masalah enkapsulasi data di kelas. Masalah apa yang harus dipecahkan pemrograman fungsional?

Evgeny Elchev: Matematikawan menemukan pemrograman fungsional. Para lelaki berkumpul dan memutuskan untuk membuat paradigma di mana semuanya bisa dibuktikan. Ada kode, belum diluncurkan, tapi kami akan buktikan semuanya. Poin mana pun dari program ini dapat dihitung, dengan memahami di mana kita akan datang ketika kita mengizinkan beberapa tindakan.

Kedengarannya abstrak, jadi mari kita lihat contoh fungsi murni. Kami menulis fungsi penjumlahan yang membutuhkan dua argumen, meneruskan 2 dan 3 untuk itu, mendapatkan 5, dan kami dapat membuktikannya. Itu selalu benar. Jika seluruh program kami terdiri dari fungsi-fungsi seperti itu, maka itu semua bisa dibuktikan.

Saat membuat bahasa, fungsi dasar mulai dilewatkan, dan fitur tambahan muncul: lambdas, fungsi tingkat tinggi, monad, monoids.

Paradigma fungsional tidak menyelesaikan satu masalah, itu adalah keinginan yang sama untuk menulis kode yang baik sesederhana mungkin sehingga program stabil dan mudah dipelihara.

Jika Anda melihat lebih dekat, banyak hal yang kami gunakan dalam OOP tercermin dalam pendekatan fungsional. Ada kelas dalam OPP yang merangkum satu set bidang. Dalam FP, ini juga bisa dilakukan menggunakan kelas tipe. Seperti Vitaly Bragilevsky suka mengatakan : "Jika Anda melihat tablet di mana data berjalan di sepanjang garis dan kolom fungsi, maka FI berjalan di sepanjang kolom, OOP berjalan di sepanjang garis." Itu saja.

Daniil Popov: Bagaimana kaitan FI dengan paradigma lain? Bisakah saya menulis secara fungsional di OOP? Bagaimana mencampuradukkan paradigma, dan apakah itu masuk akal?

Evgeny Elchev: Paradigma ini terbatas pada fakta bahwa Anda menulis fungsi dengan data. Salah satu fitur AF adalah tidak adanya status variabel. Jika data Anda adalah kelas, maka tidak ada masalah. Jika kelas sepenuhnya tidak dapat diubah, maka itu dapat digunakan. Kelas hanyalah tipe, seperti string atau angka, hanya lebih kompleks, terdiri dari beberapa nilai.

Daniil Popov: Anda mengatakan sebelumnya bahwa Anda dapat membuktikan kebenaran matematis suatu program jika Anda menulisnya secara fungsional secara eksklusif. Kemudian lelucon "kompilasi - karya" untuk bahasa fungsional tidak lagi menjadi lelucon, bukan?

Evgeny Elchev: Jika Anda melihat kesalahan I / O, maka ya. Sebelumnya, pemrogram berjuang dengan masalah: terhubung ke jaringan, tidak ada jaringan, kembali nill, dan semuanya jatuh. Untuk solusinya, cara termudah adalah memeriksa apa yang datang - tidak ada / tidak ada, tetapi karena ada risiko bahwa tidak semuanya diperhitungkan, program dapat dikompilasi dan macet.

Dalam bahasa modern, ini diputuskan. Di Haskell, Anda dapat menulis sebuah program yang akan bekerja dan tidak macet, tetapi tidak ada yang akan mengatakan seberapa benar kerjanya. Tentu saja, ada tipe yang ketat, dan Anda tidak dapat membuat kesalahan dengan menambahkan nomor ke string, tetapi Anda selalu dapat meninggalkan bug dalam aplikasi, dan itu akan berhasil.

Tempat Pendekatan Fungsional di Swift


Alexei Kudryavtsev: Berapa banyak yang bisa Swift sebut sebagai bahasa fungsional?

Evgeny Elchev: Itu mungkin. Fungsionalitas diposisikan sebagai stateless, tetapi Anda dapat menulis di Swift menghindari status tersebut. Pada saat yang sama, Swift tidak sama dengan menulis di bawah iOS, di mana ada status di mana-mana. Tentu saja, di Swift tidak ada instruksi khusus seperti di Haskell, di mana semua fungsi bersih secara default dan kompiler tidak akan memungkinkan Anda untuk mengakses negara dan mengubahnya. Jika Anda menandai fungsi sebagai "kotor", maka perubahan menjadi tersedia.

Alexei Kudryavtsev: Ada pengubah murni di Swift kedua atau ketiga, tetapi hanya bertindak pada tingkat kompilasi sehingga nilai global tidak akan berubah. Anda menulis sesuatu di dalamnya, tetapi kompiler memotong semuanya.

Evgeny Elchev: Ya, di iOS kompiler tidak akan mengikuti ini. Semuanya sepenuhnya berada di hati nurani kita: saat Anda menulis, itu akan terjadi.

Alexei Kudryavtsev: Anda mengatakan bahwa ada banyak status dalam aplikasi iOS, tetapi di mana dan apa yang harus dilakukan dengan mereka jika Anda menulis dengan gaya fungsional?

Evgeny Elchev: Keadaan paling penting adalah UI, misalnya, bidang masukan. Praktis tidak ada yang bisa dilakukan dengan mereka. Anda dapat mencoba abstrak dari mereka, mengumpulkan di satu tempat dan menulis kode sebanyak mungkin tanpa memperhitungkannya. Misalnya, Anda menulis satu fungsi kotor yang mendapatkan semua data dari UI.

Dalam artikel saya, saya memberikan contoh formulir otorisasi, di mana penting bahwa pengguna memasukkan nama pengguna / kata sandi. Kami menulis satu fungsi kotor yang mengembalikan struktur dengan data otorisasi, dan kemudian kami menulis kode bersih di atasnya. Kami mendapat data ini, divalidasi, jika hasilnya valid, kirim permintaan ke server. Permintaan server juga merupakan fungsi kotor, dan memprosesnya sepenuhnya mungkin bersih. "Received, parsed" adalah fungsi linier: input ke data, output adalah struktur kita. Kemudian mereka diubah, difilter dan dapat ditampilkan di layar lagi.

Alexei Kudryavtsev : Di Haskell, kompiler sangat membantu. Jika keadaan berasal dari suatu tempat, seluruh rantai panggilan akan dianggap kotor dan Anda harus membungkus semuanya dalam monad. Jika fungsinya murni, maka caching hasil berhasil - output yang sama selalu merupakan output yang sama. Di Swift, Anda harus mengimplementasikan peta sendiri dan mencoba mengembalikan hasilnya jika sudah di-cache.

Daniil Popov: Sebagian besar bahasa modern dianggap multi-paradigmatik dan banyak memiliki fitur fungsional. Misalnya, di Jawa ada penjelasan khusus untuk antarmuka - @FunctionalInterface , yang mewajibkan pengembang untuk mendefinisikan hanya satu metode di antarmuka, sehingga antarmuka ini dalam bentuk lambdas digunakan dalam seluruh kode. Ketika Anda menambahkan metode kedua atau menghapus yang sudah ada, kompiler akan mulai bersumpah bahwa ia tidak lagi menjadi antarmuka fungsional. Apakah Swift, terlepas dari platform iOS, memiliki fitur fungsional seperti itu?

Evgeny Elchev: Sulit bagi saya untuk memahami apa yang dilakukan penjelasan seperti itu di Jawa. Jika Anda bermaksud mengimplementasikan antarmuka ini ke kelas, dan kemudian Anda hanya menerapkan satu metode, maka tidak ada batasan seperti itu di Swift. Anda bisa membuat typealias, beri nama dan menggunakannya sebagai tipe fungsi sebagai tipe argumen, tipe variabel untuk menetapkan penutupan. Anda dapat mendefinisikan batasan - argumen penutupan input dan output. Fungsi tingkat tinggi sendiri yang dapat ditutup adalah polimorfisme, dan di Swift Anda dapat membangun polimorfisme pada tipe, tidak terbatas pada objek.

Tapi saya tidak tahu hal-hal fungsional spesifik. Dulu ada kari di Swift pertama, tapi itu terputus. Sekarang kita dapat menulis fungsi untuk menjelajah diri kita sendiri, atau menulis fungsi sehingga mengembalikan satu sama lain, tetapi ini tidak sepenuhnya benar.

Kami tidak memiliki functors atau monad kotak. Mereka bahkan tidak bisa ditulis. Fitur-fitur baru di Swift 5.1 akan membantu untuk melakukan ini, tetapi saya mencoba menulis kode seperti itu, dan xCode jatuh.

Pada prinsipnya, di Swift, jika Anda mau, mudah untuk melakukan semuanya sendiri. Sudah ada monad opsional di luar kotak (di Haskell - mungkin). Dia memiliki peta dan peta datar untuk membangun komputasi linier.

Swift memiliki pencocokan pola yang kuat. Switch, yang ada di hampir setiap bahasa dan dalam banyak kasus mengaitkan integer dengan unit, dapat memetakan variabel ke pola tertentu, rentang, jenis, ekstrak nilai dari tipe terkait. Ada carthage - Anda membuat tipe baru, memasukkan beberapa lainnya ke dalamnya. Berdasarkan mereka, Anda juga dapat melakukan pencocokan pola. Ada enumerasi yang dapat membatasi jenis, mengikat jenis yang terkait dengannya.

Alexei Kudryavtsev: Saya akan mengklarifikasi bahwa tipe terkait mirip dengan kelas yang disegel Kotlin. Ini adalah enum di dalam case di mana Anda dapat menempatkan nilai terikat. Sebagai gantinya, Anda dapat menulis: ini masalahnya, perluas, di dalam objek. Misalnya, kasus pengguna dan perusahaan dengan objek yang sesuai dapat berupa enum dan dapat diaktifkan. Hanya kelas yang disegel yang bisa diperluas, dan sakelar terbatas.

Mengapa seorang mobilis membutuhkan fungsionalisme?


Daniil Popov: Bagaimana pendekatan fungsional berguna untuk pengembangan ponsel? Apakah ada masalah yang dia selesaikan?

Evgeny Elchev: Tidak ada masalah khusus yang dapat diselesaikan dengan tepat dengan bantuan pemrograman fungsional.

Yang paling penting adalah bahwa mengikuti prinsip-prinsip ini, bahkan jika itu tidak berhasil, kita harus meninggalkan kondisinya, karena itu adalah rasa sakit utama.

Dengan mengabaikannya, Anda membuat kode Anda lebih mudah dimengerti. Saya tidak mengatakan bahwa akan ada lebih sedikit kesalahan, karena ini setidaknya harus diukur. Namun, ketika Anda mulai mengimplementasikan sesuatu, kode itu berubah. Sering terjadi bahwa Anda melihat kode dan segala sesuatu di dalamnya adalah kasusnya, tetapi Anda mulai menulis ulang, bertukar, menghapus yang tidak perlu dan lebih mudah dibaca.

Mengikuti paradigma fungsional, Anda mendapatkan sumber inspirasi tambahan.

Daniil Popov: Jika saya mulai menulis kelas yang tidak dapat diubah dalam bahasa OOP dan menggunakan metode yang tidak dapat diubah, dapatkah saya mengatakan bahwa saya menulis secara fungsional?

Evgeny Elchev: Ya, saat Anda mulai melihat pro. Semakin mudah untuk menguji metode karena kurangnya negara global, lebih mudah untuk menyusun rantai perhitungan dari metode.

Daniil Popov: Dalam artikel Anda, Anda menjelaskan apa fungsi murni dan efek sampingnya. Anda memberi contoh dengan penjumlahan, di mana fungsinya juga memodifikasi keadaan eksternal. Masalahnya adalah ketika Anda membaca kode seperti itu, sulit untuk mengingat semua perubahan: Anda perlu melihat variabel global ini, siapa lagi yang membacanya, siapa lagi yang menulis kepadanya apa yang bisa terjadi. Tetapi pendekatan fungsional memungkinkan Anda untuk tetap berada di arus, tidak pergi ke kelas tetangga, Anda hanya membaca kode.

Alexei Kudryavtsev: Jika Anda menggunakan bahasa fungsional, maka di satu sisi lebih mudah bagi Anda untuk menulis kode, tetapi di sisi lain, Anda harus memahami jenis monad Anda sekarang.

Evgeny Elchev: Ya, tetapi ketika Anda mulai menulis semuanya pada fungsi murni, masalah lain muncul. Misalnya, cara membangun rantai perhitungan yang panjang. Dalam gaya yang biasa, tanpa memikirkannya, Anda dengan mudah membuang data yang sebelumnya tidak ada di sana. Dalam pendekatan fungsional, ini tidak dapat dilakukan: Anda harus memutuskan rantai, hubungkan semua perhitungan yang digunakan dalam beberapa metode ke negara. Anda harus terbiasa dengannya.

Di sisi lain, tidak seperti kelas di OPP, yang membuat kode mengeras dan sulit untuk disusun, fungsi bisa lebih fleksibel. Anda dapat menulis satu fungsi, menambah kebebasan dengan bantuan penutupan, membuang fungsi-fungsi tersebut dan menggabungkannya ke dalam rantai.

Alexei Kudryavtsev: Ini mirip dengan ideologi Unix: ada bash, terminal dan Anda dapat mentransfer data dari program kecil yang melakukan satu tindakan kecil ke yang lain.

Daniil Popov: Ini mengingatkan saya pada pendekatan Rx, di mana mereka menulis rantai raksasa.

Evgeny Elchev: Anda berdua benar. Dan Unix-way adalah tentang itu, dan Rx adalah perpaduan dari gagasan pengikatan dan reaktivitas. Dalam FP, kami mengikat ke sumber acara dan dalam rantai perhitungan kami mengubahnya, mengikat hasilnya ke keadaan akhir.

Daniil Popov: Apakah bahasa multi-paradigma bagus, betapa nyaman dan bermanfaatnya, bahwa bahasa dapat melakukan ini dan itu?

Evgeny Elchev: Jika Anda benar-benar mengikuti semacam paradigma, akan selalu ada hal-hal yang tidak nyaman untuk dilakukan. Ada hal-hal yang sulit dicapai dalam gaya fungsional, misalnya, menyimpan keadaan dan membuat cache.

Ketika dimungkinkan untuk memilih alat yang lebih cocok untuk tugas tertentu - ini keren.

Anda dapat membuat kelas, di dalamnya buat beberapa metode dengan gaya fungsional dan atur kode secara ringkas dalam rantai, atau tinggalkan kelas sepenuhnya, buat fungsi yang diperlukan, dan gunakan.

The downside adalah bahwa ada dilema pilihan dan semakin banyak pilihan, semakin sulit untuk memilih. Ini juga menjadi semakin sulit untuk dipahami: semakin banyak opsi, semakin sulit untuk membaca kode.

Tentang Monad Jam


Alexei Kudryavtsev: Kembali ke fungsionalisme, apa itu monad?

Evgeny Elchev: Saya akan menyebutnya wadah tempat Anda dapat menggabungkan rantai perhitungan. Cara paling sederhana adalah sebuah wadah tempat Anda dapat menerapkan fungsi dan mengubahnya menjadi wadah baru dengan nilai yang dimodifikasi.

Bayangkan kotak di mana stroberi berada, dan ada perangkat yang memungkinkan Anda membuat selai dari stroberi, tetapi Anda tidak bisa memasukkan sekotak stroberi di dalamnya, Anda harus menuangkannya. Monads - ini adalah hal yang sangat memungkinkan Anda untuk memasukkan kotak ke perangkat.

Ini bukan keadaan dalam arti langsung, karena keadaan disimpan secara terpisah, tetapi di sini adalah konteks (kotak) dengan nilai dan Anda berpindah dari satu ke yang lain. Ini adalah transfer informasi dari satu perhitungan ke perhitungan lainnya.

Alexei Kudryavtsev: Ternyata dalam pendekatan fungsional, untuk membuat kemacetan, Anda harus masuk ke dalam kotak ...

Evgeny Elchev: Keindahannya adalah Anda tidak harus naik ke dalam kotak. Anda bisa melempar kotak.

Fungsionalitas untuk elit?


Daniil Popov: Ada pendapat bahwa pemrograman fungsional tidak dapat dipraktikkan tanpa gelar doktor dalam matematika. Apakah ini benar?

Evgeny Elchev: Ini tidak benar. Pengetahuan matematika, tentu saja, membuat semuanya lebih baik, tetapi saya lupa matematika setelah lulus dan hidup secara normal. Sebenarnya, semua ini adalah alat yang diwujudkan dalam bahasa dalam memecahkan masalah tertentu. Mereka dapat digunakan tanpa mencoba membuktikan secara matematis. Meskipun Anda akan menyusun persamaan dari sudut pandang matematika, akan lebih cepat dan lebih mudah untuk melemparkan beberapa baris kode dengan mengetik, dan mereka akan bekerja.

Alexei Kudryavtsev: Seberapa besar hobi untuk pendekatan fungsional mengganggu pengembangan produk? Jika bagian dari kode sudah ditulis secara fungsional, apakah ada kesulitan dalam bekerja dengannya?

Evgeny Elchev: Tidak sama sekali. Jika Anda bukan seorang maniak dan Anda tidak akan menulis ekosistem besar dengan dekorator, maka Anda dapat menggunakan pencocokan pola yang sama.

Akan lebih sulit jika Anda ingin beralih ke elemen fungsionalisme baru. Misalnya, Swift kelima dan hasil monad baru-baru ini muncul, Anda belum pernah menggunakannya sebelumnya, tetapi sekarang Anda memutuskan bahwa semuanya akan ada di sana. Anda mengambil fungsi kueri ke jaringan dan menulis bahwa hasilnya sekarang adalah hasil (baik data atau kesalahan), dan Anda memutuskan untuk menggabungkan dengan permintaan berikutnya, dan di sana Anda memiliki penutupan terpisah dengan nilai dan kesalahan, dan Anda perlu menulis ulang. Saya mulai menulis seperti ini di satu tempat, terbangun dua hari kemudian, ketika saya menulis ulang setengah kode, saya juga membuat pembungkus baru untuk perpustakaan untuk diintegrasikan dengan indah.

Di mana untuk memulai?


Daniil Popov: Apa yang harus dibaca pemula untuk memahami pemrograman fungsional?

Evgeny Elchev: Kita perlu menggunakan bahasa yang murni fungsional, misalnya, Haskell dan mencobanya dalam praktik. Anda mengambil buku teks dan melakukan contoh paling sederhana. Di sini Anda memahami pendekatan - ketika tidak ada untuk, Anda tidak dapat membuat variabel di mana Anda dapat mengubah nilainya. Secara pribadi, saya pernah mengambil buku "Pelajari Haskell atas nama kebaikan", di mana semuanya dijelaskan dalam bahasa yang sederhana. Setelah itu, Anda dapat mulai membaca artikel di Internet: tentang bagaimana tampilan monads di Swift, tentang tipe data aljabar. Beberapa artikel, dan menjadi jelas bahwa ini tidak perlu takut.

Daniil Popov : Hal yang paling sulit adalah mematahkan paradigma di kepala Anda sendiri.

Evgeny Elchev: Tidak perlu terjun secara tajam ke pemrograman fungsional. Banyak orang berpikir bahwa mereka berdua akan duduk dan mulai menulis secara fungsional - ini salah.

Alexei Kudryavtsev: Hal paling keren yang saya lihat adalah kursus Stepic oleh Haskell dari Denis Moskvin . Anda mulai dengan menambahkan beberapa angka, dan berakhir dengan membungkus monad dalam monad. Dan jika Anda ingin benar-benar menghancurkan pikiran Anda, yaitu, buku "Struktur interpretasi program komputer" adalah kursus di Lisp dari contoh-contoh sederhana hingga apa yang Anda tulis sebagai interpreter Lisp dalam Lisp.

Jika ketakutan utama fungsionalisme telah berlalu, maka lihatlah laporan Vitaliy Bragilevsky dari spring AppsConf. Namun, di musim gugur AppsConf kami akan menyentuh topik yang tidak kalah menarik - komunitas iOS menantikan laporan oleh Daniil Goncharov tentang rekayasa balik Bluetooth , dan pengembang android bersama dengan Alexander Smirnov akan membahas pendekatan saat ini untuk membangun animasi

Source: https://habr.com/ru/post/id462121/


All Articles