Mengapa komunitas Lisp lebih suka mengakumulasi semua tanda kurung di akhir fungsi?

26

Mengapa komunitas Lisp lebih suka mengakumulasi semua tanda kurung di akhir fungsi:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

Mengapa tidak menggunakan konvensi seperti C atau Java?
Baiklah, Lisp jauh lebih tua daripada bahasa-bahasa itu, tapi saya berbicara tentang Lispers kontemporer.

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Catatan: Cuplikan kode dari buku "The Joy of Clojure".

Chiron
sumber
13
Saya diberitahu bahwa pada masa kartu punch, adalah praktik umum untuk mengakhiri program Lisp dengan kartu tambahan dengan 80 tanda kurung tepat di atasnya, hanya untuk memastikan ada cukup untuk mencocokkan semua tanda kurung terbuka dalam program .
Alger
2
Wow, saya menyukainya. Saya selalu membenci tanda kurung, tetapi contoh kedua Anda terlihat menyenangkan bagi saya. Tidak sebagus Python, tetapi peningkatan.
Eric Wilson
5
karena mereka bisa?
Muad'Dib
Alangkah baiknya jika dialek Clojure mempromosikan lekukan menjadi struktur kode. Itu akan menghapus kurung sama sekali (seperti F #, dll.). Tentu saja, tanda kurung akan tetap legal, jika diinginkan.
intrepidis

Jawaban:

28

Salah satu alasan bahasa berbasis Algol mendorong kawat gigi pada jalurnya sendiri adalah untuk mendorong penambahan lebih banyak garis di antara kawat gigi pembatas tanpa harus memindahkan kawat gigi. Yaitu, jika seseorang mulai dengan

if (pred)
{
  printf("yes");
}

mudah untuk datang dan menambahkan pernyataan lain di dalam kurung:

if (pred)
{
  printf("yes");
  ++yes_votes;
}

Apakah bentuk aslinya telah

if (pred)
{ printf("yes"); }

maka kita harus "memindahkan" dua kawat gigi, tetapi contoh saya lebih mementingkan yang kedua. Di sini, kawat gigi membatasi apa yang dimaksudkan sebagai urutan pernyataan , sebagian besar digunakan untuk efek samping.

Sebaliknya, Lisp tidak memiliki pernyataan; setiap bentuk adalah ekspresi , menghasilkan beberapa nilai — bahkan jika dalam beberapa kasus yang jarang (memikirkan Common Lisp), nilai itu sengaja dipilih sebagai "tanpa nilai" melalui (values)bentuk kosong . Itu kurang umum untuk menemukan urutan ekspresi , sebagai lawan dari ekspresi bersarang . Keinginan untuk "membuka urutan langkah-langkah sampai pembatas penutup" tidak muncul sesering, karena ketika pernyataan pergi dan mengembalikan nilai menjadi mata uang yang lebih umum, lebih jarang mengabaikan nilai balik ekspresi, dan karenanya lebih jarang mengevaluasi serangkaian ekspresi untuk efek samping saja

Dalam Common Lisp, prognformulir adalah pengecualian (seperti juga saudara kandungnya):

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

Di sini, prognevaluasi tiga ekspresi secara berurutan, tetapi buang nilai pengembalian dari dua yang pertama. Anda dapat membayangkan menulis kurung penutup terakhir pada barisnya sendiri, tetapi perhatikan lagi bahwa karena bentuk terakhir spesial di sini (bukan dalam arti Lisp umum menjadi istimewa ), dengan perlakuan berbeda, kemungkinan besar seseorang akan menambahkan baru ekspresi di tengah urutan, bukan hanya "menambahkan satu lagi di akhir," karena penelepon kemudian akan terkena dampak tidak hanya oleh efek samping baru tetapi lebih karena perubahan kemungkinan nilai pengembalian.

Dengan menyederhanakan, tanda kurung di sebagian besar program Lisp membatasi argumen yang diteruskan ke fungsi — seperti dalam bahasa mirip C — dan tidak membatasi blok pernyataan. Untuk alasan yang sama kita cenderung menjaga tanda kurung membatasi panggilan fungsi dalam C tutup di sekitar argumen, jadi kita juga melakukan hal yang sama dalam Lisp, dengan sedikit motivasi untuk menyimpang dari pengelompokan yang dekat itu.

Penutupan tanda kurung jauh lebih sedikit impor daripada lekukan bentuk tempat mereka membuka. Pada waktunya, orang belajar untuk mengabaikan tanda kurung dan menulis dan membaca dengan bentuk — seperti yang dilakukan programmer Python. Namun, jangan biarkan analogi itu membuat Anda berpikir bahwa menghilangkan tanda kurung sepenuhnya akan bermanfaat. Tidak, itu perdebatan terbaik yang bisa diselamatkan comp.lang.lisp.

seh
sumber
2
Saya pikir menambahkan pada akhirnya tidak terlalu jarang, misalnya (let ((var1 expr1) more-bindings-to-be-added) ...), atau(list element1 more-elements-to-be-added)
Alexey
14

Karena itu tidak membantu. Kami menggunakan lekukan untuk menunjukkan struktur kode. Jika kita ingin memisahkan blok kode, kita menggunakan baris yang benar-benar kosong.

Karena sintaksis Lisp sangat konsisten, tanda kurung adalah panduan definitif untuk lekukan untuk programmer dan editor.

(Bagi saya, pertanyaannya adalah mengapa programmer C dan Java suka membuang-buang kawat gigi mereka.)

Hanya untuk menunjukkan, dengan asumsi bahwa operator ini tersedia dalam bahasa seperti-C:

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

Dua kawat gigi penutup menutup dua tingkat sarang. Sintaksnya jelas benar. Dalam analogi sintaksis Python, kurung kurawal hanyalah token INDENT dan DEDENT.

Tentu saja, ini mungkin bukan TM "one brace style sejati" , tapi saya percaya itu hanya kecelakaan dan kebiasaan bersejarah.

Svante
sumber
2
Juga, hampir semua cadel editor menandai kurung yang cocok saat menutup (beberapa juga yang benar )untuk ]atau sebaliknya), sehingga Anda tahu Anda menutup jumlah yang tepat bahkan jika Anda tidak memeriksa tingkat lekukan.
configurator
6
WRT "pertanyaan", karena "melempar" token penutup dengan gaya contoh kedua memungkinkan Anda dengan mudah menyejajarkannya dengan mata Anda dan melihat apa yang menutup apa, bahkan jika Anda hanya berada di editor teks tanpa pencocokan otomatis / fitur penyorotan.
Mason Wheeler
1
@Mason Wheeler Ya, Tepat.
Chiron
3
TBH, apa yang dikatakan oleh saya adalah bahwa parens di LISP sebagian besar berlebihan, dengan kemungkinan (seperti dengan C ++) bahwa lekukan dapat menyesatkan - jika lekukan memberi tahu Anda apa yang perlu Anda ketahui itu harus memberitahu kompiler hal yang sama juga, seperti pada Haskell dan Python. BTW - memiliki kawat gigi pada tingkat indent yang sama dengan if / switch / while / apa pun dalam C ++ membantu mencegah kasus di mana indentasi menyesatkan - pemindaian visual ke bawah LHS memberitahu Anda bahwa setiap brace terbuka dicocokkan dengan brace dekat dan lekukan itu konsisten dengan kawat gigi itu.
Steve314
1
@ Steve314: Tidak, lekukannya berlebihan. Lekukan dapat menyesatkan dengan Python dan Haskell juga (pikirkan lekukan sekali saja atau tabulars), hanya saja pengurai juga menyesatkan. Saya pikir tanda kurung adalah alat untuk penulis dan pengurai, sedangkan lekukan adalah alat untuk penulis dan pembaca (manusia). Dengan kata lain, lekukan adalah komentar, tanda kurung adalah sintaks.
Svante
11

