คำอธิบายที่ดีของหลักการการโต้ตอบของ Tennent คืออะไร


21

ฉันพบว่าตัวเองกำลังดิ้นรนเพื่อดูว่าหลักการนี้เกี่ยวกับอะไรและทำไมจึงเป็นสิ่งสำคัญสำหรับการออกแบบภาษา

โดยพื้นฐานแล้วกล่าวว่าสำหรับทุกการแสดงออกexprในภาษาควรตรงกับโครงสร้างนี้:

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

นอกจากนี้ฉันเคยได้ยินว่าทับทิมปฏิบัติตามหลักการนี้ในขณะที่งูใหญ่ไม่ ฉันไม่เข้าใจว่าทำไมสิ่งนี้ถึงเป็นจริงหรือถ้ามันเป็นเรื่องจริง


3
ฉันไม่สามารถเห็นได้ว่าทำไมมันถึงปิดหัวข้อมีคนอธิบายให้ฉันได้ไหม
แอนดรู

3
มีปัญหาอยู่สองสามข้อ ฉันได้สัมผัสแล้วและส่งไปที่โปรแกรมเมอร์ที่การอภิปรายเช่นนี้ยินดีต้อนรับอีกเล็กน้อย
Ripped Off

1
Ruby ไม่เชื่อฟังหลักการนี้: สมมติว่าexprได้รับการติดตามสแต็กปัจจุบัน
Landei

คำตอบ:


18

ฉันไม่เคยได้ยินมาก่อนเกี่ยวกับ "หลักการความสอดคล้องของ Tennent" และแม้แต่น้อยที่ความสำคัญในการออกแบบภาษา Googling การแสดงออกดูเหมือนว่าทั้งหมดจะนำไปสู่หนึ่งบล็อก Neal Gafter ของปี 2006 ที่วางสิ่งที่เขาคิดว่ามันเป็นและวิธีที่เขาคิดว่ามันควรนำไปใช้กับการปิดเช่นกัน และส่วนใหญ่คนอื่น ๆ ทั้งหมดในฟอรัมดูเหมือนจะอ้างถึงรายการของ Gafter

นี่คืออย่างไรก็ตามการกล่าวขวัญกล่าวว่า "TCP" โดยดักลาส Crockford (ชื่อฉันรู้และความไว้วางใจ): http://java.sys-con.com/node/793338/ ในส่วนของ

มีบางสิ่งที่ไม่สามารถปิดล้อมด้วยวิธีดังกล่าวได้เช่นแถลงการคืนสินค้าและใบแจ้งยอดผิดพลาดซึ่งผู้สนับสนุนการเรียกร้องหลักการทางจดหมาย (หรือ TCP) ของ Tennent นั้นเป็นอาการของกลิ่นเหม็น เฮ่ย! การออกแบบภาษานั้นยากพอแล้วโดยไม่ต้องรับมือกับอาการประสาทหลอน ดังนั้นเพื่อให้เข้าใจปัญหาได้ดีขึ้นฉันซื้อสำเนาหนังสือ Tennent's 1981 หลักการสอนภาษา

ปรากฎว่าหลักการทางจดหมายนั้นเป็นคำอธิบายไม่ได้กำหนดไว้ล่วงหน้า เขาใช้มันเพื่อวิเคราะห์ภาษาการเขียนโปรแกรมภาษา Pascal (โดยลืมไปแล้ว) ซึ่งแสดงความสอดคล้องกันระหว่างคำจำกัดความของตัวแปรและพารามิเตอร์ของโพรซีเดอร์ Tennent ไม่ได้ระบุขาดการติดต่อของงบการกลับมาเป็นปัญหา

ดังนั้นดูเหมือนว่าดังนั้นชื่อ "หลักการความสอดคล้องของ Tennent" จึงใช้ผิดและสิ่งที่โอนีลพูดถึงอาจเรียกได้ว่า "Gafter's Imagined and Possibly General TCP" ... หรือบางอย่างเช่นนั้น ไม่ว่าในกรณีใดก็ตามไม่เพียงพอที่จะซ่อนอยู่หลังม่านชื่อหนังสือที่พิมพ์ออกมา


1
+1 สำหรับ "Gafter's จินตนาการและเป็นไปได้ TCP ทั่วไป"
jcora

9

ฉันเห็นสิ่งนี้เป็นส่วนหนึ่งของกฎทั่วไปว่าภาษาที่ออกแบบมาอย่างดีทำสิ่งที่โปรแกรมเมอร์คาดหวัง หากฉันมีบล็อกของรหัสที่ฉันต้องการ refactor ในการปิดและฉันตัดบล็อกที่มีไวยากรณ์ที่เหมาะสมโดยไม่ต้องคิดเกี่ยวกับแต่ละบรรทัดของรหัสแล้วฉันคาดว่าบล็อกที่จะทำสิ่งเดียวกันในการปิดตามที่มัน ทำแบบอินไลน์ ถ้าข้อความบางคำใช้คำสำคัญ "นี่" (อาจบอกเป็นนัย) และภาษาทำให้ "นี่" ใช้ในการปิดอ้างอิงถึงคลาสนิรนามที่ใช้แทนมันมากกว่าคลาสที่กำหนดวิธีที่กำหนดการปิดดังนั้นความหมายของ ข้อความเหล่านั้นเปลี่ยนไปบล็อกของฉันไม่ได้ทำในสิ่งที่ฉันคิดอีกต่อไปและฉันต้องติดตามข้อผิดพลาดและหาวิธีเปลี่ยนรหัสของฉันให้ทำงานในช่วงปิด

