О чем это

Некоторое время назад меня попросили обосновать, почему я за Pascal при обучении программирования. А я скорее не конкретно за Pascal, я за наличие у языка для обучения программированию ряда важных свойств. Получается, я скорее против Python (шутка). В результате получилась эта мини-статья.

Важные для обучения свойства языка программирования:

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

1. Должен быть явный компилятор

  • Сразу видно, когда происходит трансляция (что для интерпретатора часто скрыто), это важно для понимания работы инструментов программиста.

  • Можно показать различие ошибок на этапе компиляции и ошибок времени исполнения.

  • Сообщение об ошибках компилятора часто понятнее и конкретнее (чем runtime), и как минимум затрагивают меньше возможных причин. А ответ "где у меня ошибка" особенно важен при обучении.

  • Слабое соображение, но всё-таки, скомпилированная программа, как правило, заметно быстрее выполняется. Это и само по себе интересно показать, и бывает важно на олимпиадах, экзаменах и т.п.

2. Должно быть явное объявление переменных и статическая типизация

  • Тип данных — важная концепция, и продемонстрировать её гораздо проще, когда тип явно указывается для переменной.

  • Раздел объявления переменных позволяет проиллюстрировать рассказ о сегменте данных программы и механизмах управления памятью.

  • Добавляет контроля на этапе компиляции, позволяя раньше и точнее обнаружить ошибку.

  • Повышает дисциплину ума при программировании, формирует хороший стиль.

  • Следует избегать языков с развитым автоматическим преобразованием типа данных, чем автопреобразование меньше, тем лучше. Автопреобразование усложняет понимание программы, на некоторых языках есть даже жанр основанных на этом загадок ("WAT"). Также автопреобразование смазывает само понятие типа данных.

3. В синтаксисе языка явное и заметное предпочтительнее скрытого

  • Значимость непечатных символов должна быть минимальна. Этот принцип можно сформулировать так: на месте любого количества любых невидимых символов можно поставить один пробел, и смысл не изменится. Пробельные символы – не отображаются, на глаз труднее отличить, сколько их идет подряд. Интуитивно трудно принять, что количество пробелов имеет значение. И всё это приводит к неприятным и трудно обнаружимым ошибкам.

  • Слова заметнее специальных символов, и смысл слова чаще всего понятнее, чем символа (если есть знание соответствующего английского слова). Использование слов ускоряет обучение и снижает количество ошибок (хотя и удлиняет программу).

  • Об��зательное явное указание чего-то часто лучше, чем умолчание. Тут лучше пояснить на примерах, которые будут дальше.

4. Если в языке прямо реализованы какие-то методические идеи, это хорошо

Если язык программирования разрабатывался именно для обучения (или эта цель хотя бы принималась во внимание), автор мог сделать что-то в языке специально, чтобы научить чему-то конкретному. Далее рассмотрим конкретные примеры такого.

5. Есть ещё много соображений для выбора языка

  • Важна популярность языка, как в индустрии, так и в образовании.

  • Хорошо, когда для языка есть развитая инструментальная база.

  • Для разнообразия творческих задач может быть важно, что на этом языке есть много готовых библиотек для решения разных задач в различных областях.

  • Для обучения, особенно первого изучаемого языка, важен "порог входа" – сколько всего нужно узнать, прежде чем сможешь написать свою первую "настоящую" программу (нетривиальную, не "Hello world"). Имеется в виду "вход в программирование" – минимальная подготовка для самостоятельного движения дальше.

Как видите, важных свойств много, оценки по ним вполне могут противоречить друг другу. И однозначного приоритета у них нет, итоговый выбор – скорее вопрос вкуса и опыта.

Примеры реальных языков программирования

Рассмотрим какие-то из описанных свойств на конкретных примерах языков.

Pascal vs Python

Наконец-то заявленное в названии сравнение. Кроме того, что это просто конкретные примеры, и примеры, реально предлагаемые в образовании, они ещё и "полюсы" диаметрально противоположенных подходов. Поэтому именно их сравнение будет особо показательно.

Трансляция

Pascal – компилируемый. Компилятор выдаёт понятные сообщения об ошибках, удобно встроен в среды разработки. Компиляция происходит в машинный код, программа выполняется максимально быстро и с минимальным расходом памяти.

Python – интерпретатор. Хотя у Python есть "фаза анализа", всё сказанное про смазанность этапа трансляции и диагностику ошибок только во время выполнения к нему вполне относится. Отчасти эти неприятности могут быть скомпенсированы хорошей средой разработки со статическим анализатором кода (типа PyCharm). Работает программа на Python обычно сравнительно медленнее и прожорливее по памяти, чем на других языках (трудно найти язык ещё медленнее). Для повышения быстродействия есть варианты (CPython, PyPy, Jython), но чаще решение находится в использовании внешних библиотек, написанных на C++.

Объявление и типы переменных

Pascal – требует объявления переменных и параметров, проверяет типы на этапе компиляции, чётко разделяет области видимости. Автоматическое преобразование типов ограничено числовыми типами, хорошо описано и мотивировано.

Python – отдельных объявлений переменных нет, они создаются динамически, при первом использовании (например, если ошиблись в имени переменной, будет просто создана новая). Есть глобальная область видимости. Тип переменной не фиксирован, есть по сути только тип значения. Есть объявление параметров функций, есть аннотации для указания типа, но в комплексе это мало меняет ситуацию. Преобразования типов часто явные (функции int()str()), также есть автоматическое преобразование числовых типов, но их правила сложнее и запутаннее, чем в Pascal, особенно при участии операторов сравнения. Также от числовых (и некоторых структурных) типов в Python не отделён логический.

Покажем пару примеров для иллюстрации. Сначала Python:

