Ada sebuah artikel klasik bernama On the Criteria To Be Digunakan dalam Sistem Penguraian menjadi Modul yang baru saja saya baca untuk pertama kalinya. Masuk akal bagi saya, dan mungkin merupakan salah satu artikel yang menjadi dasar OOP. Kesimpulannya:
Kami telah mencoba menunjukkan dengan contoh-contoh ini bahwa hampir selalu salah untuk memulai penguraian suatu sistem menjadi modul-modul berdasarkan diagram alur. ... Setiap modul kemudian dirancang untuk menyembunyikan keputusan seperti itu dari yang lain
Menurut pendapat saya yang tidak berpendidikan dan tidak berpengalaman, pemrograman fungsional mengambil saran yang berlawanan dari artikel ini. Pemahaman saya adalah pemrograman fungsional membuat aliran data idiomatis. Data diteruskan dari satu fungsi ke fungsi lainnya, masing-masing fungsi sangat menyadari data dan "mengubahnya" di sepanjang jalan. Dan saya pikir saya telah melihat pembicaraan Hickey Kaya di mana dia berbicara tentang bagaimana menyembunyikan data berlebihan atau tidak perlu atau sesuatu, tapi saya tidak ingat pasti.
- Pertama saya ingin tahu apakah penilaian saya benar. Apakah paradigma FP dan artikel ini secara filosofis tidak setuju?
- Dengan asumsi mereka tidak setuju, bagaimana FP "mengompensasi" karena kurangnya data yang disembunyikan? Mungkin mereka mengorbankan data yang disembunyikan tetapi mendapatkan X, Y dan Z. Saya ingin tahu alasan mengapa X, Y dan Z dianggap lebih menguntungkan daripada menyembunyikan data.
- Atau, dengan asumsi mereka tidak setuju, mungkin FP merasa bahwa menyembunyikan data itu buruk. Jika demikian, mengapa menurutnya menyembunyikan data itu buruk?
- Dengan asumsi mereka setuju, saya ingin tahu apa implementasi menyembunyikan data oleh FP. Jelas melihat ini di OOP. Anda dapat memiliki
private
bidang yang tidak dapat diakses siapa pun di luar kelas. Tidak ada analogi yang jelas tentang ini bagi saya di FP. - Saya merasa ada pertanyaan lain yang harus saya tanyakan tetapi saya tidak tahu saya harus bertanya. Jangan ragu untuk menjawabnya juga.
Memperbarui
Saya menemukan pembicaraan Neal Ford ini yang memiliki slide yang sangat relevan di dalamnya. Saya akan menyematkan tangkapan layar di sini:
sumber
Jawaban:
Artikel yang Anda sebutkan adalah tentang modularitas secara umum, dan akan berlaku sama untuk program yang terstruktur, fungsional, dan berorientasi objek. Saya telah mendengar artikel itu sebelumnya dari seseorang yang adalah seorang pria OOP besar, tetapi saya membacanya sebagai artikel tentang pemrograman secara umum, bukan sesuatu yang OOP spesifik. Ada sebuah artikel terkenal tentang pemrograman fungsional, Mengapa Masalah Pemrograman Fungsional , dan kalimat pertama dari kesimpulan menyatakan "Dalam makalah ini, kami berpendapat bahwa modularitas adalah kunci untuk pemrograman yang sukses." Jadi jawaban untuk (1) adalah tidak.
Fungsi yang dirancang dengan baik tidak mengasumsikan lebih banyak tentang data mereka daripada yang seharusnya, sehingga bagian tentang "sangat menyadari data" salah. (Atau setidaknya sama salahnya dengan OOP. Anda tidak dapat memprogram secara ketat pada abstraksi tingkat tinggi dan mengabaikan semua detail selamanya dalam paradigma apa pun. Pada akhirnya, beberapa bagian dari program ini benar-benar perlu mengetahui tentang rincian spesifik data.)
Penyembunyian data adalah istilah khusus OOP, dan itu tidak persis sama dengan menyembunyikan informasi yang dibahas dalam artikel. Informasi yang disembunyikan dalam artikel ini adalah tentang keputusan desain yang sulit dibuat atau kemungkinan akan berubah. Tidak setiap keputusan desain tentang format data sulit atau mungkin berubah, dan tidak setiap keputusan sulit atau mungkin berubah adalah tentang format data. Secara pribadi, saya tidak dapat melihat mengapa programmer OO ingin semuanya menjadi objek. Terkadang, Anda hanya membutuhkan struktur data sederhana.
Sunting: Saya menemukan kutipan yang relevan dari wawancara dengan Rich Hickey .
sumber
Tidak juga, tetapi hal itu menambah diskusi, khususnya bagi para praktisi yang, pada saat itu, dilatih untuk menguraikan sistem menggunakan kriteria pertama yang ia jelaskan di koran.
Tidak. Terlebih lagi, bagi saya, uraian Anda tentang seperti apa program FP tidak berbeda dari yang lain yang menggunakan prosedur atau fungsi:
... kecuali bagian "keintiman", karena Anda dapat (dan sering melakukannya) memiliki fungsi yang beroperasi pada data abstrak, tepatnya untuk menghindari keintiman. Dengan demikian, Anda memiliki kontrol atas "keintiman" itu dan Anda dapat mengaturnya sesuka Anda, dengan mengatur antarmuka (mis. Fungsi) untuk apa yang ingin Anda sembunyikan.
Jadi, saya tidak melihat alasan mengapa kita tidak dapat mengikuti kriteria penyembunyian informasi Parnas menggunakan pemrograman fungsional dan berakhir dengan implementasi indeks KWIC dengan manfaat yang sama dengan implementasi keduanya.
Sejauh menyangkut data, Anda dapat menguraikan abstraksi data dan abstraksi tipe data menggunakan FP. Semua ini menyembunyikan struktur beton dan manipulasi struktur beton ini menggunakan fungsi sebagai abstraksi.
EDIT
Ada semakin banyak pernyataan di sini yang menyatakan bahwa "menyembunyikan data" dalam konteks FP tidak begitu berguna (atau OOP-ish (?)). Jadi, izinkan saya memberi stempel di sini contoh yang sangat sederhana dan jelas dari SICP:
Misalkan sistem Anda perlu bekerja dengan bilangan rasional. Salah satu cara Anda mungkin ingin mewakili mereka adalah sebagai pasangan atau daftar dua bilangan bulat: pembilang dan penyebut. Demikian:
Jika Anda mengabaikan abstraksi data, kemungkinan besar Anda akan mendapatkan pembilang dan penyebut menggunakan
car
dancdr
:Mengikuti pendekatan ini, semua bagian dari sistem yang memanipulasi bilangan rasional akan tahu bahwa bilangan rasional adalah
cons
- mereka akancons
membuat angka rasional dan mengekstraknya menggunakan operator daftar.Satu masalah yang mungkin Anda hadapi adalah ketika Anda perlu mengurangi bentuk bilangan rasional - perubahan akan diperlukan di seluruh sistem. Juga, jika Anda memutuskan untuk mengurangi pada waktu pembuatan, Anda mungkin menemukan kemudian bahwa mengurangi ketika mengakses salah satu istilah rasional lebih baik, menghasilkan perubahan skala penuh lainnya.
Masalah lain adalah jika, secara hipotesis, representasi alternatif untuk mereka lebih disukai dan Anda memutuskan untuk meninggalkan
cons
representasi - perubahan skala penuh lagi.Setiap upaya yang waras dalam menghadapi situasi ini kemungkinan akan mulai menyembunyikan representasi rasional di balik antarmuka. Pada akhirnya, Anda mungkin berakhir dengan sesuatu seperti ini:
(make-rat <n> <d>)
mengembalikan bilangan rasional yang pembilangnya adalah bilangan bulat<n>
dan yang penyebutnya bilangan bulat<d>
.(numer <x>)
mengembalikan pembilang dari bilangan rasional<x>
.(denom <x>)
mengembalikan penyebut bilangan rasional<x>
.dan sistem tidak akan lagi (dan seharusnya tidak lagi) tahu dari apa rasionalisasi dibuat. Hal ini karena
cons
,car
dancdr
tidak intrinsik untuk rasional, tetapimake-rat
,numer
dandenom
yang . Tentu saja, ini bisa dengan mudah menjadi sistem FP. Jadi, "penyembunyian data" (dalam hal ini, lebih dikenal sebagai abstraksi data, atau upaya untuk merangkum representasi dan struktur konkret) muncul sebagai konsep yang relevan dan teknik yang banyak digunakan dan dieksplorasi, baik dalam konteks OO, pemrograman fungsional atau terserah.Dan intinya adalah ... meskipun orang mungkin mencoba untuk membuat perbedaan antara apa "jenis persembunyian" atau enkapsulasi yang mereka lakukan (apakah mereka menyembunyikan keputusan desain, atau struktur data atau algoritma - dalam kasus abstraksi prosedural), semuanya memiliki tema yang sama: mereka dimotivasi oleh satu atau lebih poin yang dibuat Parnas secara eksplisit. Itu adalah:
Contoh di atas diambil dari buku SICP jadi, untuk diskusi lengkap dan presentasi konsep-konsep ini dalam buku, saya sangat merekomendasikan memeriksa bab 2 . Saya juga merekomendasikan untuk membiasakan diri dengan Tipe Data Abstrak dalam konteks FP, yang membawa masalah lain ke tabel.
sumber
Keyakinan Anda bahwa pemrograman fungsional tidak memiliki data yang disembunyikan adalah salah. Hanya dibutuhkan pendekatan berbeda untuk menyembunyikan data. Salah satu cara paling umum untuk menyembunyikan data dalam pemrograman fungsional adalah melalui penggunaan fungsi polimorfik yang menggunakan fungsi sebagai argumen. Misalnya fungsi ini
hanya dapat melihat struktur terluar data (yaitu bahwa itu adalah daftar) ia tidak dapat melihat apa pun tentang data yang berisi daftar dan hanya dapat beroperasi pada data melalui fungsi tunggal yang diteruskan ke itu.
Fungsi yang dilewatkan sebagai argumen analog dengan metode publik pada tipe data yang berisi daftar. Ini memberikan cara operasi yang terbatas pada data, tetapi tidak mengekspos cara kerja internal tipe data.
sumber
Saya akan mengambil risiko di sini dan mengatakan bahwa konsep itu tidak relevan di FP seperti di OO.
tl; dr; Maksud dari penyembunyian data adalah untuk memastikan bahwa tanggung jawab dipertahankan di mana seharusnya mereka berada, dan Anda tidak memiliki aktor luar yang mengacaukan data yang tidak mereka ketahui. Dalam data FP dihasilkan oleh ekspresi, dan dengan cara ini Anda tidak dapat mengacaukan data karena itu bukan sifat yang bisa berubah seperti halnya komputasi komposer yang benar-benar mengubah aturan permainan.
Dalam pengalaman saya dengan FP; yang diakui tidak signifikan, saya cenderung menemukan satu kontras dengan OO dalam apa yang menunjukkan pemodelan data yang baik / umum.
Kontras ini adalah bahwa dalam OO secara umum Anda memodelkan hal-hal untuk mewakili data Anda. Analogi mobil wajib:
OO
Yang perlu diperhatikan di sini adalah ketika Anda memodelkan hal-hal dalam format OO, itu semua tentang mewakili hal-hal sebagai Data. Anda memiliki objek dengan properti, banyak dari properti itu adalah objek dengan lebih banyak properti. Anda memiliki beberapa metode di sana-sini yang melekat pada objek-objek itu, tetapi semua yang mereka lakukan biasanya adalah menyatukan sifat-sifat objek dengan cara ini dan itu, sekali lagi pemodelan ini sangat berpusat pada data; itu adalah Anda memodelkan data Anda untuk berinteraksi dengan berfokus pada penataan itu untuk menyediakan semua poin data Anda sehingga konsumen dapat mengubah data dengan cara ini dan itu.
FP
Perbedaan besar antara OO dan FP yang selalu menyerang saya adalah seperti yang saya katakan di atas cara Anda memodelkan data. Dalam OO seperti yang disebutkan di atas Anda memodelkan data sebagai data, dalam FP Anda memodelkan data sebagai perhitungan, ekspresi, algoritma, ini lebih tentang pemodelan aktivitas data Anda daripada fakta-fakta itu. Pikirkan tentang pemodelan data dasar dalam matematika, selalu tentang mendapatkan persamaan yang dapat menghasilkan data Anda, yang memodelkan data Anda sebagai aktivitas yang menyebabkannya, berbeda dengan OO, pemodelan akan menghasilkan cara untuk mewakili data yang Anda miliki. Itu banyak perbedaan antara FP dan OO.
Ingat, untuk banyak waktu LISP, salah satu bahasa FP dasar, hidup dengan sejumlah kecil tipe data primitif. Ini berfungsi karena pendekatannya bukan tentang pemodelan representasi kompleks data Anda, melainkan komputasi yang menghasilkan dan mengekspresikan perilaku sistem Anda.
Ketika saya mulai menulis beberapa kode dalam FP, saya mulai dengan menulis kode yang melakukan sesuatu, di mana ketika saya mulai menulis kode dalam OO, saya mulai dengan menulis model yang menggambarkan sesuatu. Melakukan hal-hal tersembunyi dalam FP dengan menjadi ekspresi, melakukan hal-hal yang diekspos dalam OO dengan dijelaskan dengan data, menyembunyikan batas data ini kata paparan.
Kembali ke pertanyaan yang ada, apa yang dikatakan FP tentang penyembunyian data, apakah ia menghargainya atau tidak setuju atau tidak?
Saya katakan itu tidak masalah, di OO data Anda adalah nyali dan bagian penting dalam program Anda yang harus disembunyikan dari campur tangan. Dalam FP, nyali dan pengetahuan sistem Anda semuanya tersembunyi dalam algoritme dan komputasi yang mengekspresikan sistem. Secara definisi ini kurang lebih tidak berubah, satu-satunya cara untuk mengubah ekspresi komputasi adalah hal-hal seperti makro, tetapi meskipun demikian, definisi mutasi Anda adalah ekspresi itu sendiri yang tidak dapat diajak campur tangan lebih jauh.
sumber
Ada sedikit paradoks di sini. Meskipun pemrograman fungsional berfokus pada, yah, fungsi, dan seringkali memiliki fungsi yang bekerja langsung pada tipe data primitif, ia cenderung memiliki lebih banyak data yang bersembunyi daripada pemrograman berorientasi objek.
Bagaimana ini? Pikirkan tentang antarmuka OO yang bagus yang menyembunyikan data yang mendasarinya - mungkin koleksi (saya mencoba untuk mengambil sesuatu yang hampir di mana-mana). Anda mungkin tidak perlu mengetahui tipe objek yang mendasari dalam koleksi atau tipe objek yang mengimplementasikan koleksi, selama Anda tahu koleksi mengimplementasikan, katakanlah, IEnumerable. Jadi, Anda memiliki data yang disembunyikan.
Dalam pemrograman fungsional, Anda dapat menulis fungsi yang bekerja secara efektif dengan antarmuka IEnumerable, tetapi beroperasi pada tipe data primitif (atau pada tipe data apa pun). Tetapi bagaimana jika tipe itu tidak pernah mengimplementasikan metode IEnumerable? Inilah kuncinya, Anda selalu dapat memiliki "metode" yang membentuk bagian yang diperlukan dari "antarmuka" menjadi parameter yang diteruskan ke fungsi Anda. Atau Anda bisa menggabungkan fungsi dengan data dan melakukan hal-hal dengan cara seperti OO.
Perhatikan bahwa Anda tidak menyembunyikan data dengan jumlah yang lebih sedikit daripada yang Anda miliki di OO. Fungsi umum saya yang bekerja pada tipe apa pun jelas tidak mengakses data dalam tipe itu - yang terjadi di dalam fungsi yang dilewatkan sebagai parameter ke fungsi umum, tetapi fungsi umum tidak pernah mengintip ke dalam fungsi tersebut untuk melihat data.
Jadi, sejauh poin 1 Anda, saya tidak berpikir FP dan artikelnya benar-benar tidak setuju. Saya tidak berpikir karakterisasi FP Anda tidak menyembunyikan data sudah benar. Orang bisa mengimplementasikan desain yang disukai penulis di FP, tentu saja.
Sejauh poin 4 (2 dan 3 tidak masuk akal untuk menjawab mengingat apa yang saya katakan untuk poin 1) itu bervariasi. Ini juga bervariasi dalam bahasa OO, dan di banyak bidang pribadi bersifat pribadi oleh konvensi daripada ditegakkan oleh bahasa.
sumber
Pertama, terima kasih atas tautannya ke artikel hebat ini, saya tidak tahu sejauh ini, dan itu memberi saya banyak masukan tentang beberapa hal yang saya diskusikan dengan beberapa perancang perangkat lunak lain dari komunitas pada tahun-tahun terakhir. Inilah pendapat saya tentang hal itu:
Desain FP sangat berfokus pada aliran data (yang menurut IMHO tidak terlalu buruk seperti yang disiratkan oleh artikel). Jika ini merupakan "ketidaksepakatan" yang lengkap, dapat diperdebatkan.
IMHO itu tidak mengkompensasi. Lihat di bawah.
Saya rasa sebagian besar pengguna atau perancang FP tidak merasakan atau berpikir dengan cara ini, lihat di bawah.
Inilah intinya - Anda mungkin telah melihat begitu banyak sistem OOP diimplementasikan dengan cara non-fungsional yang Anda yakini OOP tidak berfungsi. Dan itu salah, IMHO OOP dan FP sebagian besar konsep ortogonal, dan Anda dapat dengan sempurna membangun sistem OO fungsional, yang memberi Anda jawaban yang jelas untuk pertanyaan Anda. Implementasi "objek" klasik dalam FP dilakukan dengan memanfaatkan penutupan , dan jika Anda ingin objek digunakan dalam sistem fungsional, titik kuncinya adalah untuk mendesainnya agar tidak berubah.
Jadi untuk membuat sistem yang lebih besar, IMHO Anda dapat membuat modul, kelas dan objek menggunakan konsep OO, persis seperti yang dijelaskan di bawah "Modularisasi 2" dalam artikel tanpa meninggalkan "jalur FP". Anda akan menggunakan konsep modul bahasa FP favorit Anda, menjadikan semua objek Anda tidak berubah dan menggunakan "yang terbaik dari kedua dunia".
sumber
TL; DR : Tidak
Apakah paradigma FP dan artikel ini secara filosofis tidak setuju?
Tidak, tidak. Pemrograman Fungsional adalah deklaratif yang merupakan "gaya membangun struktur dan elemen program komputer, yang mengekspresikan logika perhitungan tanpa menjelaskan aliran kontrolnya." Ini kurang tentang mengikuti diagram alur dan lebih seperti membuat aturan yang membuat aliran muncul sendiri.
Pemrograman prosedural jauh lebih dekat dengan pengkodean diagram alur daripada pemrograman fungsional. Ini mengikuti bahwa transformasi yang terjadi, dan mengkodekan dari transformasi tersebut menjadi prosedur yang dieksekusi dalam urutan, persis seperti aliran dalam diagram alur yang dijelaskan.
Menyembunyikan Data
sumber