Яке хороше пояснення принципу листування Теннента?


21

Я намагався зрозуміти, в чому полягає цей принцип, і чому він такий важливий для дизайну мови.

В основному, він стверджує, що для кожного висловлення exprв мові повинно бути точно те саме, що і ця конструкція:

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

Крім того, я чув, що Рубі дотримується цього принципу, в той час як Python цього не робить. Я не розумію, чому це правда чи взагалі це правда.


3
Я не бачу, чому це поза темою, хтось може мені це пояснити?
Андрій

3
Є пара питань; Я торкнувся його і надсилаю програмістам, де подібні дискусії вітаються трохи більше.
Зірвано

1
Рубі не дотримується цього принципу: Припустимо, exprотримує поточний слід стека.
Ландей

Відповіді:


18

Я ніколи не чув про "Принцип листування Теннента", а тим більше, що він важливий у мовному дизайні. Гуглінг виразів, мабуть, веде до одного блогу Ніла Гафтера 2006 року, який визначає, що він вважає, і як він вважає, що це має стосуватися і закриття. І більшість всіх інших на форумах, схоже, посилаються на запис Гафтера.

Однак тут згадується згаданий "TCP" Дугласом Крокфордом (ім'я, яке я знаю і якому я довіряю): http://java.sys-con.com/node/793338/ . Частково

Є деякі речі, які не можна вкласти в такий спосіб, наприклад, заяви про повернення та заяви про перерви, які, на думку прихильників принципу листування Теннента (або TCP), є симптомом поганого запаху. Йоу! Дизайн мови вже досить складний, не впораючись із нюховими галюцинаціями. Отож, щоб краще зрозуміти проблему, я купив примірник книги Тенента 1981 року "Принципи мов програмування".

Виявляється, Принцип листування є описовим , а не розпорядчим . Він використовує його для аналізу (на сьогодні забутого) мови програмування Pascal, показуючи відповідність між змінними визначеннями та параметрами процедури. Теннент не визначає відсутність відповідності зворотних заяв як проблему .

Тому здається, що назва "Принцип листування Теннента" вживається неправильно, і все, про що Ніл говорить, можливо, слід називати "Уявленим Гафтером і, можливо, узагальненим TCP" ... або деяким подібним. У будь-якому випадку недостатньо для того, щоб сховатися за завісою книги, яка не надрукована


1
+1 за "Уявлені Гафтером і, можливо, узагальнені TCP"
jcora

9

Я вважаю це частиною загального правила, що добре розроблена мова робить те, що, природно, очікував програміст. Якщо у мене є блок коду, який я хочу переробити на закриття, і я загортаю цей блок із відповідним синтаксисом, не замислюючись про окремі рядки коду, тоді я очікую, що цей блок буде робити те саме, що і при закритті зробив в рядку. Якщо в деяких висловлюваннях використовується ключове слово "this" (можливо неявно), а мова робить "this", що використовується всередині закриття, відносяться до анонімного класу, який використовується для його представлення, а не до класу, який визначає метод, який визначає закриття, тоді значення значення ці заяви змінилися, мій блок коду більше не робить те, що я думаю, що це робить, і я повинен відшукати помилку і зрозуміти, як змінити код на роботу в закритті.

Проблему можна також усунути за допомогою IDE із розумними інструментами рефакторингу, які можуть витягувати закриття, виявляти потенційні проблеми та навіть автоматично коригувати витягнутий код для вирішення проблем.


3

Клаус Рейнке: стосовно "Теннента" Дизайн мови на основі семантичних принципів "
дає цікаве тлумачення принципів:
" Листування - це принцип, який дозволяє нам сказати, що

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

і

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

повинно бути рівнозначним, і що все, що ми можемо зробити у формальних списках параметрів, ми також повинні робити в деклараціях, і навпаки. "[див. також, Рейнке, нижче.]

RD Tennent: Методи дизайну мови, засновані на семантичних принципах
"Два методи дизайну мови, засновані на принципах, що випливають із денотаційного підходу до семантики мови програмування, описані та проілюстровані додатком до мови Pascal. Принципи - це, по-перше, відповідність між параметричними та декларативні механізми, по-друге, принцип абстрагування для мов програмування, адаптований із теорії множин. За допомогою цих принципів з'являється кілька корисних розширень та узагальнень Паскаля, включаючи рішення проблеми параметра масиву та засоби модуляції ".

Клаус Рейнке: "Про функціональне програмування, дизайн мови та наполегливість" на Haskell


Клаус Рейнке: "Про функціональне програмування, дизайн мови та наполегливість" на Haskell на community.haskell.org/~claus/publications/fpldp.html
Kris

2

Щоб відповісти на питання, чому CP Теннента настільки важливий для мовного дизайну, я хочу процитувати Ніла Гафтера :

Принципи Теннента дуже потужні, оскільки порушення їх проявляються в мові як недоліки, нерівності, зайві обмеження, несподівані взаємодії чи ускладнення тощо.

Будь-яке порушення TCP, швидше за все, зашкодить якомусь програмісту в майбутньому, коли він очікує, що закриття працюватиме як код незакриття, але виявить, що, порушуючи TCP, вони цього не роблять.


1

RE Python не дотримується цього принципу. Як правило, він дотримується принципу. Основний приклад:

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

Однак Python визначає вирази та оператори окремо. Оскільки ifгілки, whileпетлі, деструктивне призначення та інші висловлювання взагалі не можуть бути використані у lambdaвиразах, буква принципу Теннента не стосується їх. Тим не менш, обмеження себе використовувати лише вирази Python все ще створює повну систему Тьюрінга. Тож я не вважаю це порушенням принципу; точніше, якщо це порушує принцип, то жодна мова, яка окремо визначає висловлювання та вирази, не може відповідати цьому принципу.

Крім того, якщо тіло lambdaвиразу фіксувало слід стека або робило інший самоаналіз у ВМ, це може спричинити відмінності. Але, на мою думку, це не слід вважати порушенням. Якщо exprі (lambda: expr)() обов'язково компілювати в один і той же байт-код, то принцип дійсно стосується компіляторів, а не семантики; але якщо вони можуть компілювати різні байт-коди, ми не повинні очікувати, що стан VM буде однаковим у кожному випадку.

Сюрприз можна зустріти, використовуючи синтаксис розуміння, хоча я вважаю, що це також не є порушенням принципу Теннента. Приклад:

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

Побачене таким чином, розуміння "сюрпризу" вище є менш дивним, а не порушенням принципу Теннента.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.