x: int = 1
b: bool = True
# Типы тут - только аннотации и не обязательны

print(b + x)
# Выводит "2", никаких сообщений

b = 1
# Выполняется, в PyCharm предупреждение: Expected type 'bool', got 'int' instead
print(b)
# Выводит "1"

b = b + 2
# Выполняется, никаких ошибок или предупреждений

print(False ** False == True)
# Выводит "True", WAT?

x = (1 << 53) + 1
print(x + 1.0 < x)
# Выводит "True", WAT?

И это я ещё не стал приводить примеры с использованием структурных типов данных.

Теперь посмотрим, как те же примеры (или похожие, если точной аналогии нет) выглядят в Pascal:

var
  x: Integer = 1;
  b: Boolean = True;
begin
  WriteLn(b + x);
  {Ошибка компиляции: Операция '+' не применима к типам boolean и integer}
  
  b := 1;
  {Ошибка компиляции: Нельзя преобразовать тип integer к boolean}
  
  b := b + 2;
  {Ошибка компиляции: Операция '+' не применима к типам boolean и integer}
  
  WriteLn(exp(ord(False)) = ord(True));
  {Выводит "True",
   не совсем то, что в Python (тут нет целочисленного возведения в степень),
   но в целом можно понять, что происходит}

  var x: BigInteger;
  {Ошибка компиляции: Внутриблочные переменные не могут иметь те же имена,
   что и переменные из блока верхнего уровня}

  var y: BigInteger = BigInteger(exp(ln(2)*54)) + 1;
  WriteLn(BigInteger(Real(y) + 1.0) < y);
  {Выводит "True",
   здесь мы обязаны явно использовать тип для длинной арифметики,
   и явное преобразование, чтобы сложить с вещественным числом.
   Можно выводить результаты выражений по очереди, и понять,
   что дело в ограничении точности вещественных чисел}
end.

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

Явное или скрытое

Тут в первую очередь приходят на ум значимые отступы на Python. Также можно привести в пример явное обозначение первой ветки условного оператора на Pascal. Рассмотрим код сортировки пузырьком на Python (честно украденный где-то в Интернете):

from random import randint


def bubble(array):
  n = len(a)
  i = 0
  while i < n-1:
    j = 0
    while j < n-1-i:
      if a[j] > a[j+1]:
        a[j], a[j+1] = a[j+1], a[j]
      j += 1
    i += 1


a = [randint(0, 99) for n in range(10)]
bubble(a)
print(a)

И аккуратно переведём его на Pascal (для наглядности максимально сохраняя соответствие строк):

procedure bubble(a: array of Integer);
begin
  var n := Length(a);
  var i := 0;
  while i < n-1 do
  begin
    var j := 0;
    while j < n-1-i do
    begin
      if a[j] > a[j+1] then
      begin
        var tmp := a[j];
        a[j] := a[j+1];
        a[j+1] := tmp;
      end;
      j := j + 1;
    end;
    i := i + 1;
  end;
end;

const N = 10;
var a: array of Integer;

begin
  a := new integer[N];
  randomize;
  for var i := 0 to N-1 do
  begin
    a[i] := random(100);
  end;
  
  bubble(a);
  
  for var i := 0 to N-1 do
  begin
    write (a[i]:3);
  end;
end.

В примерах специально сделаны одинаковые (и маленькие, всего два пробела) отступы, чтобы подчеркнуть эффект от явных операторных скобок. Большие отступы и подсветка в IDE конечно улучшают ситуацию. В целом на примерах видны и преимущества и недостатки обоих языков. Явность и четкость Pascal оборачивается многословностью. А краткость и «сахарность» Python (вы только посмотрите на кортежное присваивание, или инициализацию списка выражением) — незаметностью ошибок. Даже в приведённом примере на Python есть грубая ошибка (если не заметили, вместо формального параметра процедура сортировки обращается к глобальной переменной массива, и возможность такого — большая беда Python). Pascal, правда, подтягивается по части "сахара". Например, в современных версиях можно объявлять переменные внутри блока (для счётчика цикла это очень уместно), и не указывать тип в случае, когда компилятор может его однозначно определить сам (переменная в результате всё рано имеет строгий тип).

Для подготовки примеров кода использованы:

  1. IntelliJ IDEA 2024.1.2 (Community Edition) + Python plug-in Community Edition + Python 3.12 SDK

  2. PascalABC.NET 3.11, язык Русский.

Пара слов про методические идеи

Часто о Pascal говорят, что он специально придуман для обучения. Это действительно так, Никлаус Вирт, будучи профессором, одной из целей имел обучение студентов структурному программированию. Но хочется показать хотя бы один пример конкретной языковой конструкции, в которой воплотился хоть какой-то конкретный методический приём. И такой пример у меня есть:

type
   TFigure = (RECT, CIRCLE);
   TFigureParams = record
       case figure: TFigure of
          RECT: (a, b: Real);
          CIRCLE: (r: Real);
   end;

function area(f: TFigureParams): Real;
begin
    case f.figure of
        RECT: area := f.a * f.b;
        CIRCLE: area := Pi * f.r * f.r;
    end;
end;

var f: TFigureParams;
begin
    f.figure := CIRCLE;
    f.r := 10;
    writeLn(area(f));
    
    f.figure := RECT;
    f.a := 2;
    f.b := 3;
    writeLn(area(f));
end.

Это пример использования записей с вариантами (помните таких?). Видите, как похоже описание типа данных и код его обработки? Это и есть методическая идея Вирта: аналогия между алгоритмическими конструкциями и структурами данных. Массив — цикл со счетчиком, запись с вариантами — оператор выбора... Если честно, это всё, третьего примера у меня нет.

