Bagaimana Anda mendesain program dalam Haskell atau bahasa pemrograman fungsional lainnya?

52

Saya memiliki beberapa pengalaman dalam bahasa pemrograman berorientasi objek seperti c # atau ruby. Saya tahu bagaimana merancang suatu program dengan gaya berorientasi objek, cara membuat kelas dan objek, dan bagaimana mendefinisikan hubungan di antara mereka. Saya juga tahu beberapa pola desain.

Bagaimana orang menulis program fungsional? Bagaimana mereka memulai? Apakah ada pola desain untuk bahasa fungsional? Apakah metodologi seperti pemrograman ekstrem atau pengembangan gesit berlaku untuk bahasa fungsional?

Luke
sumber
1
pertanyaan terkait: stackoverflow.com/questions/3077866/…
HaskellElephant

Jawaban:

24

Saya menulis jawaban saya terutama dengan Haskell dalam pikiran, meskipun banyak konsep berlaku sama baiknya dengan bahasa fungsional lainnya seperti Erlang, Lisp (s), dan ML. Beberapa bahkan berlaku (sampai batas tertentu) ke Ruby, Python, Perl dan Javascript.

Bagaimana orang menulis program fungsional? Bagaimana mereka memulai?

Dengan menulis fungsi. Ketika Anda melakukan pemrograman fungsional, Anda menulis main, atau menulis fungsi pembantu. Terkadang tujuan utama Anda mungkin menulis tipe data dengan berbagai fungsi relevan yang beroperasi di atasnya.

Pemrograman fungsional sangat cocok untuk pendekatan top-down dan bottom-up. Haskell sangat menyarankan untuk menulis program Anda dalam bahasa tingkat tinggi, dan kemudian hanya mendefinisikan detail desain tingkat tinggi Anda. Lihat minimum, misalnya:

minimum    :: (Ord a) => [a] -> a
minimum xs =  foldl1 min xs

Fungsi untuk menemukan elemen terkecil dalam daftar ditulis hanya sebagai traversal atas daftar, menggunakan fungsi min untuk membandingkan setiap elemen dengan "akumulator", atau nilai minimum saat ini.

Apakah ada pola desain untuk bahasa fungsional?

Ada dua hal yang bisa disamakan dengan "pola desain", imho, fungsi tingkat tinggi dan monad . Mari kita bicara tentang yang pertama. Fungsi tingkat tinggi adalah fungsi yang mengambil fungsi lain sebagai input, atau menghasilkan fungsi sebagai output. Bahasa fungsional umumnya yang menggunakan berat map, filterdanfold(lipat sering juga disebut "mengurangi"): tiga fungsi tingkat tinggi yang sangat dasar yang menerapkan fungsi ke daftar dengan cara yang berbeda. Ini menggantikan boilerplate untuk loop dengan cara yang indah. Melewati fungsi sekitar sebagai parameter adalah anugerah yang sangat kuat untuk pemrograman; banyak "pola desain" dapat diselesaikan dengan lebih sederhana dengan menggunakan fungsi tingkat tinggi, mampu membuat Anda sendiri, dan mampu memanfaatkan pustaka standar yang kuat, yang penuh dengan fungsi yang bermanfaat.

Monad adalah topik yang "lebih menakutkan". Tapi mereka tidak terlalu menakutkan. Cara favorit saya untuk memikirkan monad adalah menganggapnya sebagai menyelubungi suatu fungsi dalam sebuah gelembung dan memberikan fungsi itu kekuatan super (yang hanya berfungsi di dalam gelembung). Saya bisa menguraikan, tetapi dunia tidak benar-benar membutuhkan analogi monad lainnya. Jadi saya akan beralih ke contoh cepat. Misalkan saya ingin menggunakan "pola desain" nondeterministik. Saya ingin menjalankan perhitungan yang sama untuk berbagai input yang berbeda secara bersamaan. Saya tidak ingin memilih hanya satu input, saya ingin memilih semuanya. Itu akan menjadi daftar monad:

allPlus2 :: [Int] -> [Int]
allPlus2 xs = do x <- xs
                 return (x + 2)

Sekarang, cara idiomatis untuk melakukan ini sebenarnya map, tetapi demi ilustrasi, dapatkah Anda melihat bagaimana daftar monad memungkinkan saya untuk menulis fungsi yang sepertinya beroperasi pada satu nilai, tetapi memberkahinya dengan kekuatan super untuk bekerja pada setiap elemen di sebuah daftar? Negara adidaya lainnya termasuk kegagalan, negara, berinteraksi dengan "dunia luar", dan eksekusi paralel. Kekuatan super ini sangat kuat, dan sebagian besar bahasa pemrograman memungkinkan fungsi dengan kekuatan super mengamuk di sekitar. Kebanyakan orang mengatakan Haskell sama sekali tidak mengizinkan kekuatan super ini, tapi sungguh, Haskell hanya mengandung mereka di monad sehingga efeknya dapat dibatasi dan diamati.

tl; dr Grokking fungsi tingkat tinggi dan monad adalah setara dengan Haskell untuk pola desain grokking. Setelah Anda mempelajari konsep-konsep Haskell ini, Anda mulai berpikir "pola desain" kebanyakan merupakan solusi murah untuk mensimulasikan kekuatan Haskell.

Apakah metodologi seperti pemrograman ekstrem atau pengembangan gesit berlaku untuk bahasa fungsional?

Saya tidak melihat apa pun yang mengikat strategi manajemen ini ke paradigma satu pemrograman. Seperti yang dikatakan phynfo, pemrograman fungsional secara praktis memaksa Anda untuk melakukan dekomposisi fungsi, memecah masalah besar menjadi submasalah, jadi tonggak-tonggak mini harus menjadi sepotong kue. Ada alat seperti QuickCheck dan Zeno untuk menguji atau bahkan membuktikan properti tentang fungsi yang Anda tulis.

Dan Burton
sumber
"Aku bisa menguraikan, tetapi dunia tidak benar-benar membutuhkan analogi monad lain." - Kami selalu membutuhkan lebih banyak analogi untuk monad dan milik Anda adalah salah satu yang terbaik yang pernah saya lihat.
Adam Gent
1
Jawaban yang bagus, tapi saya pikir diskusi Anda tentang pola desain agak menyesatkan. Meskipun Anda tidak memerlukan pola desain gaya OO / GOF di Haskell - sebenarnya konyol untuk mencobanya - pola itu sendiri hanyalah cara komunitas mengkomunikasikan solusi untuk berbagai masalah yang muncul berulang-ulang. Komunitas Haskell masih sangat muda, jadi belum ada banyak pola untuk dibicarakan, tetapi jika Anda bertanya kepada saya untuk contoh pola Haskell, saya akan menyebutkan hal-hal seperti GADT atau Arrowized FRP.
rtperson