Я занимаюсь изучением синтаксиса языков программирования и пытаюсь отделить фундаментальные закономерности от исторических случайностей. Одна из таких случайностей - почти повсеместное использование терминов выражение (expression) и инструкция (statement) при описании и классификации синтаксиса языков программирования.
Интуитивно кажется, что 2 + 2 и if (x) { foo(); } - это совершенно разные сущности, но при более глубоком анализе оказывается, что подобное разделение искусственное и возникло из-за архитектурных особенностей вычислительных машин почти полвека назад и с тех пор просто «переходит» из языка в язык.
Часть 1. Историческая
Чтобы понять, откуда взялось разделение синтаксиса на выражения (expression) и инструкции (statement), нужно проследить эволюцию синтаксических правил от первых языков высокого уровня.
FORTRAN -> ALGOL 60 -> C: три акта одной пьесы
FORTRAN (1957) - разделение «по железу». В языке, созданном для IBM 704, выражения (
X + Y*Z) были строго отделены от управляющих инструкций (IF,DO,GOTO). Первые вычисляли значения, вторые управляли потоком исполнения и смешивать их между сбой было нельзя. Эта концепция синтаксиса напрямую отражала архитектуру вычислительных машин того времени: программа понималась как последовательность машинных команд, каждая из которых либо вычисляла операнд, либо изменяла счётчик команд, но не одновременно.-
ALGOL 60 (1960). В ALGOL 60 ввели составной оператор
begin ... end(прообраз блока), но важнее другое: язык разрешил условное выражение. Конструкцияif B then E1 else E2могла появляться в позиции любого выражения, а значит, допускала записьx := if a > b then a else bЭто был прямой предшественник тернарного оператора, показавший, что выбор между двумя значениями прекрасно вписывается в роль выражения.
C (1972) - фиксация различий Expression vs Statement. Деннис Ритчи (при участии Кена Томпсона, автора языка-предшественника B), проектируя C как «переносимый ассемблер» для PDP-11, закрепил в грамматике чёткое различие между этими понятиями. Такое решение диктовалось стремлением к максимально прямому отображению конструкций языка на машинные инструкции: условный переход
jzBEQпроцессора - это действие, а не способ вычислить значение.
Lisp и параллельная вселенная без statement’ов
Почти синхронно с FORTRAN, в 1958 году, Джон Маккарти создал Lisp, фундаментально основанный на лямбда-исчислении. Здесь нет понятия statement - вся программа состоит из S-выражений: будь то вызов функции, условная конструкция или блок.
В Лиспе конструкция (if (> a b) a b) - это просто выражение, значение которого можно присвоить, передать в функцию или использовать в более сложной композиции. А блок (let ((x 5)) (+ x 1)) возвращает 6, оставаясь обычным выражением.
Это не «расширение» возможностей, а фундаментальный иной принцип: вся программа - это вычисляемые значения, а не последовательность управляющих инструкций. С этой позиции разделение на expression и statement выглядит ненужным.
Часть 2. Современные языки
Интересно посмотреть, насколько глубоко в современных языках укоренилась граница между этими терминами и как она преодолевается.
Классическая модель: C, C++, Java (и Python)
В этих языках if, for, while категорически не могут быть частью выражения. Код
int y = if (x > 0) { 1; } else { 2; } // ошибка компиляции
недопустим. Разработчики вынуждены либо использовать тернарный оператор:
int y = (x > 0) ? 1 : 2;
либо (в C/C++) прибегать к нестандартным расширениям вроде GCC statement expressions:
int y = ({ if (x > 0) 1; else 2; }); // только с расширениями GCC
В Python ситуация аналогична: if - это инструкция, но начиная с версии 2.5 введено тернарное выражение:
y = 1 if x > 0 else 2
Эта конструкция - точный аналог тернарного оператора ?:, «заплатка» для грамматики, которая не решилась сделать if/else полноценным выражением.
JavaScript: эмуляция выражения через функцию
JavaScript формально сохраняет C-подобное разделение, однако в язык встроены обходные инструменты. Используя IIFE (немедленно вызываемые функциональные выражения), разработчик может «обернуть» statement-логику в контекст, где требуется значение:
let y = (function() { if (x > 0) return 1; else return 2; })();
До появления современных JIT-оптимизаций подобный трюк нёс реальные накладные расходы на вызов функции, но он наглядно демонстрирует ручную эмуляцию expression-грамматики поверх statement. Показательно, что раз сообщество идёт на подобные ухищрения - значит, потребность в таком подходе велика.
Ruby: всё есть выражение
В Ruby любая конструкция - выражение, без исключений. Можно написать:
y = if x > 0 1 else 2 end z = case value when 1 then "one" when 2 then "two" else "other" end w = while false # тело не выполняется end # w равно nil, но сам while - валидное выражение
Даже определение класса или метода возвращает значение последнего выражения. Грамматика Ruby не вводит отдельного нетерминала «statement» - вся программа состоит только из выражений.
Rust: гибрид - ни нашим ни вашим
Rust предлагает своеобразный компромисс: формально в грамматике различие между statement и expression есть (let-декларации, объявления элементов - это не expression), но управляющие конструкции (if, match, loop) целиком отнесены к категории Expression - в отличие от C/C++, где это принципиально невозможно.
При этом в Rust есть немало запутанных правил вокруг конструкций, которые вроде бы expression, но ведут себя странно в зависимости от контекста. Например:
let x = while true { break 5; }; // ОШИБКА! while не умеет так
тогда как очень похожий loop - умеет:
let x = loop { break 5; }; // ОК! x == 5
Различие между loop и while/for в том, что компилятор не может гарантировать, что тело while/for вообще выполнится хоть раз (условие может быть ложным с самого начала), тогда как loop либо бесконечен, либо выходит через break с конкретным значением. Эта асимметрия поведения чисто техническая, но принципиально влияет на синтаксис этих конструкций.
Или вот if со скобками и без них ведёт себя неожиданно:
fn f() -> i32 { if x > 0 { 1 } else { 2 } - 1 }
Кажется, что это должно вычислить (if...) - 1. Но нет! Компилятор трактует if {...} else {...} как отдельный statement (потому что он стоит не в «хвостовой» позиции блока), а - 1 - как отдельное выражение - унарный минус. Функция вернёт ошибку типов, потому что последней строкой оказывается -1, а значение if-выражения будет просто отброшено.
Чтобы получить ожидаемое поведение, нужно явно обернуть if в скобки:
let y = (if x > 0 { 1 } else { 2 }) - 1; // так работает как надо
Но самым странным является поведение точки с запятой, которая может менять тип функции:
fn f() -> i32 { 5 + 3; // с точкой с запятой! 10 }
А если поставить ; после последней строки:
fn f() -> i32 { 5 + 3; 10; // теперь тут ; } // ОШИБКА: функция должна вернуть i32, а вернула ()
; (точка с запятой) в Rust - это не просто «конец строки», а оператор, буквально меняющий тип выражения на () (пустой тип). Забытая или лишняя точка с запятой - самая частая причина неочевидных ошибок среди новичков.
Rust действительно продвинулся дальше C в сторону «всё есть expression», но это не единый принцип, а набор точечных, местами противоречащих друг другу правил, каждое из которых решает конкретную инженерную задачу (типовую безопасность, гарантии терминации, разрешение неоднозначности парсинга), а не следствие одной красивой идеи, последовательно применённой везде.
Часть 3. Что на самом деле отличает Statement от Expression?
Если вернуться к изначальному вопросу, так чем же отличается statement от expression?
Отвергнутые критерии
«Statement - выполнение, Expression - вычисление»- оба термина связаны с вычислительными действиями, разница не в природе операции.«Statement не возвращает значение»- опровергается Ruby, где всё возвращает значение.«Statement - отдельная категория грамматики»- это просто описание симптома (того, что в конкретном языке зафиксировано на уровне BNF), а не объяснение принципиальных различий между терминами.
Более продуктивным оказывается взгляд на пару statement/expression как на структурную композицию, в которой:
Expression - неделимая лексическая единица: литерал, идентификатор, вызов функции, математическая операция.
Точка с запятой
;(или её аналоги - перевод строки в Ruby) играет роль оператора-разделителя, который сообщает компилятору: нужно вычислить выражение слева, отбросить его значение и перейти к правой части. Цепочка из нескольких выражений, последовательно соединённых;и образует statement.Составной блок, ограниченный фигурными скобками
{...}- это способ превратить цепочку из нескольких выражений в единое неделимое выражение. Значением блока становится значение последнего выражения в нём.
Таким образом, statement - это не альтернативная категория синтаксиса, а композиция одного или нескольких выражений, соединённых оператором ;, где значение всей цепочки равно значению последнего элемента. Запись:
expression1; expression2; ... expressionN
можно прочитать как (expression1 ; expression2 ; ... ; expressionN), и её значением является значение expressionN. А если нужно оформить такую цепочку как единое выражение, то её помещают в фигурные скобки:
{ expression1; expression2; ...; expressionN }
В языках, поддерживающих такую интерпретацию, блоки естественно встают на место выражений в любом контексте - в правой части присваивания, как аргументы функции, как операнды.
А как же оператор запятая в C/C++?
Оператор запятая в C/C++ очень похож на реализацию описанной формальной модели - композицию выражений:
int y = (foo(), bar(), 42); // foo() и bar() вычислены ради побочного эффекта, y == 42
Но есть принципиальное отличие: оператор запятая - это всегда Expression и не может выйти за пределы одного expression-контекста.
int y = foo(), bar(); // Это НЕ comma-expression! Это два объявления (переменной и функции)! int z = (foo(), bar()); // а вот это - настоящий comma operator, нужны скобки
Без явных скобок оператор , (запятая) в контексте объявления переменных или списка аргументов функции интерпретируется грамматикой иначе - как разделитель в списке (declarator-list, argument-list). И эта неоднозначность грамматики C/C++ разрешается только контекстом.
Кроме того, оператор ,(запятая) ограничен уровнем expression и не может содержать statement-конструкции:
int y = (if (x > 0) 1; else 2, 42); // ОШИБКА - if не Expression, нельзя вставить внутрь ","
И напоследок, comma operator имеет самый низкий приоритет среди операторов C, и область его применения жёстко ограничена контекстами, где парсер может быть точно уверен, что это не разделитель списка:
f(a, b, c); // это вызов с тремя аргументами, а не comma-expression! f((a, b), c); // а вот это уже - comma-expression как первый аргумент
Часть 4. Формальная грамматика универсального синтаксиса
Если попробовать описать expression и statement в общем виде, получится красивая взаимная рекурсия между ними:
Program := Statement Expression := atomic-expr | Expression binary-op Expression | unary-op Expression | Expression '(' arg-list ')' // вызов функции | '{' Statement '}' // сгруппированный Statement - тоже Expression! Statement := Expression | Expression ';' Statement // рекурсивная композиция через ; arg-list := Expression (',' Expression)*
Где:
Statement - это либо одно выражение, либо выражение, за которым следует
;и новыйstatement(рекурсивно).Expression - это атомарное выражение (идентификатор, литерал, операция, вызов) либо
{ statement }, то есть блок, внутри которого находитсяstatement, но который сам рассматривается как единое выражение.
Именно взаимная рекурсия, при которой блоки могут содержать statement, а statement строится из выражений, придаёт такой модели логичность и стройность, тогда как разные языки её по-разному ограничивают:
C-подобные языки разрешают переход
{ statement } -> expressionтолько для простых выражений, но запрещают для управляющих конструкций. Поэтомуif (x) { ... }нельзя использовать как выражение, хотя блок в фигурных скобках сам по себе мог бы быть выражением, если бы грамматика это допускала. Более того, GCC-расширение({ ... })буквально реализует такое правило:{ statement }интерпретируется какexpression, тогда как стандартный C так не умеет.Lisp-подобные языки и Ruby вообще не нуждаются в отдельном нетерминале
statement- в этих языках всё является выражением.Rust оказался где-то посередине. В нём есть категория объявлений (
let,fn,mod), которые являются инструкциями и не могут быть частью выражения. Тем не менее все управляющие конструкции (if,match,loopи т. д.) являются выражениями.
Заключение
Разделение на expression и statement, привычное для многих поколений разработчиков C-подобных языков - не логическая необходимость, а историческая случайность, закреплённая архитектурой машин фон Неймана, которую перенесли из FORTRAN в ALGOL, а затем в C. Оно зафиксировалось в грамматиках как «удобное» решение на заре компиляторостроения, но не имеет фундаментальных причин на уровне синтаксиса.
Анализ развития синтаксиса разных языков программирования показывает, что statement можно рассматривать как структурную композицию выражений, а блоки в скобках - как инструмент обратной «упаковки» такой последовательности в единое целое.
Понимание истинной природы statement избавляет от ненужных условностей и даёт единый взгляд на синтаксис, в котором точка с запятой - всего лишь оператор, а фигурные скобки - не отличительный признак statement, а мост между двумя мирами, которые на самом деле, по сути, одно и тоже.
Комментарии (33)

SIISII
02.08.2026 09:35В языке, созданном для IBM 704, выражения (
X + Y*Z) были строго отделены от управляющих инструкций (IF,DO,GOTO). Первые вычисляли значения, вторые управляли потоком исполнения и смешивать их между сбой было нельзя. Эта концепция синтаксиса напрямую отражала архитектуру вычислительных машин того времени: программа понималась как последовательность машинных команд, каждая из которых либо вычисляла операнд, либо изменяла счётчик команд, но не одновременно.Вообще-то, современные вычислительные машины ничем в этом плане не отличаются: есть машинные команды, выполняющие вычисления, а есть выполняющие переходы (с анализом некоего условия или без такового). Иногда встречаются команды, выполняющие простейшее вычисление и условный переход в зависимости от результата -- главным образом, для организации циклов по счётчику, но такие есть не везде, да и не позволяют они делать "серьёзные" вычисления.
Это был прямой предшественник тернарного оператора
Во-первых, это, по свой сути, оно и есть, а не "предшественник". А во-вторых, в русском языке для действий в выражениях принято слово "операция" (арифметические операции и т.п. -- не операторы; в английском -- да, operator).
Такое решение диктовалось стремлением к максимально прямому отображению конструкций языка на машинные инструкции: условный переход
jzпроцессора - это действие, а не способ вычислить значение.Во-первых, повторю уже сказанное: древние и современные машины не имеют сколько-нибудь принципиальных различий в действиях, выполняемых машинными командами; важнейшее отличие -- современные архитектуры все вычисления выполняют в регистрах, в то время как "в древности" нередко имелись команды, выполняющие операции прямо над содержимым ячеек памяти.
Ну а во-вторых, у PDP-11 нет команды JZ, хотя идея, высказанная автором, понятна.
архитектурой машин фон Неймана
Архитектура фон Неймана, как и гарвардская архитектура -- это не про то, какие команды есть и что они делают, а про то, используются ли для памяти единые адреса независимо от того, команды это или данные (фон Нейман), или же адресные пространства кода и данных строго отделены друг от друга и не пересекаются, а численно один и тот же адрес указывает разные ячейки памяти в зависимости от того, является ли он адресом команды или данных (Гарвард).
В общем, как по мне, слов в публикации много, а толку -- не очень...

rsashka Автор
02.08.2026 09:35Спасибо, вы поняли суть, но упустили из виду важный момент. Я старался делать акцент не на абстрактных командах микропроцессора (ассемблера), а на синтаксисе языка, как отображения этих команд.
То есть, если раньше синтаксис ЯВУ старался повторять идеологию машинных инструкций и архитектуру вычислителей, то сейчас (точнее уже много лет) синтаксис языков вообще никак не привязан к вычислительной архитектуре.

KvanTTT
02.08.2026 09:35Странно, что в статье не упоминается Kotlin, в котором почти все является expression, результат которого можно использовать. И есть типы
UnitиNothing:fun executeAction(action: () -> Unit) { action() // Executes the lambda expression } fun main() { executeAction { println("Button clicked!") } }fun throwError(message: String): Nothing { throw IllegalArgumentException(message) } fun parseAge(input: String): Int { val age = input.toIntOrNull() // Valid because Nothing is a subtype of Int return age ?: throwError("Invalid age provided!") }fun calculateComplexTax(income: Double): Double { // Compiles cleanly even though it doesn't return a Double yet! TODO("Implement regional tax calculation logic") }
rsashka Автор
02.08.2026 09:35На самом деле у меня изначально была еще и Java, но потом я подумал, что и так очень много примеров получается, поэтому Java (и Kotlin, как её творческую переработку) я в качестве примеров приводить уже не стал, хотя может быть именно Kotlin и стоило бы упомянуть.
mbait
Вместо тысячи слов хотелось бы увидеть пример дизайна языка, где нет разделения на statements и expressions, но и нет уродливого побочного синтаксиса. Скажем, решаем мы, что последнее выражение это значение блока while, но тут мы пишем последней строкой break. И приходится либо задавать break возвращаемый тип, либо... у нас снова разделение.
rsashka Автор
Ruby ?
mbait
То есть, вы просто посмотрели синтаксис языка и решили, что в компиляторе нет разделения на expr/stmt?
rsashka Автор
Что значит “посмотрел синтаксис”? Это фундаментальная особенность языка.
playermet
В Ruby есть явное разделения на statement и expression, и в официальной документации подписано что есть что. Например: break, next и redo это statements.
А вот грамматические правила для statements в Ruby, это очень старая версия, от 2004 года, но тем не менее. Видно что есть stmt, а есть expr и expr_value.
rsashka Автор
Что вы имеете ввиду под “statements” ? Нет возвращает значение, отдельная категория грамматики, которую нельзя использовать как отдельное выражение или что-то еще?
Ведь “break, next и redo” - это часть грамматики языка, но она не является автономными лексическими единицами, чтобы их можно было использовать как отдельное выражение. Поэтому, да это действительно операторы, но их нельзя использовать по отдельности вне определенного контекста, т.е. они сами по себе не являются “неделимой лексической единицей” и сами по себе не являются выражениями.
Тогда как лексические конструкции, где эти термины могут быть использованы, являются полноценными выражениями, а тот же самый
breakспособен возвращать значение.playermet
Имею ввиду общеиспользуемое их определение, которое в том числе применяется в официальной документации Ruby.
Как и все другие в языке кроме корневого, с которого начинается парсинг.
А вот еще такой пример из документации:
rsashka Автор
Общепринятое определение, это “statement” не возвращает значения, но в Ruby это не так. Так какое определение вы имеете ввиду?
playermet
Источник можно? В англоязычной википедии например используется "statement is a syntactic unit of an imperative programming language that expresses some action to be carried out", в русскоязычной "наименьшая автономная часть языка программирования ... Программа обычно представляет собой последовательность инструкций". И примерно то же самое написано во всех учебниках.
При этом каждое expression в Ruby буквально включено в statement. Это отражено как в его правилах грамматики:
stmt : expr, так и в документации, что можно заметить по цитате выше: "statements that are not expressions ...". И Ruby в этом не одинок, в тех же C и C++ это тоже верно.rsashka Автор
Тогда по вашему выходит, что “expression” не может содержать инструкции? А если может (а оно может), то в чем тогда между ними различие?
playermet
Напрямую не может, конечно. И это не "по моему", это прямо отражено в документациях, грамматиках, и реализациях любого языка программирования в котором заданы эти сущности. Нет, ну можно конечно заморочиться и создать собственный язык, где все это принципиально схлопнуто в одну сущность, но это будет не более чем академический (если не эзотерический) эксперимент.
Отличие в том, что из последовательности statement состоит вся программа, а expression это лишь одна из подкатегорий statement в некоторых языках, кроме которой существуют множество других.
rsashka Автор
Это детали реализации грамматики, которые разработчики реализовали именно таким образом. В одном случае это будут одни правила, а в другом - другие. Просто разработчики написали их именно так и нетерминал в парсере назвали
stmt, а в другом языке нетерминал грамматики взяли и назвалиstmt_or_expr, но что в итоге доказывает или объясняет?Все это только детали реализации, а не фундаментальная причина их использования, ведь в противном случае у нас просто не было бы причины для спора :-)
LISP, это не академический язык, в котором все является выражением и вся программа состоит тоже только из выражений.
playermet
Так про что угодно можно сказать. Все ЯП это формальные системы, в которых если захотеть можно букве имени класса номер строки в исходниках присвоить. Но это не значит что можно перечеркнуть все устоявшиеся определения.
Так уж случилось, что есть сотни императивных языков программирования, в которых отдельно stmt, отдельно expr, и назвать их можно как уходно, хоть a и b, но это две отдельные сущности, которые соотносятся определенным образом, и это не только деталь реализации, но и прописанная в документациях особенность. И так уж случилось, что нет языков, в которых есть один терминал названный stmt_or_expr, либо это две отдельные сущности, либо одной из них просто нет. Определения нужны, чтобы люди могли одинаково понимать о чем идет речь, когда слышат конкретное слово, и обычно они отражают конкретную сложившуюся по факту действительность. И она сложилась именно вот так.
Тем более, что речь изначально шла про Ruby, в котором это разделение такое же явное, как и в С/С++/Java. Т.е. прямо указано, что некоторые statement не являются expression, и использование их там, где ожидается expression является синтаксической ошибкой.
LISP это не императивный язык, в нем программа это список атомов, а не последовательность строк/команд. В нем просто нет statement как сущности, и это не то же самое что тождественность stmt и expr. При этом он вполне академический.
rsashka Автор
Напишите пожалуйся “устоявшееся” определения, чем же, по вашему, «Expression» принципиально отличается от «Statement». Я нашел несколько определений, но все они имеют опровергающие контр примеры. Может быть у вас это получится лучше.
Можете привести примеры этих statement?
Давайте, пожалуйста, не будем притягивать за уши лишние сущности, и сперва определимся, чем же «Expression» принципиально отличается от «Statement», а потом уже посмотрим, что есть в LISP, а чего нет.
Ведь я и пишу о том, что жесткое разделение на stmt и expr, это только дань привычке, потому что нас так учили на примере подобного разделения, которое в своею очередь так сложилось в силу некоторых обстоятельств.
playermet
Ну например.
Для statement: "syntactic unit of an imperative programming language that expresses some action to be carried out".
Для expression: "a sequence of operators and operands, that specifies a computation".
Какие конкретно сущности являются лишними? Статья была посвящена терминам statement и expression, разделение которых продиктовано синтаксическими нюансами императивных ЯП, например банальным нежеланием разрешать и как-то обрабатывать конструкции вроде "print(return)", "if(break)", "while(import)" и т.п. Теперь вы приводите в пример LISP, в грамматике которого вообще не прописано ни statement, ни expression в значениях аналогичных императивным языкам.
Настолько же дань привычке, как использование латиницы в ключевых словах, организация кода по строкам с выполнением сверху вниз, позиционной десятиричной системы счисления. Можно ли сделать язык с египетскими иероглифами, выполнением по спирали на сетке против часовой стрелки и римскими числами? Можно. Является ли "дань привычке" основной причиной почему так не делают? Сомнительно.
Вот если сначала доказать, что альтернативный способ объективно лучше используемого (например, приводит к меньшему числу ошибок, или ускорению написание кода при прочих равных), но не используется без видимых причин - вот тогда это действительно может быть данью привычки.
Siemargl
В каждом языке свое определение разделения на stmt и expr итп
И, соответственно, записано в БНФ
Осталось взять и проанализировать
rsashka Автор
Именно это я и сделал. Результаты анализа представлены в статье :-)
Siemargl
Я тогда не уловил смысл статьи.
Заголовок всегда true, поскольку разделение [на stmt, expr, op, ..] зависит от контекста (ЯП и даже местоположения лексической конструкции).
Популяризация чтения учебников и БНФ, рассказ как оно устроено и почему?
Для меня это выглядит как "небо синее". И далее либо ты это принимаешь как есть, либо лезешь в физику дифракции света.
rsashka Автор
Все так, разделение на stmt и expr зависит от языка, который, как правило, тянет за собой исторический груз, в том числе и из базовых учебников. Сперва мне самому захотелось разобраться в этом вопросе, а потом решил результатами поделиться с остальными.
ImagineTables
Nemerle
rsashka Автор
Точно!
NeoCode2
Я уже много лет изучаю языки программирования и разрабатываю свой, и я совершенно не вижу в чем проблема создать такой дизайн. Главное - правильная система типов.
Для
if(x>0) y; else z;возвращаемый тип это тип-суммаtypeof(y) + typeof(z)То есть если y это string а z это float то на выходе получаем "вариант" (дискриминированное объединение) с двумя полями string и float.
Если у нас цикл с предусловием
while(x>0) y;или условие без альтернативной веткиif(x>0) y;то очевидно что результат - тип-суммаtypeof(y) + never, что эквивалентно опционалуoptional(typeof(y))Разумеется эти типы должны быть структурными (не номинативными), чтобы была совместимость с любым явно определенным пользовательским номинативным типом.
При такой системе типов стандартные структурные стейтменты всегда будут совместимы с выражениями. Операторы "запятая" и "условие" можно будет убирать из языка чтобы они не создавали путаницы (по сути запятая это ведь разделитель элементов в списке, к примеру в списке аргументов функции или в литеральном кортеже, но если она еще и оператор - возникает ненужная путаница и усложнение).
rsashka Автор
Подобное ограничение не обязательно (точнее, оно определяется системой типов языка).
NeoCode2
Это не ограничение, а скорее удобство. Компилятор генерирует "нетерминальный" составной тип, и логично чтобы он вел себя так же, как литерал вида {1,2,3} в обычном Си, которым можно инициализировать объект любой структуры из трех числовых полей (увы, в Си эта тема неразвита, и просто присвоить или передать в функцию литерал не получится - только инициализировать, но как пример подойдет и инициализация).
rsashka Автор
Так я и говорю, чтоб подобное удобство зависит от системы типов языка. Если он статически типизируемый как С/С++ , тогда ему по любому нужен какой-то тип, чтобы было что проверять при компиляции. А, например, для динамически типизируемых языков конкретный тип выводить не обязательно. Для них без анализа будет достаточно какого нибудь
Any, который все равно будет проверяться в рантайме.kotan-11
Argentum
ant3mc
Будут ли новые материалы по Аргентуму ?