ปัญหายังสามารถบรรเทาลงได้ด้วย IDE ด้วยเครื่องมือการรีแฟคเตอร์อัจฉริยะซึ่งสามารถแยกการปิดตรวจจับปัญหาที่อาจเกิดขึ้นและแม้แต่ปรับรหัสที่แยกออกมาโดยอัตโนมัติเพื่อแก้ปัญหา


3

Claus Reinke: เกี่ยวกับ "การออกแบบภาษาตามหลักการความหมายของ Tennent"
ให้การตีความที่น่าสนใจของหลักการ:
"การโต้ตอบคือหลักการที่ทำให้เราสามารถพูดได้ว่า

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

และ

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

ควรจะเท่ากันและสิ่งที่เราสามารถทำได้ในรายการพารามิเตอร์ที่เป็นทางการเราก็ควรจะสามารถทำได้ในการประกาศและในทางกลับกัน "[ดูเพิ่มเติม Reinke ด้านล่าง]

RD Tennent: วิธีการออกแบบภาษาตามหลักการความหมาย
"วิธีการออกแบบสองภาษาบนพื้นฐานของหลักการที่ได้มาจากวิธีการ denotational เพื่อความหมายภาษาการเขียนโปรแกรมมีการอธิบายและแสดงโดยการประยุกต์ใช้กับภาษา Pascal. หลักการคือประการแรก กลไกการประกาศและประการที่สองหลักการของนามธรรมสำหรับภาษาการเขียนโปรแกรมดัดแปลงมาจากทฤษฎีเซตการขยายและการวางนัยทั่วไปของ Pascal ที่เป็นประโยชน์หลายประการโดยการใช้หลักการเหล่านี้รวมถึงการแก้ปัญหาพารามิเตอร์อาเรย์

Claus Reinke: "ในการเขียนโปรแกรมการทำงานการออกแบบภาษาและการคงอยู่" ใน Haskell


ซานตาคลอส Reinke: "ในการเขียนโปรแกรมการทำงาน, การออกแบบภาษาและความเพียร" ใน Haskell ที่community.haskell.org/~claus/publications/fpldp.html
คริส

2

เพื่อตอบคำถามว่าทำไม CP ของ Tennent จึงมีความสำคัญต่อการออกแบบภาษาฉันต้องการอ้างอิง Neal Gafter :

หลักการของ Tennent นั้นมีพลังมากเพราะการละเมิดนั้นมักจะปรากฏในภาษาเช่นข้อบกพร่องความผิดปกติข้อ จำกัด ที่ไม่จำเป็นการโต้ตอบหรือภาวะแทรกซ้อนที่ไม่คาดคิดเป็นต้น

การละเมิดใด ๆ ของ TCP มีแนวโน้มที่จะทำร้ายโปรแกรมเมอร์บางคนในอนาคตเมื่อเขาคาดว่าการปิดการทำงานเช่นรหัสไม่ปิด แต่พบว่าในการละเมิด TCP พวกเขาไม่ได้


1

RE Python ไม่ปฏิบัติตามหลักการนี้ โดยทั่วไปจะปฏิบัติตามหลักการ ตัวอย่างพื้นฐาน:

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

อย่างไรก็ตาม Python กำหนดนิพจน์และข้อความสั่งแยกกัน เนื่องจากifกิ่งก้านwhileลูปการมอบหมายการทำลายล้างและข้อความอื่น ๆ ไม่สามารถใช้ในlambdaการแสดงออกได้เลยจดหมายของหลักการ Tennent จึงไม่สามารถใช้กับพวกเขาได้ ถึงกระนั้นการ จำกัด ตัวเองให้ใช้เพียงการแสดงออกของงูหลามยังคงสร้างระบบทัวริงที่สมบูรณ์ ดังนั้นฉันไม่เห็นสิ่งนี้เป็นการละเมิดหลักการ; หรือหากเป็นการละเมิดหลักการก็ไม่มีภาษาใดที่กำหนดข้อความและสำนวนแยกกันอาจเป็นไปตามหลักการ

นอกจากนี้หากเนื้อความของlambdaนิพจน์ถูกดักจับการติดตามสแต็กหรือทำวิปัสสนาอื่น ๆ ใน VM ซึ่งอาจทำให้เกิดความแตกต่าง แต่ในความคิดของฉันนี้ไม่ควรนับเป็นการละเมิด ถ้าexprและ(lambda: expr)() จำเป็นต้องคอมไพล์ด้วยรหัสไบต์เดียวกันหลักการก็เกี่ยวข้องกับคอมไพเลอร์ไม่ใช่ซีแมนติก แต่ถ้าพวกเขาสามารถคอมไพล์โค้ดไบต์ที่แตกต่างกันได้เราไม่ควรคาดหวังว่าสถานะ VM จะเหมือนกันในแต่ละกรณี

ความประหลาดใจสามารถพบได้โดยใช้ไวยากรณ์ความเข้าใจแม้ว่าฉันเชื่อว่านี่ไม่ใช่การละเมิดหลักการ Tennent เช่นกัน ตัวอย่าง:

>>> [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]

ความประหลาดใจเป็นผลมาจากวิธีการกำหนดรายการความเข้าใจ ความเข้าใจ 'ประหลาดใจ' ข้างต้นเทียบเท่ากับรหัสนี้:

>>> 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]

เห็นด้วยวิธีนี้ความเข้าใจ 'ประหลาดใจ' ข้างต้นไม่น่าแปลกใจและไม่เป็นการละเมิดหลักการ Tennent

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.