Karena bahasa mesin (misalnya, 0110101000110101
) bahasa komputer umumnya berevolusi ke bentuk abstraksi yang lebih tinggi, umumnya membuatnya lebih mudah untuk memahami kode ketika diterapkan pada masalah. Assembler adalah abstraksi atas kode mesin, C adalah abstraksi atas assembler, dll.
Desain berorientasi objek tampaknya sangat baik dalam memungkinkan kita untuk memodelkan masalah dalam hal objek, misalnya, masalah sistem pendaftaran program universitas dapat dimodelkan dengan Course
kelas, Student
kelas, dll. Kemudian, ketika kita menulis solusinya dalam bahasa OO, kami memiliki kelas serupa yang mendapatkan tanggung jawab dan yang umumnya bermanfaat untuk desain, terutama untuk memodulasi kode. Jika saya memberikan masalah ini kepada 10 tim independen yang menyelesaikannya dengan metode OO, umumnya 10 solusi akan memiliki kelas yang berkaitan dengan masalah yang sama. Mungkin ada banyak perbedaan ketika Anda mulai masuk ke dalam penggabungan dan interaksi dari kelas-kelas tersebut, sehingga tidak ada yang namanya "kesenjangan representasi nol."
Pengalaman saya dengan Pemrograman Fungsional sangat terbatas (tidak ada penggunaan dunia nyata, hanya program jenis Hello World). Saya gagal melihat bagaimana bahasa tersebut memungkinkan pemetaan solusi FP dengan mudah ke masalah (dengan kesenjangan representasi yang rendah) seperti yang dilakukan bahasa OO.
Saya mengerti keuntungan FP sehubungan dengan pemrograman bersamaan. Tetapi apakah saya kehilangan sesuatu, atau apakah FP bukan tentang mengurangi kesenjangan representasional (membuat solusi lebih mudah dipahami)?
Cara lain untuk bertanya: apakah kode FP dari 10 tim berbeda yang menyelesaikan masalah dunia nyata yang sama memiliki banyak kesamaan?
Dari Wikipedia tentang Abstraksi (ilmu komputer) (penekanan pada saya):
Bahasa pemrograman fungsional umumnya menunjukkan abstraksi yang terkait dengan fungsi , seperti abstraksi lambda (membuat istilah menjadi fungsi beberapa variabel), fungsi orde tinggi (parameter adalah fungsi), abstraksi braket (membuat istilah menjadi fungsi dari variabel).
Kesenjangan representasional dapat berpotensi meningkat, karena [beberapa] masalah dunia nyata tidak dimodelkan dengan mudah dengan abstraksi semacam itu.
Cara lain yang saya lihat mengurangi kesenjangan representasional adalah dalam menelusuri elemen solusi kembali ke masalah. Kode 0
's' dan ' in ' 1
sangat sulit dilacak, sedangkan Student
kelasnya mudah dilacak. Tidak semua kelas OO melacak dengan mudah ke ruang masalah, tetapi banyak yang melakukannya.
Bukankah abstraksi FP selalu perlu dijelaskan untuk mencari tahu bagian mana dari ruang masalah yang mereka selesaikan (selain dari masalah matematika )?OK - saya bagus di bagian ini. Setelah melihat lebih banyak contoh, saya melihat bagaimana abstraksi FP sangat jelas untuk bagian masalah yang diekspresikan dalam pemrosesan data.
Jawaban yang diterima untuk pertanyaan terkait Bisakah UML digunakan untuk memodelkan program Fungsional? - mengatakan "Programer fungsional tidak memiliki banyak kegunaan untuk diagram." Saya benar-benar tidak peduli apakah itu UML, tapi itu membuat saya bertanya-tanya tentang abstraksi FP menjadi mudah dimengerti / dikomunikasikan, jika tidak ada diagram yang banyak digunakan (dengan asumsi jawaban ini benar). Sekali lagi, tingkat penggunaan / pemahaman FP saya sepele, jadi saya mengerti tidak perlu diagram pada program FP sederhana.
Desain OO memiliki fungsi / kelas / tingkat paket abstraksi, dengan enkapsulasi (kontrol akses, penyembunyian informasi) di masing-masing, yang membuat pengelolaan kompleksitas menjadi lebih mudah. Ini adalah elemen yang memungkinkan pergi dari masalah ke solusi dan kembali dengan lebih mudah.
Banyak jawaban berbicara tentang bagaimana analisis dan desain dilakukan dalam FP dengan cara yang analog dengan OO, tetapi tidak ada yang mengutip apa pun tingkat tinggi sejauh ini (paul mengutip beberapa hal menarik, tetapi tingkat rendah). Saya melakukan banyak Googling kemarin dan menemukan beberapa diskusi yang menarik. Berikut ini adalah dari Program Fungsional Refactoring oleh Simon Thompson (2004) (penekanan tambang)
Dalam mendesain sistem berorientasi objek, dapat diterima bahwa desain akan mendahului pemrograman. Desain akan ditulis menggunakan sistem seperti UML yang didukung dalam alat-alat seperti Eclipse. Pemrogram pemula mungkin belajar pendekatan desain visual menggunakan sistem seperti BlueJ. Pekerjaan pada metodologi serupa untuk pemrograman fungsional dilaporkan dalam FAD: Analisis Fungsional dan Desain , tetapi sedikit pekerjaan lain yang ada. Mungkin ada sejumlah alasan untuk ini.
Program fungsional yang ada adalah skala yang tidak memerlukan desain. Banyak program fungsional kecil, tetapi yang lain, seperti Glasgow Haskell Compiler, substansial.
Program fungsional secara langsung memodelkan domain aplikasi, sehingga membuat desain tidak relevan. Sementara bahasa fungsional memberikan berbagai abstraksi yang kuat, sulit untuk membantah bahwa ini menyediakan semua dan hanya abstraksi yang diperlukan untuk memodelkan dunia nyata.
Program fungsional dibangun sebagai serangkaian prototipe yang berkembang.
Dalam tesis PhD yang dikutip di atas , manfaat menggunakan Analisis dan metodologi desain (ADM) diuraikan secara independen dari paradigma. Tetapi argumen dibuat bahwa ADM harus sejajar dengan paradigma implementasi. Artinya, OOADM bekerja paling baik untuk pemrograman OO dan tidak diterapkan dengan baik pada paradigma lain seperti FP. Berikut adalah kutipan yang bagus yang saya pikir memparafrasekan apa yang saya sebut kesenjangan representasional:
kita dapat berdebat panjang lebar tentang paradigma mana yang memberikan dukungan terbaik untuk pengembangan perangkat lunak, tetapi seseorang mencapai paket pengembangan yang paling alami, efisien dan efektif ketika seseorang tetap berada dalam paradigma tunggal mulai dari deskripsi masalah hingga implementasi dan pengiriman.
Berikut adalah set diagram yang diusulkan oleh FAD:
- diagram ketergantungan fungsi yang menyajikan fungsi dengan yang digunakannya dalam implementasinya;
- type dependency diagram yang menyediakan layanan yang sama untuk tipe; dan,
- diagram dependensi modul yang menyajikan tampilan arsitektur modul sistem.
Ada studi kasus di bagian 5.1 dari tesis FAD, yang merupakan sistem untuk mengotomatisasi produksi data yang berkaitan dengan liga sepak bola. Persyaratannya adalah 100% fungsional, misalnya, memasukkan hasil sepak bola, menghasilkan tabel liga, tabel penilaian, tabel kehadiran, mentransfer pemain antar tim, memperbarui data setelah hasil baru, dll. Tidak disebutkan bagaimana FAD bekerja untuk menyelesaikan persyaratan non-fungsional didokumentasikan , selain menyatakan bahwa "fungsionalitas baru harus diizinkan dengan biaya minimal", sesuatu yang hampir mustahil untuk diuji.
Sayangnya, terlepas dari FAD, saya tidak melihat referensi modern untuk bahasa pemodelan (visual) yang diusulkan untuk FP. UML adalah paradigma lain, jadi kita harus melupakannya.
Jawaban:
Data dasar terstruktur sama dalam hampir semua paradigma. Anda akan memiliki
Student
, sebuahCourse
, dll apakah itu sebuah objek, struct, catatan, atau apa pun. Perbedaannya dengan OOP bukanlah bagaimana data terstruktur, melainkan bagaimana fungsinya terstruktur.Saya benar-benar menemukan program fungsional yang jauh lebih cocok dengan cara saya berpikir tentang masalah. Misalnya, untuk merencanakan jadwal siswa untuk semester depan, Anda berpikir tentang hal-hal seperti daftar kursus yang telah diselesaikan siswa, kursus dalam program gelar siswa, kursus yang ditawarkan semester ini, kursus yang telah diselesaikan siswa dengan prasyarat, kursus dengan waktu yang tidak konflik, dll.
Tiba-tiba, tidak jelas kelas mana yang harus membuat dan menyimpan semua daftar ini. Apalagi ketika Anda memiliki kombinasi kompleks dari daftar ini. Namun, Anda harus memilih satu kelas.
Di FP, Anda menulis fungsi yang mengambil siswa dan daftar kursus dan mengembalikan daftar kursus yang difilter. Anda dapat mengelompokkan semua fungsi tersebut bersama-sama dalam sebuah modul. Anda tidak harus memasangkannya dengan satu kelas atau yang lain.
Jadi model data Anda pada akhirnya tampak lebih seperti bagaimana programmer OOP memikirkan model mereka, sebelum mereka tercemar dengan kelas yang tidak memiliki tujuan lain selain menyediakan tempat yang nyaman untuk meletakkan fungsi yang beroperasi pada kombinasi kelas lain. Tidak ada
CourseStudentFilterList
kelas aneh atau serupa yang Anda selalu butuhkan di OOP, tetapi tidak pernah memikirkan desain awal.sumber
StudentCourseFilterer
untuk menjaga hal-hal dapat dikelolafunc
(seperti yang Anda beri namaIFilter
, dll). Secara umum, di FP Anda akan menamainyafunc
atauf
ketika salah satusort
ataufilter
pilihan yang valid! Sebagai contoh, saat menulis fungsi tingkat tinggi yang diambilfunc
sebagai parameter.Ketika saya mengambil kelas Java saya bertahun-tahun yang lalu, kami diharapkan menunjukkan solusi kami untuk seluruh kelas, jadi saya harus melihat bagaimana orang berpikir; bagaimana mereka memecahkan masalah secara logis. Saya sepenuhnya mengharapkan solusi untuk mengelompokkan sekitar tiga atau empat solusi umum. Sebagai gantinya, saya menyaksikan 30 siswa memecahkan masalah dengan 30 cara yang sangat berbeda.
Tentu saja, ketika programmer pemula mendapatkan pengalaman, mereka akan mendapatkan paparan pola perangkat lunak umum, mulai menggunakan pola-pola itu pada kode mereka, dan kemudian solusi mereka mungkin menyatu di sekitar beberapa strategi optimal. Pola-pola ini membentuk bahasa teknis dimana pengembang berpengalaman dapat berkomunikasi.
Bahasa teknis yang menopang pemrograman fungsional adalah matematika . Dengan demikian, masalah yang paling cocok untuk diselesaikan dengan menggunakan pemrograman fungsional pada dasarnya adalah masalah matematika. Dengan matematika, saya tidak mengacu pada jenis matematika yang akan Anda lihat dalam solusi bisnis, seperti penambahan dan pengurangan. Sebaliknya, saya berbicara tentang jenis matematika yang mungkin Anda lihat di Math Overflow, atau jenis matematika yang akan Anda lihat di mesin pencari Orbitz (ditulis dalam Lisp).
Orientasi ini memiliki beberapa konsekuensi penting bagi programmer fungsional yang mencoba memecahkan masalah pemrograman dunia nyata:
Pemrograman fungsional lebih bersifat deklaratif daripada keharusan; itu menyangkut dirinya sendiri terutama dengan memberi tahu komputer apa yang harus dilakukan, bukan bagaimana melakukannya.
Program berorientasi objek seringkali dibangun dari atas ke bawah. Desain kelas dibuat, dan detailnya diisi. Program fungsional sering dibangun dari bawah ke atas, dimulai dengan fungsi kecil dan terperinci yang digabungkan menjadi fungsi tingkat yang lebih tinggi.
Pemrograman fungsional dapat memiliki keuntungan untuk prototyping logika kompleks, membangun program fleksibel yang dapat mengubah dan berkembang secara organik, dan membangun perangkat lunak di mana desain awal tidak jelas.
Program berorientasi objek dapat lebih cocok untuk domain bisnis karena kelas, pesan antar objek, dan pola perangkat lunak semuanya menyediakan struktur yang memetakan ke domain bisnis, menangkap kecerdasan bisnisnya, dan mendokumentasikannya.
Karena semua perangkat lunak praktis menghasilkan efek samping (I / O), bahasa pemrograman yang murni fungsional memerlukan mekanisme untuk menghasilkan efek samping tersebut sambil tetap murni secara matematis (monad).
Program fungsional dapat lebih mudah dibuktikan, karena sifat matematikanya. Sistem tipe Haskell dapat menemukan hal-hal pada waktu kompilasi yang sebagian besar bahasa OO tidak bisa.
Dan seterusnya. Seperti banyak hal dalam komputasi, bahasa berorientasi objek dan bahasa fungsional memiliki pengorbanan yang berbeda.
Beberapa bahasa OO modern telah mengadopsi beberapa konsep pemrograman fungsional yang berguna, sehingga Anda dapat memiliki yang terbaik dari kedua dunia. Linq, dan fitur bahasa yang ditambahkan untuk mendukungnya, adalah contoh yang bagus untuk itu.
sumber
SafeString
tipe untuk mewakili string yang telah di-HTML-lolos - dan umumnya melacak lebih banyak efek.Saya ingin menekankan aspek yang saya anggap penting dan belum tercakup dalam jawaban lainnya.
Pertama-tama, saya berpikir bahwa kesenjangan representasional antara masalah dan solusi bisa lebih di benak programmer, sesuai dengan latar belakang mereka dan konsep-konsep yang lebih mereka kenal.
OOP dan FP melihat data dan operasi dari dua perspektif yang berbeda dan memberikan pengorbanan yang berbeda, seperti yang telah ditunjukkan oleh Robert Harvey.
Salah satu aspek penting di mana mereka berbeda adalah cara mereka mengizinkan untuk memperluas perangkat lunak Anda.
Pertimbangkan situasi di mana Anda memiliki kumpulan tipe data dan kumpulan operasi, misalnya Anda memiliki format gambar yang berbeda dan Anda memelihara perpustakaan algoritma untuk memproses gambar.
Pemrograman fungsional membuatnya lebih mudah untuk menambahkan operasi baru ke perangkat lunak Anda: Anda hanya perlu perubahan lokal dalam kode Anda, yaitu Anda menambahkan fungsi baru, yang menangani berbagai format data input. Di sisi lain, menambahkan format baru lebih terlibat: Anda perlu mengubah semua fungsi yang sudah Anda implementasikan (perubahan non lokal).
Pendekatan berorientasi objek simetris dengan ini: setiap tipe data membawa implementasi sendiri dari semua operasi dan bertanggung jawab untuk memilih implementasi yang tepat saat runtime (pengiriman dinamis). Ini membuatnya mudah untuk menambahkan tipe data baru (mis. Format gambar baru): Anda cukup menambahkan kelas baru dan mengimplementasikan semua metodenya. Di sisi lain, menambahkan operasi baru berarti mengubah semua kelas yang perlu menyediakan operasi itu. Dalam banyak bahasa ini dilakukan dengan memperluas antarmuka dan mengadaptasi semua kelas yang mengimplementasikannya.
Pendekatan berbeda terhadap ekstensi ini adalah salah satu alasan mengapa OOP lebih sesuai untuk masalah tertentu di mana rangkaian operasi lebih jarang bervariasi dibandingkan dengan set tipe data tempat operasi ini bekerja. Sebuah contoh khas adalah GUI: Anda memiliki satu set tetap operasi bahwa semua widget harus menerapkan (
paint
,resize
,move
, dan sebagainya) dan kumpulan widget yang Anda ingin memperpanjang.Jadi, menurut dimensi ini, OOP dan FP hanyalah dua cara specular untuk mengatur kode Anda. Lihat SICP , khususnya Bagian 2.4.3, Tabel 2.22, dan paragraf yang disampaikan .
Meringkas: di OOP Anda menggunakan data untuk mengatur operasi, di FP Anda menggunakan operasi untuk mengatur data. Setiap pendekatan lebih kuat atau lebih lemah sesuai dengan konteksnya. Secara umum, tidak satu pun dari keduanya memiliki kesenjangan representasional yang lebih tinggi antara masalah dan solusi.
sumber
Other
varian / konstruktor dalam tipe data Anda, dan parameter fungsi ekstra untuk diteruskan ke semua fungsi untuk menangani kasus lainnya . Tentu saja canggung / kurang alami sehubungan dengan solusi OOP (pengiriman dinamis). Dengan cara yang sama, pola pengunjung canggung / kurang alami daripada solusi FP (fungsi tingkat tinggi).Sebagian besar bahasa fungsional tidak Berorientasi Objek. Itu tidak berarti mereka tidak memiliki objek (dalam arti tipe kompleks yang memiliki fungsi spesifik yang terkait dengannya). Haskell, seperti java, memiliki Daftar, Peta, Array, semua jenis Pohon dan banyak jenis kompleks lainnya. Jika Anda melihat modul Daftar Haskell atau Peta, Anda akan melihat serangkaian fungsi yang sangat mirip dengan metode di sebagian besar modul perpustakaan bahasa OO. Jika Anda memeriksa kode, Anda bahkan akan menemukan enkapsulasi yang serupa, dengan beberapa tipe (atau konstruktornya, tepatnya) dan fungsinya hanya dapat digunakan oleh fungsi-fungsi lain dalam modul.
Cara Anda bekerja dengan tipe-tipe ini di Haskell seringkali tidak jauh berbeda dari cara OO. Di Haskell saya katakan
dan di Jawa Anda katakan
Tomat, tomat. Fungsi Haskell tidak terikat pada objek tetapi mereka terkait erat dengan tipenya. ¨Ah, tapi , ¨ kamu bilang, ¨ di Jawa itu memanggil metode mana yang panjangnya sesuai dengan kelas sebenarnya dari objek itu¨. Kejutan - Haskell juga melakukan polimorfisme dengan kelas tipe (dan hal-hal lain).
Di Haskell, jika saya punya adalah - kumpulan bilangan bulat - tidak masalah apakah koleksi itu daftar atau set atau larik, kode ini
akan mengembalikan koleksi dengan semua elemen digandakan. Polimorfisme berarti bahwa fungsi peta yang sesuai untuk tipe spesifik akan dipanggil.
Contoh-contoh ini, sebenarnya, bukan tipe yang benar-benar kompleks tetapi hal yang sama berlaku pada masalah yang lebih sulit. Gagasan bahwa bahasa fungsional tidak memungkinkan Anda memodelkan objek yang rumit dan mengaitkan fungsionalitas tertentu dengannya sangat salah.
Ada yang perbedaan penting dan signifikan antara gaya fungsional dan OO tapi saya tidak berpikir jawaban ini harus berurusan dengan mereka. Anda bertanya apakah bahasa fungsional mencegah (atau menghambat) pemodelan intuitif masalah dan tugas. Jawabannya adalah tidak.
sumber
FP memang berupaya untuk mengurangi kesenjangan representasional:
Sesuatu yang Anda akan melihat banyak dalam bahasa fungsional adalah praktik membangun bahasa (menggunakan desain bottom-up) menjadi Bahasa Tertentu Domain Tertanam (EDSL) . Ini memungkinkan Anda mengembangkan cara untuk mengungkapkan masalah bisnis Anda dengan cara yang wajar untuk domain Anda dalam bahasa pemrograman. Haskell dan Lisp sama-sama bangga akan hal ini.
Bagian (jika tidak semua) dari apa yang memungkinkan kemampuan ini adalah lebih banyak fleksibilitas dan ekspresi dalam bahasa dasar itu sendiri; dengan fungsi kelas satu, fungsi tingkat tinggi, komposisi fungsi, dan dalam beberapa bahasa, tipe data aljabar (serikat terdiskriminasi AKA) dan kemampuan untuk mendefinisikan operator *, ada lebih banyak fleksibilitas yang tersedia untuk cara mengekspresikan sesuatu daripada yang ada dengan OOP, yang berarti Anda mungkin akan menemukan cara yang lebih alami untuk mengekspresikan sesuatu dari domain masalah Anda. (Seharusnya mengatakan sesuatu yang banyak bahasa OOP telah mengadopsi banyak fitur ini baru-baru ini, jika tidak memulai dengan mereka.)
* Ya, dalam banyak bahasa OOP Anda dapat mengganti operator standar, tetapi di Haskell, Anda dapat menentukan yang baru!
Dalam pengalaman saya bekerja dengan bahasa fungsional (kebanyakan F # dan Haskell, beberapa Clojure, dan sedikit Lisp dan Erlang), saya memiliki waktu yang lebih mudah memetakan ruang masalah ke dalam bahasa daripada yang saya lakukan dengan OOP - terutama dengan tipe data aljabar, yang saya temukan lebih fleksibel daripada kelas. Sesuatu yang mendorong saya untuk mengulang ketika memulai dengan FP, terutama dengan Haskell, adalah bahwa saya harus berpikir / bekerja pada tingkat yang sedikit lebih tinggi daripada yang saya gunakan dalam bahasa imperatif atau OOP; Anda mungkin mengalami sedikit ini juga.
sumber
Seperti yang dikatakan Niklaus Wirth, "Algoritma + Struktur Data = Program". Pemrograman fungsional adalah tentang cara mengatur algoritma, dan tidak memberi tahu banyak tentang cara mengatur struktur data. Memang, ada bahasa FP baik dengan variabel bisa berubah (Lisp) dan tidak berubah (Haskell, Erlang). Jika Anda ingin membandingkan dan membedakan FP dengan sesuatu, Anda harus memilih pemrograman imperatif (C, Java) dan deklaratif (Prolog).
OOP di sisi lain adalah cara untuk membangun struktur data dan melampirkan algoritma pada mereka. FP tidak mencegah Anda membangun struktur data yang mirip dengan OOP. Jika Anda tidak mempercayai saya, lihatlah deklarasi tipe Haskell. Namun, sebagian besar bahasa FP setuju bahwa fungsi bukan milik struktur data dan lebih baik berada di namespace yang sama. Ini dilakukan untuk menyederhanakan membangun algoritma baru melalui komposisi fungsi. Memang, jika Anda tahu input apa yang dibutuhkan suatu fungsi dan output apa yang dihasilkannya, mengapa harus mementingkan struktur data yang mana juga? Sebagai contoh, mengapa
add
fungsi harus dipanggil pada instance tipe Integer dan mengeluarkan argumen alih-alih hanya melewati dua argumen Integer?Oleh karena itu, saya tidak melihat mengapa FP harus membuat solusi lebih sulit untuk dipahami secara umum, dan saya tidak berpikir itu melakukannya. Namun, perlu diingat bahwa programmer imperatif berpengalaman pasti akan menemukan program fungsional lebih sulit untuk dipahami daripada yang imperatif, dan sebaliknya. Ini mirip dengan dikotomi Windows-Linux ketika orang-orang yang telah berinvestasi 10 tahun untuk merasa nyaman dengan lingkungan Windows menemukan Linux sulit setelah satu bulan penggunaan, dan mereka yang terbiasa dengan Linux tidak dapat mencapai produktivitas yang sama di Windows. Oleh karena itu, 'kesenjangan representasional' yang Anda bicarakan sangat subjektif.
sumber
if you know what input a function takes and what output it produces, why should it matter which data structure it belongs too?
Kedengarannya sangat mirip kohesi, yang penting bagi sistem modular. Saya berpendapat bahwa tidak mengetahui di mana menemukan suatu fungsi akan membuat lebih sulit untuk memahami solusi, yang disebabkan oleh kesenjangan representasional yang lebih tinggi. Banyak sistem OO memiliki masalah ini, tetapi itu karena desain yang buruk (kohesi rendah). Sekali lagi, masalah namespace keluar dari keahlian saya di FP, jadi mungkin itu tidak seburuk kedengarannya.