Kode itu jauh lebih kompak. Gerakan di editor adalah dengan s-ekspresi, jadi Anda tidak perlu ruang untuk mengedit. Kode dibaca sebagian besar oleh struktur dan kalimat - bukan dengan mengikuti pembatas.

Rainer Joswig
sumber
Suatu kehormatan mendapat jawaban dari Anda :) Terima kasih.
Chiron
Editor apa yang bergerak dengan ekspresi s? Lebih khusus lagi, bagaimana saya bisa vimmelakukan itu?
hasen
2
@ HasenJ Anda mungkin perlu vimacs.vim plugin . : P
Mark C
5

Lispers, Anda tahu, penuh kebencian bletcherous karena mungkin, ada adalah tertentu ne je sais quoi untuk contoh kedua:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Pada awalnya saya tidak bisa meletakkan jari pada sumber daya pikat, jadi untuk berbicara, dan kemudian saya menyadari itu hanya kehilangan pemicu:

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

Voa!

Saya akan berlatih cengkeraman Lisp gangster saya sekarang!

Kaz
sumber
2
Ini adalah salah satu jawaban terlucu dan paling diremehkan yang pernah saya lihat di situs ini. Aku ujung topiku.
byxor
0

Dalam kasus saya, saya menemukan baris yang didedikasikan untuk pembatas buang-buang ruang layar, dan ketika Anda menulis kode di C ada juga gaya

if (pred) {
   printf("yes");
   ++yes_votes;
}

Mengapa orang meletakkan brace pembuka di baris yang sama "jika" untuk menghemat ruang, dan karena terlihat berlebihan untuk memiliki garis sendiri ketika "jika" sudah memiliki garis sendiri.

Anda mencapai hasil yang sama dengan mengumpulkan tanda kurung di akhir. Di C akan terlihat aneh karena pernyataan diakhiri dengan titik koma dan pasangan kurung tidak terbuka seperti ini

{if (pred)
    printf("yes");
}

itu seperti kurung kurawal di ujung, tampak tidak pada tempatnya. terbuka seperti ini

if (pred) {
    printf("yes");
}

memberi Anda pandangan yang jelas tentang blok dan batasannya oleh '{' & '}'

Dan dengan bantuan editor yang cocok dengan tanda kurung dengan menyoroti mereka seperti vim, Anda dapat pergi ke akhir di mana semua tanda kurung dikemas dan bergerak melalui mereka dengan mudah dan mencocokkan semua parens pembuka dan melihat bentuk pelat bersarang

(defn defer-expensive [cheap expensive]
    _(if-let [good-enough (force cheap)]
    good-enough
    (force expensive)_))

Anda bisa meletakkan kursor di paren penutup di tengah dan memiliki paren pembuka jika-biarkan disorot.

kisai
sumber