Mengapa beberapa bahasa pemrograman fungsional menggunakan ruang untuk aplikasi fungsi?

34

Setelah melihat beberapa bahasa untuk pemrograman fungsional, saya selalu bertanya-tanya mengapa beberapa bahasa fp menggunakan satu atau lebih karakter spasi putih untuk aplikasi fungsi (dan definisi), sedangkan sebagian besar (semua?) Bahasa yang berorientasi / objek berorientasi menggunakan tanda kurung, yang tampaknya menjadi cara yang lebih matematis. Saya juga berpikir bahwa gaya yang terakhir jauh lebih jelas dan mudah dibaca daripada tanpa parens.

Jadi jika kita memiliki fungsi f (x) = x² ada dua alternatif untuk memanggilnya:

  • FP: f x

    Contoh:

    • ML, Ocaml, F #
    • Haskell
    • LISP, Skema (entah bagaimana)
  • Non-FP: f(x)

    Contoh:

    • Hampir semua bahasa imperatif (saya tahu, lihat komentar / jawaban)
    • Erlang
    • Scala (juga memungkinkan "notasi operator" untuk argumen tunggal)

Apa alasan untuk "meninggalkan" tanda kurung?

pvorb
sumber
9
Untuk membuatnya lebih membingungkan, erm, maksudku ringkas .
Den
11
@Den jika Anda mempelajari haskell, sintaksnya akan menjadi salah satu hal yang paling membingungkan: p
Simon Bergot
5
Anda menyadari ironi mengatakan bahwa menggunakan tanda kurung akan lebih matematis dan kemudian menggunakan fungsi eksponensial sebagai contoh?
Jörg W Mittag
12
@ Apakah Anda merugikan diri sendiri dengan menolak bahasa karena sintaksisnya. Tentunya Anda tidak kesulitan mempelajari xml atau sql (itu bukan bahasa tujuan umum, tetapi mereka mendefinisikan sintaks mereka sendiri).
Simon Bergot
7
Saya ingin menunjukkan bahwa skrip shell (misalnya bash) umumnya tidak menggunakan parameter untuk memanggil perintah. Jenis PowerShell memiliki yang terburuk dari kedua dunia dalam fungsi yang dideklarasikan dengan tanda kurung dan dipanggil tanpa mereka.
Kris Harper

Jawaban:

59

yang tampaknya menjadi cara yang lebih matematis

bahasa fungsional diinspirasi oleh lambda calculus . Dalam bidang ini, tanda kurung tidak digunakan untuk aplikasi fungsi.

Saya juga berpikir bahwa gaya yang terakhir jauh lebih jelas dan mudah dibaca daripada tanpa parens.

Keterbacaan ada di mata yang melihatnya. Anda tidak terbiasa membacanya. Ini agak seperti operator matematika. Jika Anda memahami asosiatifnya, Anda hanya perlu beberapa orangtua untuk memperjelas struktur ekspresi Anda. Seringkali Anda tidak membutuhkannya.

Kari juga merupakan alasan yang baik untuk menggunakan konvensi ini. Di haskell, Anda dapat menentukan yang berikut:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

Dengan parens, Anda dapat menggunakan dua konvensi: f(5, 6)(tidak kari) atau f(5)(6)(kari). Sintaks haskell membantu membiasakan diri dengan konsep currying. Anda masih dapat menggunakan versi non-kari, tetapi lebih menyakitkan untuk menggunakannya dengan kombinator

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

Perhatikan bagaimana versi kedua memaksa Anda untuk mendaftarkan x sebagai variabel, dan bahwa subekspresi adalah fungsi yang mengambil bilangan bulat dan menambahkan 5 ke dalamnya? Versi kari jauh lebih ringan, tetapi juga dianggap oleh banyak orang sebagai lebih mudah dibaca.

Program Haskell menggunakan ekstensif aplikasi parsial dan kombinator sebagai sarana untuk mendefinisikan dan menyusun abstraksi, jadi ini bukan contoh mainan. Antarmuka fungsi yang baik akan menjadi tempat di mana urutan parameter menyediakan penggunaan kari yang ramah.