Насколько это полезно сегодня, можно понять по тому, что в PascalABC записи с вариантами вообще не поддерживаются, для компиляции пришлось использовать Free Pascal Compiler (version 3.2.2). В проекте PascalABC на Github можно найти ответ на вопрос "Does PascalABC.NET support Delphi variant records?": "Generally, you should use OOP things for this, like abstract classes and interfaces.", и тут трудно спорить.

Этот небольшой экскурс в историю конечно не отменяет того, что именно педагогические задачи подталкивали автора Pascal выбирать простой и надёжный синтаксис, следовать принципам строгости и последовательности, отказываясь от того, что могло сделать написание кода легче, но затрудняет его понимание и объяснение. Просто принципы так наглядно не покажешь.

А что же у Python? Как ни удивительно, тут тоже есть что назвать. И это странно, но назвать можно те же самые значимые отступы, которые мы ругали в предыдущем разделе. Одной из мотиваций для этого решения у Гвидо ван Россума (автора Python) было обучить программистов правильно форматировать код, соблюдая правило отступов. Правило отступов и в других языках (и в Pascal) является настоятельной рекомендацией, а Ван Россум сделал его обязательным (и для этого — единственным способом выделения блоков в программе).

Чей подход к методике обучения программированию вам больше нравится — каждый может решить самостоятельно.

Популярность, инструменты, библиотеки

Если в предыдущих номинациях я бы отдал победу Pascal, то по части популярности и использования в индустрии Python уверенно лидирует. Широта областей применения Python и разнообразие библиотек не то что по сравнению с Pascal, а в общем зачете языков программирования претендует на первое место. Не буду углубляться в эту тему, такие обзоры легко найти, в целом почти все сферы применения, от машинного обучения до разработки игр. Ну и как следствие — огромное количество информации, включая многочисленные и разнообразные учебные материалы, активное сообщество (или даже насколько, в разных областях), учебные курсы, стажировки, предложения работы и т.д. Инструментарий тоже крайне развит и разнообразен, под любые потребности, от онлайн блокнотов (Jupyter Notebook / Google Colab) до промышленных IDE c элементами ИИ (IntelliJ PyCharm).

У Pascal же всё это поскромнее (мягко говоря). Востребованность в индустрии катастрофически упала, и продолжает снижаться (хотя отдельные вакансии на Delphi ещё встречаются, но редко и за заметно меньшие деньги). Область применения и активность сообщества сосредоточена в основном в сфере образования. Учебной и методической информации тоже много. Интересно отметить, что учебные материалы по Pascal отличаются немного другой направл��нностью, больше академичностью что ли, от материалов по Python, но и тех и других так много, что найти можно под любой вкус. Инструменты есть, и вроде их достаточно, но даже отличный PascalABC всё-таки до продуктов IntelliJ не дотягивает (хотя зато шустрее и менее прожорливый).

А есть другие варианты?

На Pascal и Python предложения не заканчиваются. Для экономии времени мысленно оценим все языки программирования (какие вспомним) по двум условным шкалам: популярность/"лёгкость" и "мощность" (оба термина надо бы пояснить, но это долго, и для быстрой оценки хватит интуитивного понимания). Интересно, что они, как правило, антагонистичны (либо лёгкий, либо мощный). И с этими оценками обычно сильно коррелирует порог вхождения в язык (начать в лёгком — легко, а в мощном — трудно). Синтаксис лёгких языков часто адаптивный, "толерантный", а у мощных — более строгий (но это не точно). Также с этой дихотомией ассоциируется отчасти высокоуровневые / низкоуровневые языки. Мощные языки обычно ближе к машинной реализации вычислений (что и является источником их мощи), а лёгкие языки оперируют более близкой к человеческому мышлению абстракцией.

