Apa penjelasan yang baik dari Prinsip Korespondensi Tennent?

21

Saya menemukan diri saya berjuang untuk melihat apa prinsip ini dan mengapa begitu penting untuk desain bahasa.

Pada dasarnya, ini menyatakan, bahwa untuk setiap ekspresi exprdalam bahasa harus persis sama dengan konstruksi ini:

(function () { return expr; })()

Juga, saya telah mendengar bahwa Ruby mematuhi prinsip ini, sementara Python tidak. Saya tidak mengerti mengapa ini benar, atau jika itu benar sama sekali.

Andrew
sumber
3
Saya tidak bisa melihat mengapa itu di luar topik, bisakah seseorang menjelaskannya kepada saya?
Andrew
3
Ada beberapa masalah; Saya telah menyentuhnya dan mengirimkannya ke Programmer, di mana diskusi seperti ini sedikit lebih disambut.
Merobek
1
Ruby tidak mematuhi prinsip ini: Asumsikan exprmendapat jejak stack saat ini.
Landei

Jawaban:

18

Saya belum pernah mendengar tentang "Prinsip Korespondensi Tennent" dan bahkan kurang penting dalam desain bahasa. Googling ekspresi tampaknya semua mengarah ke satu blog Neal Gafter tahun 2006 yang menetapkan apa yang dia pikirkan dan bagaimana menurutnya itu harus diterapkan pada penutupan juga. Dan kebanyakan semua orang di forum tampaknya merujuk pada entri Gafter.

Namun di sini disebutkan "TCP" dari Douglas Crockford (nama yang saya tahu dan percayai): http://java.sys-con.com/node/793338/ . Dalam bagian

Ada beberapa hal yang tidak dapat ditutup dengan cara itu, seperti pernyataan pengembalian dan pernyataan istirahat, yang diklaim oleh pendukung Tennent's Correspondence Principle (atau TCP) adalah gejala dari bau yang tidak sedap. Yow! Desain bahasa sudah cukup sulit tanpa harus berhalusinasi dengan penciuman. Jadi untuk lebih memahami masalahnya, saya membeli buku Tennent 1981, Principles of Programming Languages.

Ternyata Prinsip Korespondensi bersifat deskriptif , bukan preskriptif . Dia menggunakannya untuk menganalisis bahasa pemrograman Pascal (sekarang terlupakan), menunjukkan korespondensi antara definisi variabel dan parameter prosedur. Tennent tidak mengidentifikasi kurangnya korespondensi dari pernyataan pengembalian sebagai masalah .

Karenanya tampaknya nama "Prinsip Korespondensi Tennent" salah digunakan, dan apa pun yang dibicarakan Neal mungkin harus disebut "Gafter's Imagined and Possiblely Generalized TCP" ... atau semacamnya. Dalam hal apa pun tidak cukup untuk bersembunyi di balik tirai nama buku yang tidak dicetak

Nas Banov
sumber
1
+1 untuk "TCP Gosed's Imagined dan Posalized Generalized"
jcora
9

Saya melihat ini sebagai bagian dari aturan umum bahwa bahasa yang dirancang dengan baik melakukan apa yang diharapkan oleh seorang programmer. Jika saya memiliki blok kode yang ingin saya refactor menjadi penutupan, dan saya membungkus blok itu dengan sintaks yang tepat tanpa benar-benar memikirkan baris kode individu, maka saya berharap bahwa blok melakukan hal yang sama di penutupan seperti itu lakukan inline. Jika beberapa pernyataan menggunakan kata kunci "ini" (mungkin secara implisit) dan bahasanya membuat "ini" yang digunakan di dalam penutupan merujuk ke kelas anonim yang digunakan untuk mewakilinya daripada kelas yang mendefinisikan metode yang menentukan penutupan, maka arti dari pernyataan-pernyataan itu telah berubah, blok kode saya tidak lagi melakukan apa yang saya pikirkan, dan saya harus melacak bug dan mencari tahu bagaimana mengubah kode saya untuk bekerja di penutupan.

Masalahnya juga dapat dikurangi dengan IDE dengan alat refactoring pintar, yang dapat mengekstrak penutupan, mendeteksi potensi masalah, dan bahkan secara otomatis menyesuaikan kode yang diekstraksi untuk menyelesaikan masalah.

JGWeissman
sumber
3

Claus Reinke: mengenai Tennent's "Desain Bahasa berdasarkan Prinsip Semantik"
Memberikan interpretasi yang menarik dari prinsip-prinsip:
"Korespondensi adalah prinsip yang memungkinkan kita mengatakan bahwa

let(this=obj, x=5) { .. }  

dan