Poin lain: fungsi tanpa parameter harus dipanggil dengan f(). Dalam haskell, karena Anda hanya memanipulasi nilai yang dievaluasi malas malas, Anda hanya menulis f, dan menganggapnya sebagai nilai yang akan perlu melakukan beberapa perhitungan ketika diperlukan. Karena evaluasinya tidak akan memiliki efek samping, tidak masuk akal untuk memiliki notasi yang berbeda untuk fungsi tanpa parameter dan nilai yang dikembalikan.

Ada juga konvensi lain untuk aplikasi fungsi:

  • Lisp: (fx) - awalan dengan tanda kurung eksternal
  • Keempat: xf - postfix
Simon Bergot
sumber
7
Minor nitpick: istilahnya adalah "currying" dan "curried", bukan "currification" dan "currified".
Doval
"non curried" apa fungsi non-curried di haskell?
Ven
3
@ user1737909 Fungsi yang menggunakan tuple sebagai argumen.
Doval
1
@Doval Sebenarnya itu harus "schönfinkeling" dan "schönfinkeled". Tapi oh well, sejarah selalu mendefinisikan pemenang secara retrospektif.
Profpatsch
Berpikir tentang sintaks currying kurung, sesuatu seperti f(a, ,b)atau add(a, )atau add(, b)akan tampak lebih fleksibel secara default.
phresnel
25

Ide dasarnya adalah membuat operasi yang paling penting (aplikasi fungsi) paling mudah dibaca dan termudah untuk ditulis. Sebuah ruang sangat tidak mengganggu untuk dibaca dan sangat mudah untuk diketik.

Perhatikan bahwa ini tidak khusus untuk bahasa fungsional, misalnya dalam Smalltalk , salah satu bahasa OO pertama dan bahasa yang terinspirasi olehnya ( Self , Newspeak , Objective-C), tetapi juga dalam Io dan bahasa yang terinspirasi olehnya (Ioke, Seph), spasi digunakan untuk pemanggilan metode.

Smalltalk-style:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(Dalam kasus terakhir, nama metodenya adalah replace:with:.)

Gaya Io:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala juga memungkinkan spasi putih untuk pemanggilan metode:

foo bar(baz)

Dan meninggalkan tanda kurung jika hanya ada satu argumen:

foo bar baz

Ruby juga memungkinkan Anda meninggalkan tanda kurung:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

menggunakan tanda kurung, yang tampaknya menjadi cara yang lebih matematis

Tidak juga:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

Notasi matematika telah berkembang sejak lama dengan cara yang sangat tidak konsisten.

Jika sama sekali, bahasa fungsional mengambil inspirasi mereka dari kalkulus λ di mana aplikasi fungsi ditulis menggunakan spasi.

Jörg W Mittag
sumber
3
Ada juga argumen untuk ekonomi pengeditan. Secara khusus, bahasa fungsional biasanya mendukung komposisi secara langsung. Menempatkan tanda kurung di sekitar fungsi alih-alih argumen masuk akal. Anda hanya perlu melakukannya "sekali": (e. F. G) x sebagai lawan dari e (f (g (x)))). Juga, Haskell, setidaknya, tidak akan menghentikan seseorang dari melakukan f (x), bukan f x. Itu tidak idiomatis.
Nomen
18

Parenthesis untuk aplikasi fungsi hanyalah salah satu dari banyak pelana yang ditinggalkan Euler. Seperti hal lain, matematika membutuhkan konvensi ketika ada beberapa cara untuk melakukan sesuatu. Jika pendidikan matematika Anda hanya meluas sejauh mata pelajaran non-matematika di universitas maka Anda mungkin tidak terlalu terbiasa dengan banyak bidang di mana aplikasi fungsi terjadi dengan bahagia tanpa tanda kurung yang tidak masuk akal ini (misalnya Geometri Diferensial, Aljabar Abstrak). Dan Anda bahkan tidak menyebutkan fungsi yang diterapkan infiks (hampir semua bahasa), "perbaikan" seperti mengambil norma, atau secara diagram (Befunge, Topologi Aljabar).

Jadi sebagai jawaban saya akan mengatakan itu karena proporsi yang lebih tinggi dari programmer fungsional dan desainer bahasa yang memiliki pendidikan matematika yang luas. Von Neumann berkata, “Anak muda, dalam matematika Anda tidak mengerti banyak hal. Anda hanya terbiasa dengan mereka.”, Tentu saja ini berlaku untuk notasi.