С этой точки зрения Python будет более лёгкий (ниже порог вхождения, больше подробностей скрыто за абстракцией, больше толерантности), а Pascal — более мощный (машинный код ближе, порог вхождения выше, больше строгости). Большинство других языков, на которых обучают (и самообучаются) программированию, оказываются либо ещё легче (и разболтаннее), чем Python (JavaScript, PHP, 1С), либо ещё мощнее (и труднее), чем Pascal (С++, C#, Java).

Но есть язык программирования, который на мой взгляд оказывается между нашими реперами. Он легче и популярнее, чем Pascal, и при этом строже и мощнее, чем Python. И Именно он будет неожиданной и свежей идеей для этой статьи. Это Kotlin (или Котлин, это вообще-то русское слово, это название острова, на котором находится город Кронштадт). Про Kotlin я могу рассказывать очень долго (это мой любимый язык программирования), но это сильно выйдет за рамки текущей темы. Так что сразу приведу резюме (похожее на рекламный слоган, но что поделать):

  • Если у вас сложилось обоснованное, "выстраданное" мнение, как что-то в языке должно быть сделано — скорее всего, в Kotlin это сделано именно так.

  • Если вы страдали от отсутствия какой-то возможности в языке программирования — скорее всего, в Kotlin эта возможность есть.

Ну и давайте попробуем перевести наш тестовый код, которым мы уже испытали Python и Pascal:

import kotlin.math.pow

fun Boolean.toInt(): Int = if (this) 1 else 0

fun main() {
    var x: Int = 1
    var b: Boolean = true
    // Типы обязательны и фиксированы, даже если не указаны явно

    println(b + x)
    // Ошибка компиляции: Unresolved reference.
    //   None of the following candidates is applicable because of receiver type mismatch:
    //   public inline operator fun BigDecimal.plus(other: BigDecimal): BigDecimal ...
    // Довольно многословно, но объясняет,
    // что оператора plus для сложения Boolean и Int нет

    b = 1
    // Ошибка компиляции:
    // The integer literal does not conform to the expected type Boolean

    b = b + 2
    // Ошибка компиляции: Unresolved reference...
    // Снова долго, но смысл тот же, подходящего оператора plus нет

    println(false.toInt().toFloat().pow(false.toInt()).toInt() == true.toInt())
    // Выводит "true",
    // пришлось написать функцию Boolean.toInt, готовой нет
    // (потому что она никому на самом деле не нужна).
    // Но это было просто, и с ней удалось в точности воспроизвести пример на Python.
    // Выглядит запутанно (и так и есть), но если внимательно читать, то понятно,
    // что же там происходит, и почему ответ именно такой.

    var x: BigInteger
    // Ошибка компиляции: Conflicting declarations

    var y = 2.toBigInteger().pow(54) + 1.toBigInteger()
    println((y.toFloat() + 1.0).toBigDecimal() < y.toBigDecimal())
    // Выводит "True", все преобразования выполнены явно
}

Всё получилось, как и у Pascal, правда текст сообщений об ошибках подкачал. Да и в целом наверно чувствуется, что технология скорее с расчетом на профессионалов, а не на студентов.

Что ж, давайте и сортировку попробуем перевести:

import kotlin.random.Random

fun bubble(a: Array<Int>) {
  val n = a.size
  var i = 0
  while (i < n - 1) {
    var j = 0
    while (j < n - 1 - i) {
      if (a[j] > a[j + 1]) {
        Pair(a[j + 1], a[j]).apply {
          a[j] = first
          a[j + 1] = second
        }
      }
      j += 1
    }
    i += 1
  }
}

fun main() {
  val a = Array<Int>(10) { Random.nextInt(99) }
  bubble(a)
  println(a.contentToString())
}

Как и обещано, мощность и строгость Pascal вместе с лаконичностью и богатством Python. Даже обмен значением сделал как в Python, через построение и деструкцию пары (только здесь это явно описано в коде). И массив инициализируется через выражение, только интуитивно понятно, где тут размер массива, а где код инициализации значений.

Что же до популярности, областей применения, сообщества, библиотек и инструментов, всё это примерно как у Java (ближайшего родственника кстати), только ещё и продолжает расти.

Выводы

Что ж, получилось обсудить, по каким параметрам вообще стоит сравнивать языки, на которых собираемся обучать программированию. Сравнили пару основных кандидатов примерно по намеченному плану, и даже предложили свежее "Соломоново решение". Конечно, однозначного вывода, какой язык самый подходящий для обучения, сделать нельзя. Серебреной пули не бывает, многое зависит от целей обучения, аудитории, да и от преподавателя. Но надеюсь, что теперь ваш выбор будет чуть более взвешенным.

Комментарии (131)


  1. bighorik
    04.11.2025 11:31

    Категорически поддерживаю.

    Сам являюсь преподавателем в it-ориентированном колледже. Веду шарпы у студентов 3 курса со специализацией .net. На первом курсе им давали питон и js , после чего некоторые не могут никак понять, на кой в коде всякие int и bool, как работает обычный for , почему они не могут сделать int a = console.readline() и тп. Надеюсь, в будущем смогу повлиять на программу.

    Мне с минимальными знаниями по шарпу в свое время было легко вкатиться в js, но я даже представить себе не могу , какого труда бы мне это стоило, выстройся мой путь наоборот.


    1. vadimr
      04.11.2025 11:31

      после чего некоторые не могут никак понять, на кой в коде всякие int и bool, как работает обычный for , почему они не могут сделать int a = console.readline() и тп

      Что характерно, в общем-то они правы. Тут вопрос в том, что является целью образования: передача устоявшихся предрассудков или развитие когнитивных способностей.

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


      1. fixikus
        04.11.2025 11:31

        Все просто объясняется. Программирование основано на логике. Первый закон логики: иметь не одно значение — значит не иметь ни одного значения; если же у слов нет значений, тогда утрачена всякая возможность рассуждать друг с другом, а в действительности — и с самим собой; ибо невозможно ничего мыслить, если не мыслить что-нибудь одно. Статическая типизация это как раз "реализация" этого закона.


        1. vadimr
          04.11.2025 11:31

          Эк вы глубоко копнули. Однако про типы переменных в этом законе ничего нет. А их значения единственны.


          1. fixikus
            04.11.2025 11:31

            Однако про типы переменных в этом законе ничего нет

            Ясен пень, этот закон Аристотель вывел.

            Смысл в том, что должна быть определенность. int - базовый целочисленный тип; bool - это примитивный тип данных в программировании, который может принимать только два значения: истина и ложь; и т. д. Типы не равны друг другу, но можно преобразовывать из один в другой. Статическая типизация позволяет вам следить за корректностью хода ваших мыслей и сразу по рукам бить, если что не так. Динамическая типизация позволяет вам говнокодить и получать ошибки в рантайме.


            1. vadimr
              04.11.2025 11:31

              Очень маловероятно, чтобы в реальной программе ошибка возникла бы именно на уровне несовместимости int с bool. Тут уж надо или формально выводить настоящие категориальные типы, как в Haskell или даже Coq, или не делать вид, что всё хорошо.

              Я очень много программировал на Паскале в своей жизни (такое время было, когда это было модно), и в целом этот язык мне скорее нравился, но я не могу припомнить, чтобы система статической строгой типизации Паскаля когда-либо нашла мне реальную ошибку в программе, а не мнимую (то есть не сводящуюся к тому, что в этом месте по синтаксису нужно вставить явное преобразование типов).


              1. Alex-ZiX
                04.11.2025 11:31

                Присваиваешь ты значение одной переменной другой переменной. Первая 32 бита, вторая 64. Всё присваивается и работает пока во вторую не запишут значение, которое не влезет в первую. А компилятор на этапе компиляции тебе уже даст подсказку, что может такая ситуация случиться.


                1. funca
                  04.11.2025 11:31

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

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


                  1. Vladimir_Zaitsev Автор
                    04.11.2025 11:31

                    Два? Обычно их гораздо больше.
                    Идея автоматизировать выбор формата данных - понятна (полезность для обучения пока отставим). Но приводит к огромному оверхеду из-за избыточности (как неотменяемая длинная арифметика в Python). И к постоянным перекодировкам, со всеми вытекающими и по производительности, и по надёжности.


                    1. funca
                      04.11.2025 11:31

                      Это действительно нужно для учёного языка? По-моему важнее интерактивность, релевантный набор абстракций изучаемым темам. Доступный API, интроспекция, чтобы абстракцию можно было крутить и разбирать на части как игрушку. А также адекватные, объясняющие сообщения об ошибках.

                      Я не фанат, но в том же питоне есть целые в смысле математики, и ctypes для работы с С-подобными структурами. В этом конструкторе подкупает возможность интерактивно "пощупать" и арифметику с фиксированной разряднотстью, и размеры структур и их отдельных полей, и работу с указателями и т.п. Можно поиграться с выравниванием, endian, и прочими прелестями, которые обычно зашиты в дебрях компиляторов и доступны в ощущениях разве что через дисассемблер. Синтаксис, правда, местами своеобразный.


                      1. Vladimir_Zaitsev Автор
                        04.11.2025 11:31

                        Про действительно ли нужно сразу вводить разные целые типы данных - дискуссионный вопрос, но могу по опыту сказать, что это точно можно, и не создаёт заметной ступеньки в обучении. Н сильно сложнее, чем просто концепция типов данных. Может даже помочь пониманию.
                        А вообще - было бы прикольно чери-пикать отдельные дизайн-решения из разных языков, жаль что такого нет.


                      1. Smerig
                        04.11.2025 11:31

                        ну вот как понадобится это число сохранить, к примеру, в БД, так сразу голова начнет работать, что выбрать. Можно вообще в строку перевести, по идее.


                      1. funca
                        04.11.2025 11:31

                        База, кстати, здесь не самый удачный пример: на границе слоев должен быть полноценный парсинг входящих данных, в рантайме, а не статический тайпчекинг. Мы же не знаем что там снаружи к нам прилетит.

                        Часто так и есть. Навороты, типа LINQ, могут скрывать от работчика подобные нюансы, реализуя парсинг за сценой - показывая в коде лишь переменные да типы. Это удобно, не спорю. Но у новичка такие неявности могут создать обманчивое впечатление, будто за него все ошибки ловит компилятор.


                      1. Smerig
                        04.11.2025 11:31

                        извините, не всегда получается выражать мысли четко :) Имеется в виду именно типы в БД. Если все вдруг будет вместо bit int64 к примеру


                      1. funca
                        04.11.2025 11:31

                        Ну мы вообще довольно далеко ушли от начальногой темы).


                  1. Alex-ZiX
                    04.11.2025 11:31

                    Как минимум для того, что один тип требует для хранения в два раза меньше памяти, чем другой. Представим обычную картинку. Чтобы её хранить, каждая точка в ней кодируется 4 байтами - RGB + альфа-канал. По вашей логике зачем использовать там целочисленный байт - давайте сразу брать UInt64. И размер нашей картинки вырастет в 8 раз. Это как пример и таких примеров огромное количество.

                    А при чём тут неверный пользовательский ввод вообще непонятно - такие задачи решаются через регулярные выражения. Это вообще разные типы ошибок.


                    1. funca
                      04.11.2025 11:31

                      Как минимум для того, что один тип требует для хранения в два раза меньше памяти, чем другой.

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

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


                      1. Smerig
                        04.11.2025 11:31

                        представляете, если все картинки вмиг вырастут в объеме в 8 раз. Этот объем для записи потребует в 8 раз больше времени. А если у вас тысяча фоточек. Раньше вы их за минуту копировали, а сейчас за 8. И это мы только про фоточки. А еще винда потяжелеет, весь код, базы данных, голос по мобильной сети передать тоже дольше. Эти числа - они везде, не только в картинках. Контрольный: уже не RTX 5070ti с 16 гигами GDDR7 памятью надо, а 128 гигов GDDR7. Интересно, сколько такая карточка стоить будет...


      1. bighorik
        04.11.2025 11:31

        Во-первых, в контексте int a = 10; я им объясняю, что в данном случае мы объявляем переменную, а дальше ею пользуемся, т.е. она существует в зоне видимости с этого момента, а не взялась хрен пойми откуда.

        Во-вторых, хоть сам я предпочитаю var, я смиряюсь с тем, что ревьювить код с явным типом гораздо проще.

        В-третьих, в контексте int sum(int a, int b) я им объясняю, что таким образом мы явно говорим, что ждем на вход, и что гарантированно будет на выходе. Это и читаемость повышает, и документирует код. Отсутствие этих "архаизмов" удобно только на примитивных примерах, а вот когда надо вспомнить, как пользоваться какой нибудь библиотечной функцией, или функцией, написанной коллегой, тут типизация упрощает понимание.

        Даже условное T func<T, R>(T first, R second, R third) понятнее, чем func(a, b, c)

        В-четвертых, аргумент про ошибки как вопрос "где вы были 8 лет " - все высмеивают, но никто так и не ответил. Мне при работе с шарпом ни разу не приходилось решать ошибки, возникающие из-за неправильного написания функции, а вот в js-e неоднократно.

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

        Например, у некоторых студентов вызывал ступор вызов console.WriteLine, когда им надо было вывести несколько значений. Они писали console.writeline(a, b, c) ожидая трёх выводов, а получают один. Им реально тяжело понять, что у функций разные сигнатуры, они привыкли к магическому print, который съест любую бездумную писанину


        1. vadimr
          04.11.2025 11:31

          Последний аргумент несколько напоминает “Господь страдал, и нам велел". Почему магическому? Просто обычная функция с параметрическим полиморфизмом и переменным количеством параметров. Допустим, print переписать в три вызова будет несложно, но с max или min придётся уже помучаться.

          Истинная причина в данном случае состоит в том, что либо в языке не поддерживаются функции с переменным количеством параметров, либо авторам стандартной библиотеки было лень удобно реализовать конкретно функцию print. Ничего дисциплинирующего мышление я в том и другом случае не вижу.


          1. bighorik
            04.11.2025 11:31

            Суть не в том, что "надо как в шарпе", а в том, что в шарпе явно обозначено, что функция принимает, и ide явно об этом говорит. Но они не привыкли это читать, они привыкли пользоваться магическим print из питона, они не понимают почему именно можно закинуть туда все через запятую.


        1. dimaaannn
          04.11.2025 11:31

          Вся беда таких способов преподавания - это в создании иллюзии будто код пишется для машины.

          Написание кода для машин уже давно ушло в прошлое, вместе с ассемблером и плюсовиками, с патологическим стремлением хвастаться кодом на бумажном листочке.

          Пайтон идеален для обучения по множеству причин. Однако параллельно дружелюбному пайтону с кучей "магии" так же нужен и старый добрый си, дабы понять что всё это работает весьма чётко и логично, хотя и умеет причинять боль из за строгости логики и "сайд эффектов" низкого уровня.

          А на счёт магического "print" - порочная практика использовать вывод в консоль в целом. Есть логгеры, есть return, и есть дебаггер. А пользоваться Console.WriteLine в коде - та самая проблема из разряда "зачем нужны типы данных" или "зачем нужен логгер"


      1. SadOcean
        04.11.2025 11:31

        Эта религиозная традиция работает в python и js так же, как и в других языках, просто замазана и скрыта (что приводит к значительным проблемам и обмазыванию тестами).

        Концепция типов фундаментальна и, в общем то, довольно проста - разные данные хранятся по разному, арифметические операции нельзя осуществлять с почтовыми адресами.


        1. vadimr
          04.11.2025 11:31

          Вы сейчас говорите о типах значений. А статическая типизация заключается в том, что типы присваиваются не только значениям, но и переменным. И это никак не влияет на возможность или невозможность осуществления арифметических операций с почтовыми адресами.

          Языки PL/I и Cobol статически типизированные, но там можно прибавить единицу к почтовому адресу (точнее, попытаться это сделать). Языки Lisp и Scheme (и Лого) динамически типизированные, но там нельзя прибавить единицу к почтовому адресу.


          1. SadOcean
            04.11.2025 11:31

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

            Это так же важно и для промышленного программирования - пусть когда то js и python сделали динамическую типизацию привлекательной фичей, сейчас куча людей пытаются решить проблемы, вызванные этим решением. TS как и аннотации типов в python придуманы не просто так.


            1. vadimr
              04.11.2025 11:31

              Типы данных, безусловно, существуют и находятся в фундаменте программирования (по крайней мере, два типа: атом и список). Я вовсе не имел в виду, что сами типы данных являются религиозной концепцией. Религиозной традицией я назвал декларации типов переменных.

              На этот вопрос можно смотреть с разных точек зрения. Вы вот привели аргумент, что динамическая типизация скрывает наличие типов данных. С моей точки зрения, скорее статическая типизация скрывает тот факт, что типы присущи самим данным, а не (не только) переменным.

              Более того, статическая типизация – это шаг к нестрогой типизации, то есть к неявным преобразованиям типов. Даже в Паскале целые разной длины неявно преобразуются друг к другу и к вещественным. Про C/C++ нечего и говорить. И это (потеря разрядов и расширение знака в результате присваивания переменной совместимого типа), на мой взгляд, гораздо более реальная проблема, чем мифическая запись bool в int.

              Я всегда специально обращаю внимание студентов, что оператор присваивания в статических языках включает в себя шаг преобразования типа справа налево, в то время как в динамических языках присваивание не имеет такой семантики.

              TS как и аннотации типов в python придуманы не просто так.

              Реальная востребованность их, как видим, невелика.


              1. SadOcean
                04.11.2025 11:31

                Ну как не имеет, а JS?
                Ну то есть при присвоении безусловно нечему приводиться, но при использовании значения JS выдает самый безумный список неявных преобразований, которые нужно знать. Собственно самые страшные вещи в js, это неявные преобразования строк в числа и обратно, потому что они нетранзитивны (не все строки это корректные числа) из-за чего их нужно постоянно валидировать при пользовательском вводе.
                Да и python сложение float и int вполне себе проводит с результатом в виде float

                C это отдельная вселенная, это безусловно не лучший учебный язык, в том числе из-за UB, а вот в других статических языках обычно неявные преобразования работают только для неразрушающих преобразований, что работает довольно прозрачно.

                Безусловно типы имеют данные, а переменные - лишь их абстракции.
                И только вам (пользователям языков в смысле) решать, какую абстракцию вы считаете менее протекающей - когда коробочка имеет форму или когда вместо коробочки наклеечка.
                Как по мне статическая валидация - штука удобная и для пользователя и для компилятора.
                Она позволяет выразительно описывать API (какие данные требуются функции), предотвращает множество тупейших ошибок с переиспользованием идентификаторов для разных (в контексте программы) сущностей и исключает целые классы ошибок валидации инвариантов, поскольку оные производятся на этапе компиляции и их никак нельзя проигнорировать.
                Динамические ЯП используют целую гору костылей типа стат анализа и автотестов для того, что работает почти бесплатно. Лишь ради возможности использовать другую, более простую, как им кажется, абстракцию идентификаторов.


                1. vadimr
                  04.11.2025 11:31

                  Неявные преобразования типов в js я, конечно, не одобряю (особенно известную фишку с конкатенацией-сложением), однако это не является аргументом против динамической типизации, как таковой.


                  1. SadOcean
                    04.11.2025 11:31

                    Так же как и против статической. Они слегка ортогональны.

                    Основное отличие - ты указываешь тип и потом живешь с этим ограничением. Или не указываешь тип, а получаешь его рантайм и живешь без ограничений.
                    Второе проще примерно на 1 ключевое слово в начале для каждой переменной и поля, но сложнее в неопределенном количестве мест, где ты не можешь определить необходимые данные по сигнатуре функции или понять что происходит лишь по идентификатору


                    1. vadimr
                      04.11.2025 11:31

                      Второе позволяет параметрический полиморфизм и универсальные контейнеры, что в некоторых приложениях очень упрощает жизнь.

                      Функция в динамическом языке не обязана знать, с чем она работает. Например, можно написать (и скомпилировать) сортировку элементов ещё неизвестного типа (так работает библиотечная функция sort).


                      1. Vladimir_Zaitsev Автор
                        04.11.2025 11:31

                        Универсальные контейнеры и обобщенные функции вполне себе есть и в статически типизируемых языках (generics).
                        И просто как забавный факт, вы удивитесь, что стало с функцией sort в последней версии Python.


                      1. vadimr
                        04.11.2025 11:31

                        Для генерика тип параметра должен быть указан в момент его компиляции. Это не всегда возможно.

                        А что там у питона?


                      1. Vladimir_Zaitsev Автор
                        04.11.2025 11:31

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

                        А в Питоне меня в своё время поразил переход на key-functions вместо компаратора (ну или интерфейса Comparable, если бы такое там было).


                      1. vadimr
                        04.11.2025 11:31

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

                        Тут возникла некоторая путаница в изложении. Конечно, нетривиальные случаи выходят за рамки обычного обучения. Однако тут приводился аргумент, что обучение динамическому языку плохо, потому что в коммерческой практике динамические языки плохи. И вот это утверждение сомнительно как в своём обосновании, так и в причинно-следственной связке.

                        А в Питоне меня в своё время поразил переход на key-functions вместо компаратора (ну или интерфейса Comparable, если бы такое там было).

                        Это действительно очень странно. Безусловно, в Питоне много странных вещей. Начиная с отступов.


      1. user-book
        04.11.2025 11:31

        вы еще и преподаватель?!?

        теперь понятно почему даже толковые люди после всяких "курсов вкатунов" могут нести откровенную ересь на собеседовании..


    1. Vladimir_Zaitsev Автор
      04.11.2025 11:31

      Спасибо за поддержку!
      Да, "высота" порога входа также определяет, куда с неё можно "опуститься" :)


  1. anonymous
    04.11.2025 11:31


    1. leonidzaytsev
      04.11.2025 11:31

      Как человек, который начал своё знакомство с программированием с JS могу подтвердить, что после него другие языки поддаются изучению, но кажется несколько сложнее, хотя паскаль, си в школе и универе учил, тут скорее всего всё дело в привычке


  1. vadimr
    04.11.2025 11:31

    Функция boolean.toint в Паскале называется ord.

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

    Кстати, в Лого есть одна очень важная в методическом отношении фишка – при обращении к переменной всегда указывается, идёт ли речь об имени или о значении. Нет ставящих в тупик

    x = x + 1

    (особенно когда в левой части присваивания тоже выражение), а есть

    сделай "x :x + 1

    где "x означает имя х, а :x означает значение x.

    Конечно, в коммерческом программировании заколебёшься каждый раз так писать, но для обучения это очень хорошо.


    1. Vladimir_Zaitsev Автор
      04.11.2025 11:31

      Интересное наблюдение про `сделай "x :x + 1`. Мне не бы в голову не пришло, что эта особенность синтаксиса может кем-то восприниматься как полезный методический инструмент.
      Если не секрет, расскажите про свой опыт преподавания программирования, пожалуйста. Какой возраст обучаемых, направленность, какие-то подробности?


      1. vadimr
        04.11.2025 11:31

        Я по основной работе занимаюсь коммерческим программированием, но иногда работаю с обучением студентов программистских специальностей. Также у меня был опыт руководства курсами повышения квалификации для преподавателей IT дисциплин в вузе и колледже.

        Интересное наблюдение про сделай "x :x + 1. Мне не бы в голову не пришло, что эта особенность синтаксиса может кем-то восприниматься как полезный методический инструмент.

        Просто огромное количество людей, изучающих программирование, либо прямо путает имя переменной с её значением, либо не может чётко объяснить соотношение между ними. Что там говорить, если даже в одном и том же стандарте ISO С++, написанном вроде бы профессионалами, связь имени со значением объясняется два раза по-разному.

        А в Лого, к слову, вот так вот можно сделать:

        (и тут же, кстати, вопрос на понимание: почему перед единицей не ставится кавычка или двоеточие?)
        (и тут же, кстати, вопрос на понимание: почему перед единицей не ставится кавычка или двоеточие?)

        Так как взрослых людей уже поздно учить Лого, я им показываю форму set в Common Lisp, в которой вычисляются обе стороны присваивания, и объясняю отличие от setq.


        1. beeruser
          04.11.2025 11:31

          либо прямо путает имя переменной с её значением

          А эти люди путают банку с надписью СОЛЬ с её содержимым?


          1. vadimr
            04.11.2025 11:31

            Эта аналогия работает только до определённой степени.

            Если у вас функция вызывается рекурсивно 10 раз, её локальная переменная одна или их 10? А если два имени ссылаются на одну ячейку памяти? А может ли быть переменная без имени?


            1. QTaKs
              04.11.2025 11:31

              Если у вас функция вызывается рекурсивно 10 раз, её локальная переменная одна или их 10?

              10, если нет передачи указателей/ссылок.

              А если два имени ссылаются на одну ячейку памяти?

              Два указателя ссылаются на один сегмент памяти. Вроде, обычная вещь.

              А может ли быть переменная без имени?

              А это называется утечка памяти.

              Надеюсь, тест прошёл)

              Вообще, всегда думал, что отсутствие явного использования ссылок и указателей идёт рука об руку с отсутствием явной типизации (прескорбно, но второе не гарантирует первого), а тут такое исключение из правил. Будем знать.


    1. GidraVydra
      04.11.2025 11:31

      То что Лого специально предназначен для обучению программированию, совершенно не значит, что он для этой цели лучше Паскаля. Я тоже начинал с Лого, и могу сказать свое ИМХО - это хорошая штука для преодоления "языкового барьера" перед написанием кода, но не очерь хороший инструмент для обучения программированию.


      1. vadimr
        04.11.2025 11:31

        А что вам не нравится в Лого по сравнению с Паскалем в качестве инструмента обучения?


        1. GidraVydra
          04.11.2025 11:31

          Очень много что не нравится.

          Во-первых, в Лого нет в явном виде многих сущностей и концепций, которые необходимы для обучения основам. Даже банальная типизация там крайне ограничена. Как вот, например, учить на Лого работе с нецелочисленными типами? А ведь это база, нет, базища! Про работу с памятью или вводом-выводом я вообще молчу. С последним вообще беда - он создает не просто упрощенное, но сильно искаженное восприятие того, как программа взаимодействует с этой подсистемой.

          Во-вторых, зачем в учебном интерпретируемом языке динамическая типизация? Чтоб обучающимся жизнь медом не казалась? Динамическая типизация - это плохой выбор для учебных целей.

          В-третьих, среда. Дельфи все-таки был довольно "взрослой" для своего времени средой, и формировал у человека приближенные к реальным привычки и восприятие. Лого же, по крайней мере в штатных средах, такого восприятия не вырабатывает. После Лого человек во взрослой среде разработки оказывается примерно столь же беспомощным, как и человек, до этого писавший псевдокод на листочке. То есть, он понимает (в рамках скромных возможностей Лого) синтаксис и логику, но совершенно не владет инструментарием разработки


          1. vadimr
            04.11.2025 11:31

            А что не так с нецелочисленными типами? В той версии Лого, которой я учился, были все основные лисповские типы данных.

            Работа с памятью в Лого прекрасна - сборщик мусора, который позволяет не заостряться на второстепенных вещах. Совершенно непонятно, зачем учить управлению памятью человека, не являющегося профессиональным программистом.

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

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


            1. GidraVydra
              04.11.2025 11:31

              А что не так с нецелочисленными типами? В той версии Лого, которой я учился, были все основные лисповские типы данных.

              Решил освежить воспоминания. Найти мануал по лого с типизацией оказалось не так уж просто. В общем, согласно мануалу, у них из численных типов на всё про всё есть только double. И это отвратительно с точки зрения преподавания. Потому что ученик не сможет ощутить на собственным опыте, что такое разные численные форматы, зачем они нужны, как работают и каковы их ограничения.

              Совершенно непонятно, зачем учить управлению памятью человека, не являющегося профессиональным программистом.

              Вот это как раз самая-самая база, и этому надо учить всех. Фактически, программа почти всегда (а в некоторых ос по факту всегда) работает именно с памятью. ИМХО не понимая как работает память - не понять как работает программа. Да, не всем надо давать такой объем знаний, какой нужен профессионалу, я даже не говорю сейчас про управление. Но хотя бы понимание того, как именно твоя программа работает с памятью, должно быть.

              Плюс черепаха, визуализирующая результаты.

              Черепаха, мягко говоря, не самый лучший и не самый репрезентативный интерфейс вывода. С помощью черепахи, наверное, даже можно извратиться и продемонстрировать, скажем, эффект двойной буферизацим, но это будут лютейшие костыли. А главное непонятно зачем всё это.


  1. anonymous
    04.11.2025 11:31


  1. dyadyaSerezha
    04.11.2025 11:31

    Теперь этот спор не имеет смысла, по крайней мере на территории России. Потому что (очередной раз) сделан 100%-кирилический язык программирования, а точнее, полностью русифицированный вариант JavaScript.

    https://www.cnews.ru/news/top/2025-10-30_bolshe_nikakoj_latinitsy

    Преподаватели программирования, настал ваш звёздный час!

    /s

    P.S. Уже вижу, как во всех учебных заведениях обзывают ставить МАХ и проводить все обучение на скрепном ЯП.


    1. Vladimir_Zaitsev Автор
      04.11.2025 11:31

      Забавно, да.
      Среди кириллических языков программирования у меня есть фаворит https://github.com/tsoding/good_training_language


    1. SadOcean
      04.11.2025 11:31

      Типичное импортозамещение - продукт с переклееннм шильдиком (js с переведенными ключевыми словами), некачественный (js взят за основу - отличный выбор), дорогой (нет инфраструктуры, материалов, библиотеки компонентов) и в конечном итоге никому не нужный при наличии оригинала


      1. dyadyaSerezha
        04.11.2025 11:31

        Зато, наверное, фонды получили на несколько лет вперёд.)


        1. SadOcean
          04.11.2025 11:31

          Я все же надеюсь, что в данном случае это больше шутка.
          И так печалей хватает с попилом


    1. dyadyaSerezha
      04.11.2025 11:31

      Люди, объясните, за что минусуете? С чем не согласны?


  1. anonymous
    04.11.2025 11:31