Почему у каждого есть двоеточие вместо «в»?

9

Из языкового руководства Java 5 :

Когда вы видите двоеточие (:) читайте его как «в».

Почему бы не использовать inв первую очередь тогда?

Это беспокоило меня годами. Потому что это несовместимо с остальной частью языка. Например, в Java есть implements, extends, superдля отношений между типами вместо символов , как в C ++, Scala или Ruby.

В Java двоеточие используется в 5 контекстах . Три из которых унаследованы от C. А два других были одобрены Джошуа Блохом. По крайней мере, так он говорил во время разговора «Споры о замыканиях» . Это возникает, когда он критикует использование двоеточия для отображения как несовместимое с семантикой for-each. Что мне кажется странным, потому что это ожидаемое поведение для каждого злоупотребленного. Нравится list_name/category: elementsили laberl/term: meaning.

Я слонялся вокруг jcp и jsr, но не нашел никаких признаков списка рассылки. Google не нашел никаких обсуждений по этому вопросу. Только новички путают смысл двоеточия в России for.


Основные аргументы против inпредоставленного до сих пор:

  • требует нового ключевого слова; а также
  • усложняет лексизм.

Давайте посмотрим на соответствующие определения грамматики :

заявление
    : оператор for ('forControl') '
    | ...
    ;

для управления
    : extendedForControl
    | ForInit? ';' выражение? ';' ForUpdate?
    ;

enhancedForControl
    : variableModifier * тип variableDeclaratorId ':' выражение
    ;

Измените, :чтобы inне принести дополнительную сложность или требует нового ключевого слова.

user2418306
источник
1
Лучшим источником для выяснения мотивов языковых дизайнеров часто являются сами дизайнеры. Тем не менее, это, по-видимому, просто синтаксический сахар над повторяемым; см. stackoverflow.com/questions/11216994/…
Роберт Харви

Ответы:

8

Нормальные парсеры, как их обычно учат, имеют стадию лексера, прежде чем парсер коснется ввода. Лексер (также «сканер» или «токенизатор») разбивает входные данные на маленькие токены, помеченные типом. Это позволяет основному синтаксическому анализатору использовать токены в качестве терминальных элементов, а не обрабатывать каждый символ как терминальный, что приводит к заметному повышению эффективности. В частности, лексер также может удалить все комментарии и пробелы. Тем не менее, отдельная фаза токенизатора означает, что ключевые слова также не могут использоваться в качестве идентификаторов (если язык не поддерживает удаление, которое несколько утратило популярность, или префикс всех идентификаторов с символом, подобным символу $foo).

Почему? Предположим, у нас есть простой токенизатор, который понимает следующие токены:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

Токенайзер всегда будет соответствовать самому длинному токену и предпочитать ключевые слова идентификаторам. Так interestingбудет лексировано как IDENT:interesting, но inбудет лексировано как INникогда IDENT:interesting. Фрагмент кода как

for(var in expression)

будет переведен в поток токенов

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

Пока что это работает. Но любая переменная inбудет указана как ключевое слово, INа не как переменная, что нарушит код. Лексер не хранит никакого состояния между токенами и не может знать, что это inобычно должна быть переменная, кроме случаев, когда мы находимся в цикле for. Также следующий код должен быть законным:

for(in in expression)

Первый inбудет идентификатором, второй будет ключевым словом.

Есть две реакции на эту проблему:

Контекстные ключевые слова сбивают с толку, давайте вместо этого будем использовать ключевые слова.

В Java много зарезервированных слов, некоторые из которых бесполезны, за исключением предоставления более полезных сообщений об ошибках программистам, переходящим на Java с C ++. Добавление новых ключевых слов нарушает код. Добавление контекстных ключевых слов вводит читателя в заблуждение, если у них нет хорошей подсветки синтаксиса, и затрудняет реализацию инструментов, поскольку им придется использовать более продвинутые методы синтаксического анализа (см. Ниже).