Sean D
sumber
7
Jangan bahkan membuat saya memulai f(x)vs sin xvs vs |x|vs x!vs x + yvs xyvs ½penjumlahan vs integral vs ...
Jörg W Mittag
2
+1 untuk kutipan von Neuman. +1 untuk sisa jawaban, tapi saya tidak bisa memberi +2. :(
pvorb
2
Saya menangkap sedikit elitisme di sini; Saya membantah netralitas "desainer bahasa pemrograman fungsional lebih berpendidikan" .
CaptainCodeman
7
@ KaptenCodeman Dia mengatakan lebih terdidik dalam matematika, pernyataan yang saya setujui Paling tidak dalam hal Haskell, Anda dapat memperhatikan bahwa kedua konsep tersebut lebih bersifat matematis dan komunitasnya juga cenderung lebih matematis. Mereka tidak lebih pintar, hanya lebih terdidik dalam matematika.
Paul
5
@CaptainCodeman Tidak, karena ia tidak pernah menyiratkan bahwa mereka lebih berpendidikan secara umum, hanya lebih terdidik dalam matematika. Itulah tepatnya yang dia katakan: "pendidikan matematika yang luas". :)
Paul
8

Meskipun ada banyak kebenaran dalam jawaban Simon, saya pikir ada juga alasan yang jauh lebih praktis. Sifat pemrograman fungsional cenderung menghasilkan kurung lebih banyak daripada pemrograman imperatif, karena fungsi chaining dan komposisi. Pola-pola rantai dan komposisi itu juga biasanya dapat direpresentasikan tanpa tanda kurung.

Intinya adalah, semua tanda kurung itu mengganggu untuk dibaca dan dilacak. Itu mungkin alasan nomor satu LISP tidak lebih populer. Jadi jika Anda bisa mendapatkan kekuatan pemrograman fungsional tanpa mengganggu tanda kurung, saya pikir perancang bahasa akan cenderung seperti itu. Lagi pula, perancang bahasa juga pengguna bahasa.

Bahkan Scala memungkinkan programmer untuk menghilangkan tanda kurung dalam keadaan tertentu, yang kebetulan muncul cukup sering ketika pemrograman dalam gaya fungsional, sehingga Anda mendapatkan semacam yang terbaik dari kedua dunia. Sebagai contoh:

val message = line split "," map (_.toByte)

Paren pada akhirnya diperlukan untuk asosiatif, dan yang lainnya ditinggalkan (serta titik-titik). Menulisnya dengan cara ini menekankan sifat dirantai dari operasi yang Anda lakukan. Mungkin tidak akrab dengan programmer imperatif, tetapi untuk programmer fungsional rasanya sangat alami dan mengalir untuk menulis dengan cara ini, tanpa harus berhenti dan memasukkan sintaks yang tidak menambah arti pada program, tetapi hanya ada di sana untuk membuat kompiler bahagia .

Karl Bielefeldt
sumber
2
"Intinya adalah, semua tanda kurung itu menjengkelkan untuk dibaca dan dilacak. Mungkin itu adalah alasan nomor satu LISP tidak lebih populer.": Saya tidak berpikir tanda kurung adalah alasan untuk Lisp untuk tidak menjadi populer: Saya tidak berpikir mereka sangat sulit dibaca, dan saya menemukan bahasa lain memiliki sintaks yang jauh lebih canggung.
Giorgio
2
Sintaksis LISP sebagian besar menggantikan kurung yang menjengkelkan dengan tingkat orthogonalitasnya yang sangat tinggi. Itu hanya berarti ia memiliki fitur penukaran lainnya yang membuatnya masih dapat digunakan, bukan karena orangtua tidak mengganggu. Saya mengerti tidak semua orang merasa seperti itu, tetapi sebagian besar programmer yang saya temui melakukannya.
Karl Bielefeldt
"Lost In Stupid Parentheses" adalah backronym LISP favorit saya. Komputer juga dapat menggunakan cara manusiawi untuk menentukan blok kode dan menjatuhkan redundansi, seperti dalam bahasa seperti Wisp .
Cees Timmerman
4