((function(x) { .. }).call(obj,5))  

harus setara, dan bahwa apa pun yang dapat kita lakukan dalam daftar parameter formal, kita juga harus dapat melakukannya dalam deklarasi, dan sebaliknya. "[lihat juga, Reinke, di bawah.]

RD Tennent: Metode desain bahasa berdasarkan prinsip semantik
"Dua metode desain bahasa berdasarkan prinsip yang diturunkan dari pendekatan denotasional ke bahasa pemrograman semantik dijelaskan dan diilustrasikan oleh aplikasi ke bahasa Pascal. Prinsip-prinsipnya adalah, pertama, korespondensi antara parametrik dan mekanisme deklaratif, dan kedua, prinsip abstraksi untuk bahasa pemrograman yang diadaptasi dari teori himpunan. Beberapa ekstensi yang berguna dan generalisasi Pascal muncul dengan menerapkan prinsip-prinsip ini, termasuk solusi untuk masalah parameter array, dan fasilitas modularisasi. "

Claus Reinke: "Tentang pemrograman fungsional, desain bahasa, dan kegigihan" di Haskell

Keris
sumber
Claus Reinke: "Tentang pemrograman fungsional, desain bahasa, dan kegigihan" di Haskell di community.haskell.org/~claus/publications/fpldp.html
Kris
2

Untuk menjawab pertanyaan mengapa Tennent's CP sangat penting untuk desain bahasa, saya ingin mengutip Neal Gafter :

Prinsip-prinsip Tennent sangat kuat karena pelanggaran terhadap mereka cenderung muncul dalam bahasa sebagai kelemahan, penyimpangan, pembatasan yang tidak perlu, interaksi yang tidak terduga atau komplikasi, dan sebagainya.

Setiap pelanggaran terhadap TCP kemungkinan akan melukai beberapa programmer di masa depan ketika ia mengharapkan penutupan bekerja seperti kode non-penutupan tetapi menemukan bahwa, dalam pelanggaran TCP, mereka tidak melakukannya.

thiton
sumber
1

RE Python tidak mengikuti prinsip ini. Secara umum, itu mengikuti prinsip. Contoh dasar:

>>> x = ['foo']
>>> x
['foo']
>>> x = (lambda: ['foo'])()
>>> x
['foo']

Namun, Python mendefinisikan ekspresi dan pernyataan secara terpisah. Karena ifcabang, whileloop, penugasan destruktif dan pernyataan lainnya tidak dapat digunakan dalam lambdaekspresi sama sekali, surat prinsip Tennent tidak berlaku untuk mereka. Meski begitu, membatasi diri untuk hanya menggunakan ekspresi Python masih menghasilkan sistem lengkap Turing. Jadi saya tidak melihat ini sebagai pelanggaran terhadap prinsip; atau lebih tepatnya, jika itu melanggar prinsip, maka tidak ada bahasa yang mendefinisikan pernyataan dan ungkapan secara terpisah yang dapat sesuai dengan prinsip tersebut.

Juga, jika tubuh lambdaekspresi menangkap jejak tumpukan, atau melakukan introspeksi lain dalam VM, itu bisa menyebabkan perbedaan. Tapi menurut saya ini seharusnya tidak dianggap sebagai pelanggaran. Jika exprdan (lambda: expr)() perlu mengkompilasi dengan bytecode yang sama, maka prinsipnya benar-benar menyangkut kompiler bukan semantik; tetapi jika mereka dapat dikompilasi ke bytecode yang berbeda, kita seharusnya tidak mengharapkan negara VM menjadi identik dalam setiap kasus.

Suatu kejutan dapat ditemui menggunakan sintaksis pemahaman, meskipun saya percaya ini bukan pelanggaran prinsip Tennent juga. Contoh:

>>> [x for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [lambda: x for x in xrange(10)]]  # surprise!
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>> # application of Tennent principle to first expression
... [(lambda: x)() for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [(lambda x: lambda: x)(x) for x in xrange(10)]]  # force-rebind x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> map(lambda f:f(), map(lambda x: lambda: x, xrange(10)))  # no issue with this form
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Yang mengejutkan adalah hasil dari bagaimana definisi daftar didefinisikan. Pemahaman 'kejutan' di atas setara dengan kode ini:

>>> result = []
>>> for x in xrange(10):
...   # the same, mutable, variable x is used each time
...   result.append(lambda: x)
... 
>>> r2 = []
>>> for f in result:
...   r2.append(f())
... 
>>> r2
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

Dilihat dengan cara ini, pemahaman 'kejutan' di atas kurang mengejutkan, dan bukan pelanggaran prinsip Tennent.

wberry
sumber