Когда мы хотим расширить язык, единственным разумным подходом является использование символов, которые ранее были недопустимы в языке. В частности, это не могут быть идентификаторы. С помощью синтаксиса цикла foreach Java повторно использовала существующее :ключевое слово с новым значением. С лямбдами Java добавила ->ключевое слово, которое ранее не могло встречаться ни в одной легальной программе ( -->все равно будет помечено как '--' '>'допустимое и ->ранее могло быть помечено как '-', '>', но эта последовательность будет отклонена синтаксическим анализатором).

Контекстные ключевые слова упрощают языки, давайте их реализовывать

Лексические бесспорно полезны. Но вместо того, чтобы запускать лексер перед парсером, мы можем запускать их вместе с парсером. Восходящие синтаксические анализаторы всегда знают набор типов токенов, которые были бы приемлемы в любом заданном месте. Затем парсер может запросить лексер для сопоставления с любым из этих типов в текущей позиции. В цикле for-each парсер будет в позиции, обозначенной ·в (упрощенной) грамматике после того, как переменная найдена:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

На этой позиции легальными токенами являются SEMICOLONили IN, но нет IDENT. Ключевое слово inбыло бы совершенно однозначным.

В этом конкретном примере парсеры сверху вниз также не будут иметь проблем, поскольку мы можем переписать приведенную выше грамматику в

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

и все жетоны, необходимые для решения, можно увидеть без возврата.

Учитывайте удобство использования

Ява всегда была склонна к семантической и синтаксической простоте. Например, язык не поддерживает перегрузку операторов, потому что это сделает код намного более сложным. Таким образом, при выборе между inи :для синтаксиса цикла for-each, мы должны учитывать, что является менее запутанным и более очевидным для пользователей. Крайний случай, вероятно, будет

for (in in in in())
for (in in : in())

(Примечание: Java имеет отдельные пространства имен для имен типов, переменных и методов. Я думаю, что это, в основном, ошибка. Это не означает, что в более поздних версиях языка придется добавлять больше ошибок.)

Какая альтернатива обеспечивает более четкое визуальное разделение между переменной итерации и повторяющейся коллекцией? Какую альтернативу можно распознать быстрее, если взглянуть на код? Я обнаружил, что разделяющие символы лучше, чем цепочка слов, когда дело доходит до этих критериев. Другие языки имеют разные значения. Например, Python расшифровывает множество операторов на английском языке, чтобы их можно было читать естественным образом и легко понять, но эти же свойства могут затруднить понимание части Python с первого взгляда.

Амон
источник
17

Синтаксис цикла for-each был добавлен в Java 5. Вам нужно было бы создать inключевое слово языка, а добавление ключевых слов в язык - это то, чего вы избегаете любой ценой, потому что это нарушает существующий код - внезапно все именованные переменные in вызывают синтаксический анализ ошибка. enumбыло достаточно плохо в этом отношении.

Майкл Боргвардт
источник
2
Это кажется ... неудобным. Это предполагает, что разработчики языка были достаточно хороши, чтобы с самого начала прогнозировать большинство необходимых ключевых слов. Я не уверен, что это даже необходимо; Приличные компиляторы могут определить, является ли ключевое слово переменной по своему контексту.
Роберт Харви
2
Я не думаю, что в Java есть контекстные ключевые слова, как в C #. Таким образом, использование inозначало бы либо ввести новое ключевое слово, нарушив тем самым обратную совместимость ( System.inкто-нибудь?), Либо ввести ранее неизвестную совершенно новую концепцию (контекстные ключевые слова). Все ради чего?
Йорг Миттаг
2
Чем вредны контекстные ключевые слова?
user2418306
5
@ user2418306 Добавление ключевого слова не должно нарушать существующий код, при условии, что язык не анализируется с отдельной фазой лексера. В частности, «in» in for(variable in expression)никогда не может быть неоднозначным с любым юридическим кодом, даже если «in» может использоваться для переменных. Однако отдельная фаза лексера довольно распространена во многих цепочках компиляторов. Это сделало бы невозможным или, по крайней мере, намного более сложным для анализа Java с некоторыми распространенными генераторами синтаксического анализатора. Простота синтаксиса языка обычно полезна для всех участников; не всем нужны синтаксические чудовища, такие как C ++ или Perl.
am
1
@RobertHarvey: не забывайте об этом, constи gotoоба являются зарезервированными словами в Java, но не используются (пока).
TMN