Kedua premis itu salah.

  • Bahasa fungsional ini tidak menggunakan ruang untuk aplikasi fungsi. Apa yang mereka lakukan hanyalah mengurai ekspresi apa pun yang muncul setelah fungsi sebagai argumen.

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    Tentu saja jika Anda tidak menggunakan spasi atau paren maka itu biasanya tidak akan berfungsi, hanya karena ekspresi tidak terpatok dengan benar

    GHCi> f3

    <‌interactive>: 7: 1:
        Tidak dalam cakupan: 'f3'
        Mungkin maksudmu 'f' (baris 2)

    Tetapi jika bahasa-bahasa ini dibatasi untuk nama variabel 1-karakter maka itu akan berfungsi juga.

  • Konvensi matematika juga tidak memerlukan tanda kurung untuk aplikasi fungsi. Matematikawan tidak hanya sering menulis sin x - untuk fungsi linear (biasanya disebut operator linier), tetapi juga sangat lazim untuk menulis argumen tanpa parens, mungkin karena (seperti halnya fungsi apa pun dalam pemrograman fungsional) fungsi linear sering ditangani tanpa secara langsung memasok sebuah argumen.

Jadi sungguh, pertanyaan Anda bermuara pada: mengapa untuk beberapa bahasa memerlukan tanda kurung untuk aplikasi fungsi? Nah, tampaknya beberapa orang, seperti Anda, menganggap ini lebih mudah dibaca. Saya sangat tidak setuju dengan itu: sepasang orangtua baik-baik saja, tetapi segera setelah Anda memiliki lebih dari dua dari mereka bersarang mulai menjadi membingungkan. Kode Lisp menunjukkan ini yang terbaik, dan hampir semua bahasa fungsional akan terlihat sama berantakan jika Anda membutuhkan parens di mana-mana. Ini tidak terjadi begitu banyak dalam bahasa imperatif, tetapi terutama karena bahasa-bahasa ini tidak cukup ekspresif untuk menulis singkat, jelas satu-baris di tempat pertama.

leftaroundabout
sumber
Paham, pertanyaannya tidak sempurna. :) Jadi kesimpulan untuk menggambar adalah: "Kurung tidak bersisik."
pvorb
2
Juga tidak ada bahasa non-fungsional yang dengan suara bulat membutuhkan tanda kurung; untuk beberapa contoh, Smalltalk memiliki object method: args, Objective C memiliki [object method: args], dan Ruby dan Perl keduanya memungkinkan menghilangkan tanda kurung dalam panggilan fungsi dan metode, hanya mengharuskan mereka untuk menyelesaikan ambiguitas.
hobbs
1

Klarifikasi sejarah kecil: notasi asli dalam matematika adalah tanpa tanda kurung !

Notasi fx untuk fungsi arbitrer x diperkenalkan oleh Johan Bernoulli sekitar 1700 tak lama setelah Leibniz mulai berbicara tentang fungsi (sebenarnya Bernoulli menulis ϕ x ). Tetapi sebelum itu banyak orang yang sudah menggunakan notasi seperti sin x atau lx (logaritma x ) untuk fungsi spesifik x , tanpa tanda kurung. Saya menduga itulah alasan banyak orang saat ini masih menulis dosa x bukannya dosa (x) .

Euler, yang adalah seorang mahasiswa dari Bernoulli, mengadopsi Bernoulli φ x dan berubah Yunani menjadi surat latin, menulis fx . Kemudian dia memperhatikan bahwa mungkin mudah untuk membingungkan f untuk "kuantitas" dan percaya bahwa fx adalah produk, jadi dia mulai menulis f: x . Karenanya notasi f (x) bukan karena Euler. Tentu saja Euler membutuhkan tanda kurung untuk alasan yang sama kita masih membutuhkan tanda kurung dalam bahasa pemrograman fungsional: untuk membedakan misalnya (fx) + y dari f (x + y) . Saya menduga bahwa orang-orang setelah Euler mengadopsi tanda kurung sebagai default karena mereka terutama melihat Euler menulis ekspresi majemuk seperti f (ax + b). Dia jarang menulis hanya fx .

Lebih lanjut tentang ini lihat: Apakah Euler pernah menulis f (x) dengan tanda kurung? .

Saya tidak tahu apakah perancang bahasa pemrograman fungsional atau penemu kalkulus lambda sadar akan akar sejarah dari notasi mereka. Secara pribadi saya menemukan notasi tanpa tanda kurung lebih alami.

Michael Bächtold
sumber