4 марта в 12:11

О чём молчат авторы «Hello, World!»-ов

image


Обычно адепты нового Того-самого-лучшего-языка пишут свои рекламные мини-программы для обучения и продвижения примерно так:


ageOfBob = 0
ageOfMary = 0

print("Input Bob's age: ")
read(ageOfBob)

print("Input Marry's age: ")
read(ageOfMary)

// Do the math
if (ageOfBob > ageOfMary)
   print("Bob is older than Mary by ", ageOfBob - ageOfMary, " years")
else if (ageOfBob < ageOfMary)
   print("Mary is older than Bob by ", ageOfMary - ageOfBob, " years") 
else 
   print("Mary and Bob are of the same age")

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


Если этот новичок и вправду заинтересуется Тем-самым-лучшем-языком, то к тому моменту, когда ему придётся писать взрослые программы в соответствии с рекомендациями по написанию надёжного кода для серьёзных клиентов или серьёзного работодателя, отступать уже будет поздно:


const MAX_PERSON_AGE = 120
const MIN_PERSON_AGE = 1

int getAge(string name) {
   age = 0

   print("Input ", name, "'s age: ")
   read(age)

   if (age < MIN_PERSON_AGE or age > MAX_PERSON_AGE)
      throw IncorrectAgeInputException
   else 
      return age
}

try {
   ageOfBob = getAge("Bob")
   ageOfMary = getAge("Mary")
} catch (IncorrectAgeInputException) {
      print("You're doing it wrong!")
}

// Do the math
...

Итого лёгким движением руки четыре строки кода ввода числа превратились в определение пары констант (потому как магические числа в коде — плохо) и функцию (потому как Don't Repeat Yourself), которая генерирует исключения, которые также нужно обрабатывать. А если ещё вспомнить, что функцию getAge ещё нужно покрыть хотя бы парой unit-тестов…


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


Теперь посмотрим, как возможности строгой типизации могут помочь решить такую задачу.


Перепишем нашу повзрослевшую программу на Ada. В Ada уже с 1983-го года есть та самая фича "если оно компилируется, значит оно работает", которой теперь рекламируют Haskell и Rust. Также программы на Ada компилируются в native-код и могут работать в том числе на микроконтроллерах в реальном времени, не на много уступая языку С по скорости выполнения. Впрочем мы отвлеклись…


with ada.text_io, ada.integer_text_io, ada.io_exceptions;
use  ada.text_io;

procedure main is
   type Age is range 1 .. 120;

   package io is new ada.text_io.integer_io(num => Age);

   ageOfBob, ageOfMary : Age;
begin
   put_line("Input Bob's age: ");
   io.get(ageOfBob);

   put_line("Input Mary's age: ");
   io.get(ageOfMary);

   -- Do the math
   if ageOfBob > ageOfMary then
      put_line("Bob is older than Mary by" & Age'Image(ageOfBob - ageOfMary) & " years");
   elsif ageOfBob < ageOfMary then
      put_line("Mary is older than Bob by" & Age'Image(ageOfMary - ageOfBob) & " years");
   elsif ageOfBob = ageOfMary then
      put_line("Mary and Bob are of the same age");
   end if;

exception
   when ada.io_exceptions.Data_Error =>
      put_line("You're doing it wrong!");
   when others => null;
end main;

По сравнению с самым простым первым вариантом этой программы был лишь добавлен новый тип Age с явно заданным диапазоном значений:


type Age is range 1 .. 120;

Также был подключен пакет ada.text_io.integer_io, параметризованный этим типом:


package io is new ada.text_io.integer_io(num => Age); 

Теперь при вызове функции io.get(ageVar), где ageVar — переменная типа Age, будет проверяться введённое пользователем число и в том случае, если оно не соответствует своему типу Age, будет генерироваться исключение Data_Error.


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

@yarric
карма
5,0
рейтинг 10,4
Пользователь
Самое читаемое Разработка

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

  • +22

    Ничего не понял, прочитав до конца. Причем тут Ада? Чем классический не раздутый хеллоу-ворлд плох для новичка? Новичек он поэтому и новичек, что потом только узнает про все остальное, а не сейчас.
    Странная статья.

    • +3
      Строгая типизация == раз скомпилировалось, значит работает. Спорное утверждение, программисты на С++ могут не согласиться.
      • +3

        Строгая типизация в этом сильно помогает.

        • +2
          Есть какие-то результаты статистических или иных исследований в поддержку этого утверждения?
          • –1

            Та же Ada ведь не с потолка была придумана.

            • 0
              При всём уважении, это вообще ни разу не ответ на мой вопрос. То, что Ада является языком со статической сильной типизацией — это не доказательство того, что сильная типизация в чем-то лучше слабой. Это лишь следствие того, что все варианты языка, представленные на конкурс, были основаны на Паскале.
              • –6

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

                • +6
                  Нет, не является.
                  • 0

                    Ой, всё :)


                    Нет, ну если серьёзно — я не знаю ни одного другого языка, дизайн которого был бы так же хорошо обоснован на основе формальных требований, составленных по результатам опыта реальной разработки сложных систем. Есть ещё пара стандартов для языка C: MISRA C и Frama-C.

                    • –2
                      Формальные требования были описаны для конкретного контекста. В другом контексте требований могут быть (и будут) совсем другими.
                    • –1
                      java?

                      дизайн, скорректированный опытом реальной разработки сложных и «сверхбольших» систем на протяжении почти 20 лет. не? :)
                      • 0

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

                        • 0
                          но в итоге то, микроволновки выросли в майфреймы.
                          • 0

                            Тем не менее никто не пытается её применять в медоборудовании или авиации. А какая область применения у неё на мейнфреймах?

                            • 0
                              гм… «основной язык для описания бизнес-приложений», если это можно так назвать. ibm-ы традиционно выделяют отдельный процессор специально под задачи джавы. (я говорю про айбиэмовские майнфреймы, потому, что иные производители, куда-то исчезли)

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

                              ps: это такой тролинг, или вы серьезно не в курсе «области применения на майнфреймах»? давайте обсудим.
                            • 0
                              Насчет медицины и авиа ничего не могу сказать, не сталкивался. Лично по мне, так си для этих целей — самое то.

                              Вы хотите сказать что ада применяется в медицине и авиа (в смысле «до сих пор»)?

                              Я не для спора, мне скорее интересна фактическая ситуация.
                              • 0
                                В медицине можно выделить два полюса приложений — учётно-статистические и оперативно-мониторящие. В чистой учётке каждый извращается как может — вплоть до 1С и макросов на Excel, но чем ближе к пульсирующему в realtime телу (когда жизненно критична скорость обработки и отклика), тем больше C\C++ и меньше экзотики. Полагаю, в авиации похожая ситуация. А так, в целом, и SCADA-системы на C# пишут и ЧПУ-станки питоном программируют.
                                • +1

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


                                  Есть немалый список проектов, в которых используется Ada. Обычный C в mission-critical проектах не используется, используется подмножество MISRA C или Frama-C. Кстати интересно заметить, что для обеспечения надёжности эти стандарты выкинули из C много фич — к теме о бесполезных нововведениях в языках. Что касается C++, то преимущества его использования по сравнению с Ada мне не понятны, разве что программистов найти легче.

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

                                    Иногда мне кажется, что если бы разработка шла с нуля, снизу-вверх (пусть бы даже и c привлечением PHP :)), получилось бы куда эффективней. По крайней мере, разработчики реально бы вжились в бизнес-процессы, а не пытались навязывать «универсальное решение».

                                    Против Ada (как подхода) ничего не имею, идея синтезировать статический анализатор с компилятором вполне себе разумная мысль (нечто подобное пытаются осуществить и упомянутая Farma-C, и набирающий популярность Rust). Но ведь это же не единственный подход. Есть, например, TDD. Опять же, статический анализатор к C++ всегда можно подключить «сбоку». Это как выбор между кухонным комбайном (всё в одном) и набором специализированных устройств (блендер, соковыжималка, мясорубка и т.д.).

                                    Что касается «новых фич», уверяю, ни в одном из языков (в классической вычислительной парадигме) никогда не будет «новой фичи», которой бы до этого не было в Lisp. Да и фича, фиче рознь. По крайне мере то, что я использую из C++[11-14], (на мой взгляд) существенно понижает вероятность ошибок, повышая удобство разработки.
                                    • +2
                                      Опять же, статический анализатор к C++ всегда можно подключить «сбоку».

                                      Лучше, когда статический анализ сделан гармонично с языком в виде системы типов.

                                      • 0
                                        Кто ж спорит, однако у этой гармоничности есть своя цена. В качестве аналогии — ифкуиль на уровне грамматики «заставляет» выражать свои мысли чётче и однозначней и в этом гораздо круче любого из естественных языков. Засада в том, что мало кто из реальных людей (даже теоретически) способен его выговорить (58 фонем с тонами!), свободно сочетая многомерные грамматические таблицы по вариативными правилами организации формативов.
                                        • 0
                                          ифкуиль на уровне грамматики «заставляет» выражать свои мысли чётче и однозначней и в этом гораздо круче любого из естественных языков.

                                          Засада в том, что мало кто из реальных людей (даже теоретически) способен его выговорить

                                          А зачем собственно что то выговаривать? Главное — суметь точно мысль выразить (как в математике).
                                          Вполне можно представить себе спецификацию С++ написанную рабочей группой по языку на урезанном подмножестве ифкуиля (без фонетики, которая там создаёт проблемы — только смысл, воспринимаемый с экрана) с введёнными дополнениями, которые будут аналогами команд Git, нужными для работы редакторов и автоматического слияния веток с логическими (а не символьными) изменениями а также — разрешения конфликтов. Тогда ифкуиль и его подмножество будут соотноситься как SGML и XML.

                                          • 0
                                            без фонетики… только смысл, воспринимаемый с экрана

                                            Для этого придуман ихьтаиль (içtaîl) — оригинальная система письма ифкуиля :). Графический вариант, действительно, считывать проще, хотя семантические корни, основанные на котрежах согласнных фонем (да и вообще — фонотактика) и разноплановые сочетания многомерных грамматических матриц никуда не деваются.
                                        • +2
                                          Кто ж спорит, однако у этой гармоничности есть своя цена. В качестве аналогии

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

                                          • 0
                                            На провокационные вопросы не отвечаю :). Однако обращу внимание, что в данной теме речь не о каком-нибудь хаскеле (который по сравнению с «массовыми языками» скорее непривычен, чем сложен), а про вполне конкретную Аду. Кроме того замечу, что знаком с упомянутыми языками весьма поверхностно и все мои высказывания по их поводу следует воспринимать не иначе, как личное мнение при отсутствии профессионального опыта применения.
                                            • +2
                                              P.S. Кстати, я не считаю Ada сложным языком (учитывая, что в своё время съел собаку на Object Pascal) и поэтому, если бы, вдруг, пришлось включаться в проект на этом языке, не морщился бы и не плевался. А вот, например, сказать, что достаточно хорошо знаю C++ (несмотря на то, что программирую на на нём много лет), я бы, положа руку на сердце, не рискнул ;).
                                    • 0
                                      Что касается «новых фич», уверяю, ни в одном из языков (в классической вычислительной парадигме) никогда не будет «новой фичи», которой бы до этого не было в Lisp.

                                      LISP до начала стандартизации Common LISP или вообще весь со всеми диалектами? (тогда — нечестно)
                                      Системы типов с автовыводом, а-ля ML, Haskell?
                                      Или это уже не "классическая вычислительная парадигма"?

                                      • 0
                                        Я скорей подразумевал Lisp как унифицированный синтаксический ассемблер (в том смысле, что программа на нём имеет максимально «плотный» доступ к своему синтаксическому дереву). Поэтому при большом желании в Lisp можно реализовать любую идею (в том числе и статический вывод типов) не выходя за рамки языка (кроме «синтаксического сахара» — всякий реализованный принцип будет иметь специфический лисповский видок :)).
                                  • +1
                                    >>Насколько мне известно, для серьёзных бизнес-задач серьёзные конторы используют специализированные языки, вроде SAP ABAP

                                    «Бизнес-задачи» бывают разные. «Бизнес-задачи» — это не только то, чем занимается «коммерция» и «бизнес».

                                    Не путайте конкретные учетные задачи, на которые заточены компоненты SAP и задачи автоматизации бизнес-процессов.

                                    Например АСУТП писать на SAP — это надо сильно удариться головой.
                                    Не стоит писать систему реального времени контроля за грузоперевозками в масштабе страны на SAP. Можно, но в здравом уме это никто делать не будет. Потому там майнфреймы IBM, DB2, Java.

                                    А вот бухгалтерию и зарплату под объемы РЖД — самое дело — SAP там лег как родной) (а на самом деле просто потому, что 1С не тянул эти объемы).

                                    ABAP хорош только в контексте учетных механизмов SAP, а как сам язык, сужу по отзывам — не очень катит супротив даже 1С.
                                    ABAP — конкурент 1С, а не Java/С++…
                    • +1
                      Вы знаете, я читал еще самые первые документы по Ada. Так уж сложилось — я как раз делал диплом, и записался в ГПНТБ, и почитывал все что попадалось интересного. Так вот — то что было изначально заложено, это опыт разработки сложных систем на тот момент. Т.е. грубо говоря — 70-е годы прошлого века для первой версии языка.

                      С тех пор много воды утекло, очень много. Что-то конечно в Ada меняли, но что-то и осталось с тех пор. А взгляды на то, как нужно делать сложные системы, они с тех пор могли и поменяться — сложность-то поменялась.

                      Я к тому, что наличие формальных требований — это не догма. В этой области тоже много чего изменилось с тех пор.
                      • 0

                        В Ada тоже добавляют новые фичи, последний стандарт вышел в 2012-м году. Большинство широко используемых сейчас современных языков появились в конце 80-х — начале 90-х. В мейнстримовых языках вообще-то довольно трудно найти какую-то концепцию, которой меньше 20-ти — 30-ти лет.

                    • +4
                      Ада — одно из величайших разочарований в моей жизни :)
                      Хорошо составленные требования были реализованы без какого-либо анализа. Есть требование — вводится соответствующая конструкция.
                      В результате, описание синтаксиса Паскаля укладывается в 30 определений, а Ада потребовала 180. Держать в голове такой компилятор не удается, а без этого производительность программиста резко падает.
                      Так что, «хорошо обоснован» еще не означает «хорошо сделан».
                      • 0

                        Интересно тогда, во сколько определений укладывается описание C++ или Java, или Rust. Подозреваю, что не сильно меньше. При этом, например в случае с C++, всяких undefined behaviour и подводных камней явно больше.

                        • +1
                          По моему, никто и не пытался составить формальное описание С++. Дело в том, что С++ создавался без формализованных требований. В первых изданиях своей книги Страуструп подробно описывал, что пытался (и не сумел) сделать. Ему требовался инструмент имитационного моделирования. Описание Симулы-67 он нашел, а работающую реализацию нет. Он стал дописывать С, постепенно добавляя необходимые механизмы. Работа осталась незавершенной. Но появились пользователи и первоначальная цель была отставлена.
                          ЗЫ: Борланд делал компилятор С++, там в документации, что-то похожее на формальное описание было. На полсотни страниц.
                          • 0

                            Ну вот, к примеру, стандарт C++ 2011 — 1336 страниц. Причём за него надо будет ещё заплатить.

                            • 0

                              Это не формальное описание. Формальное описание — это строго математическое описание в терминах, скажем, денотационной или операционной семантики.


                              Как пример попыток.

                            • +1
                              Это совсем другое дело.
                              Давным давно, я пытался доказывать, что язык, это не только синтаксис, но и словарь. И среди языков выиграет тот, в стандарт которого будет включена обширная библиотека.
                              Собственно, это и случилось. При сложном и, я бы сказал, неопрятном синтаксисе, С++ стал популярен, благодаря стандартизации библиотек. Это, действительно, требует сотен страниц описаний.
                              • +1

                                Кстати такой же стандарт ISO/IEC по Ada 2012-го года почти на 400 страниц меньше — тоже с описанием библиотеки.
                                Судя по тому, что для С++ придумано много сторонних библиотек типа Qt, Boost и т. п., со стандартной библиотекой там как раз не всё гладко.

                                • 0
                                  Встроенные библиотеки, библиотеки сторонних разработчиков, библиотеки команды разработчиков и личные библиотеки программиста всегда были есть и будут. Идет миграция функций из частных библиотек в общие, но конца ей нет.
                                  Я говорил только о том, что в «языках первого поколения» описывался только синтаксис, а встроенная библиотека отдавалась на произвол конкретной реализации языка.
                                  Как только встала задача перехода к индустриальному производству программ и обеспечении реальной переносимости ПО, стандартизация встроенной библиотеки на уровне языка, а не его реализации, стала необходимостью.

                                  Некоторое противоречие в том, что по единству и компактности синтаксиса предпочтительнее «авторские» языки — созданные одиночками. Но создание достаточно обширной и хорошо структурированной библиотеки одиночке не под силу.
                                  Поэтому и были надежды на проект Ада: за основу берется «ортогональный» синтаксис Паскаля, позволяющий почти все минимальным количеством конструкций, и дополняется обширными хорошо оттестированными библиотеками, создающими следующий слой языка. Но расширение синтаксиса, оказалось необходимым и сделано оно было крайне неряшливо. Не просто дополнение несколькими новыми конструкциями, а многократное увеличение их количества.
          • +15
            прямых доказательств нет и не будет))) зато есть куча косвенных. умеющий уши да слышит))) шучю. но ситуация примерно такова и есть, имхо.

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

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

            я говорю о методиках оценки типа FPA (метод баллов функциональности), COCOMO / COCOMO 2, UCP (usecase point analisys? — как FPA, но только принимает на вход прецеденты использования.).

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

            и тут такая особенность: институты, которые разрабатывают эти методики нацелены на промышленные, «серьезные области» — собственно там, где методы оценки трудоемкости очень важны. те области, где сбой в работе по имеет серьезные экономические или даже материальные последствия. или где комплекс программ сложный а процессы — часто запутаны. где проблем много, даже если системы не обрабатывают массовые заявки (т.е. гуглемейл или вконтактик с одноклассниками и фейсбуками например — они не показательны ни разу — это не промышленная разработка. они массовы, огромны, но их «бизнеспроцессы» достаточно примитивны и потому легко масштабируются. они очень просты по сути, даже в сравнении с обычной СЭД, или системой бухгалтерского или складского учета).

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

            и в результате, ничего что касается языков с «динамической» типизацией, я в исследованиях больших институтов я не видел. были коэффициенты для C/C++, Java, Paskal. даже бейсик кажется был. но джаваскрипта, руби, питона там я не помню.

            занимался исследованием методов оценки лет 5 назад, может сейчас что и изменилось, поправьте если что))

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

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

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

            (например, рубисты вынуждены ренерить xml кож вручную, и вручную писать правила проверки, что бы xml соответвовал требуемому xsd; на джава же этой проблемы нет вообще — на основе xsd кодогенератором делаются классы с анотациями, и при парсинге/сериализации все правила xsd проверяются автомтатически == следовательно на джава разработать алгоритмы для wsdl-вебсервисов быстрее).

            или другой прмер: при рефактоинге кода, изменнии структуры классов и их методов, для динамических языков нет методов которые позволили бы понять, где используется изменяемый класс. что бы выявить все эти места, нужно проводить масштабное тестирование, что трудоемко. а в случае языков со статической типизацией — все места использования класса, типа, метода проверяются сразу компилятором, и более того — IDE спомбна выполнить 90%работы по анализу кода за нас. == рефакторинг кода написанного на ryby/javascript это более трудоемкая процедура, чем скажем рефакторинг кода на java илиc++.

            логические рассуждения, не больше)))

            по поводу опыта могу вам сказать так: только через 8-10 лет после института, я начал понимать почему, например, на PHP очень геморройно делать большую сложную систему типа СЭД. и что проблема не в пряморукости, а в глубинных свойствах самого языка и инструментария.

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

            то что вы скептически относитесь к утверждениям, что статическая/жесткая типизация помогает контроллировать код — мне очень понятно. я сам был таким))

            но поразмыслите детально, и педантично.
            • +1
              PS: Прошу прощения за очепятки и «прямоток» без заглавных букв.
            • 0
              Мы вроде говорили о сильной/слабой, а не о статической/динамической типизациях. Ортогональные друг другу вещи, тот же Руби, о котором вы часто пишете, — язык с сильной, хоть и динамической, типизацией. Как и Python, например. При этом в C++ типизация слабая, но — статическая.

              > я сам был таким))
              Не очень понятен этот снисходительный тон.

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

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

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

                «Строгая типизация == раз скомпилировалось, значит работает» — а где здесь про скорость разработки? Или про «бизнеса/заказчика в различных контекстах применения»?

                Похоже, Вы сами в какой-то момент подменили тезис на «лучший язык — со статической типизацией», который и опровергаете.
                • 0
                  Во-первых, опять же, при чем тут статическая типизация? Речь в комментарии идет о сильной.
                  И я ничего не подменял. Вы цитируете мой комментарий, который является ответом уже на совершенно другое высказывание. Не надо демагогией заниматься.
                  • –2
                    Во-первых, вы не ответили на вопросы. Так с каким именно тезисом из какого комментария вы спорите то?
                    • 0
                      Я вообще не спорю. Я дискутирую.
                      Прочитайте еще раз комментарий, на который я отвечал, и вопросы у вас отпадут.
                      • –2
                        То есть по делу ответить нечего, так и запишем.

                        > Я вообще не спорю. Я дискутирую.

                        > ДИСКУ́ССИЯ, -и, жен. Спор, обсуждение какого-н. вопроса на собрании, в печати, в беседе.

                        > СОФИЗМ (от греч. sophisma — уловка — выдумка, головоломка), мнимое доказательство, в котором обоснованность заключения кажущаяся, порождается чисто субъективным впечатлением, вызванным недостаточностью логического или семантического анализа.
                        • 0
                          Вы сейчас демагогией занимаетесь. Повторяю: прочитайте комментарий, на который я отвечал, и ваши вопросы отпадут.
                          • –2
                            Уже отпали, что уж. Argumentum ad nauseam на мне можно не практиковать.
                            • –1
                              Какой вы интересный, однако. Такое ощущение, что вы сюда в демагогии приходите упражняться =)
                              Всего доброго!
                              • 0
                                Повторяю, обвинять меня в демагогии можно сколько угодно, но от прямого ответа уклонились вы.
                                • 0
                                  Давайте я объясню попроще. Вы процитировали мой комментарий, подразумевая, что он является ответом на комментарий yarric, в то время, как на самом деле он является ответом на комментарий dplsoft. Ваш вопрос (и обвинение в том, что я выдумываю какие-то тезисы и с ними спорю) основывается на ложной предпосылке. Я указал на это. Вы настаиваете и снова обвиняете меня в уходе от ответа. Это называется демагогия =)

                                  Я в очередной раз рекомендую вам ознакомиться с комментарием dplsoft. Я отвечал на этот комментарий, и ни на какой другой.
                                  • 0

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

                                    • 0
                                      О, а это уже прогресс!) Осталось совершить последние усилие над собой — и таки прочитать тот самый комментарий, на который я отвечал!
                                      • 0
                                        Прогресс? То, что вы передёргиваете и занудствуете было понятно сразу. Там же я написал, что вопросов больше не имею. Что-то ещё?
                                        • 0
                                          И это я-то передергиваю) Оставлю это на вашей совести =) Еще раз — всего доброго!
              • +4
                Мы вроде говорили о сильной/слабой, а не о статической/динамической типизациях.
                Ах… Гм… Был спросонок, перепутал. Погуглил. Уточнил. Осознал :)

                Крайне редко говорят про сильную и слабую типизацию.
                Вы первый или второй. Обычно говорят про динамическую-vs-статическую типизацию.

                Особой корелляции между сильной-слабой типизацией и проблемами с разработкой не отмечал.

                Проблемы с динамической типизацией изложил — с ростом объема кода, проблемы в разработке на динамических языках прастут «очень не линейно». Проблемы со статической типизацией — относительно линейно. Это мои оценки и наблюдения.

                И до тех пор, пока не будет исследований, показывающих, какая типизация действительно лучше с точки зрения, подчеркиваю, бизнеса/заказчика в различных контекстах применения, я всегда на подобные споры и попытки их разведения буду смотреть осуждающе.
                Тогда у нас дед-лок))
                Исследований нет, и наверное не будет, ибо некому за это заплатить. нет — исследований — будет постоянное осуждение. Будет осуждение — проблему скорее всего не решить. Но принимать-то архитектурные решения надо! И стажеров надо обучать! Как быть?

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

                С моей же точки зрения, если выбирать между «статикой и динамикой» в типизации при выборе языка разработки, то мой опыт говорит мне следующее: выгода зависит от объема разрабатываемого кода. ( и я сейчас буду говорить только выгодах проекта, вытекающих из минимизации трудозатрат и технических рисков. Если последствия неадекватных технических решений будут лежать на ком-то другом, или «бизнес» планирует «отскочить» до проявления проблем или «еще как-то» (вплоть до полного наплевательства на последствия, или полного одупления (непонимания лицами принимающими решения технических проблем) ) — то «бизнес», естественно, будет принимать решения совсем с других, не связаных с техническими аспектами, позиций )

                и так:
                * на коротком периоде, для небольших задач, одноразовых, скриптования поведения «жесткой части» — удобнее/выгоднее динамическая типизация. Низкий порог входа, быстрое прототипирование — позволят экономить бюджет. До тех пор, пока кривая проблем не начала расти (не сдвинулась с места), или не начался рефакторинг, или число модулей в вашей системе не выросло до двух.
                Отсюда и область применения: скрипты инициализации, тестирование, небольшие хранимки в бд, сайты с линейной логикой работы, различные примитивные СМО и пр.

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

                А корелляции между «сильной» и «слабой» типизацией я не заметил.

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

                я сам был таким))
                Не очень понятен этот снисходительный тон.
                Я принял вас за очередного молодого адепта «новомодных языков». Извиняюсь)
                • +7

                  На самом деле, в современных языках статическая типизация оказывается даже быстрее при написании скриптов, потому что:


                  1. на надо гуглить какое там апи у такого-то модуля — среда разработки сама адекватно всё подскажет.
                  2. не надо запускать и дебажить для того, чтобы обнаружить глупую опечатку — компилятор и даже среда разработки понимают, что ты написал, и как можно раньше рассказывают тебе о проблемах.
                  3. не надо постоянно вручную проверять и преобразовывать типы, на случай, если на вход передадут какую-то дичь. Типичный пример: String( arg ).toLowerCase()
                  4. типы вручную пишутся только там, где пользователю это важно — в остальных местах они выводятся автоматически
                  • 0
                    > на надо гуглить какое там апи у такого-то модуля — среда разработки сама адекватно всё подскажет.

                    IDE с поддержкой динамически типизированных языков уже существуют и активно развиваются.

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

                    Аналогично, для динамически типизированных языков есть средства линтинга, и профессионалы не выпускают код без него. Для Python я с ходу могу назвать 4 таких средства, для Javascript — 2, для многих других есть встроенные анализаторы (например, в Perl исключение по use strict генерируется именно таким контролем, а не ошибкой собственно рантайма типов).

                    Но есть таки принципиальная разница. Грубо говоря, для статически типизированных языков то, что вычисляется при компиляции, будет аксиомой при выполнении (объект типа X => у его методов будет именно та сигнатура, что у класса X). Для динамически типизированных это не обязательно — Python, Javascript позволяют подменять подложку реализации на ходу. Линтинг в принципе не способен отловить такие ситуации. Их запрет для возможности статического анализа — уже вопрос административной политики при разработке, а не собственно языка.

                    > не надо постоянно вручную проверять и преобразовывать типы, на случай, если на вход передадут какую-то дичь. Типичный пример: String( arg ).toLowerCase()

                    Да, фактор существенный. Мне в этом смысле понравились правила от Hola для Javascript: например, если требуется, чтобы на входе функции было число, пишется +x, а если строка — ""+x. Рантайм умеет опознавать эти ситуации и переводить их в конверсию типа.
                    Но вот lowercase это уже за пределами обычной возможности системы типов, надо делать свой враппер.

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

                    Пока что это достаточно малая часть таких языков. И даже в них автоматический вывод работает не всегда и не везде адекватно. А главное — что для уверенности программиста, что тип получился именно нужный, приходится его указывать явно.
                    • +2
                      IDE с поддержкой динамически типизированных языков уже существуют и активно развиваются.

                      Выглядит это примерно так:


                      image


                      Аналогично, для динамически типизированных языков есть средства линтинга

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


                      И даже в них автоматический вывод работает не всегда и не везде адекватно.

                      О чём опять же программисту сообщается сразу, а не когда в рантайме прилетит что-то не то.


                      А главное — что для уверенности программиста, что тип получился именно нужный, приходится его указывать явно.

                      Как правило программисту всё-равно какой там тип, лишь бы крякал как утка.

                      • 0

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

                        • 0

                          Наоборот, они по определению контролируют только форму, но не содержание.

                          • 0

                            Это всё демагогия. Линтером сейчас называют любой статический анализатор. По ссылке статический анализатор, проверяющий семантику кода, то есть содержание. Eslint для JavaScript — линтер, в основном проверяющий стиль написания, то есть форму.

                            • 0

                              "сообщал о подозрительных или непереносимых на другие платформы выражениях" — не тянет это на семантику.

                              • 0

                                А что же это, если не семантика? В понятие синтаксиса уже не влазит.

                                • 0

                                  Но и до семантики как до луны. Типа "ставьте точки с запятой в конце строк" — это синтаксис, а "не используйте arguments" — это уже семантика?

                                  • 0

                                    Мнэээ… перед этим шла речь о непереносимых выражениях, например. Если кто-то напишет x<<200, где x типа int, что это, как не проблема семантики (содержания, смысла… проигнорируем сверхтонкие отличия)? Ну или пора вводить новую систему терминов.
                                    Что за arguments тут — я не понял. Имеется в виду массив всех аргументов функции в JS? Вот тут, да, это настолько специфично для языка, что ближе к синтаксису, чем к семантике. Но я таки предпочёл бы тут видеть какое-то новое слово...

                                    • 0

                                      Вы правы. К сожалению для JS линтеры проверяют в основном довольно бесполезные правила.

                      • 0
                        Выглядит это примерно так:

                        Ну да. А вы считаете это недостаточным?


                        Линтеры тут ничем не помогут.

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


                        Если же речь не о линтерах, а тайпчекерах, то для них опять же нужны тайпхинты,

                        Если такое средство видит, что параметр используется как индекс в списке, оно предположит, что тут должно быть целое. Если его индексируют строкой — оно должно быть совместимым со словарём. И так далее. Явные хинты тут не обязательны. Хотя, да, желательны — я бы предпочёл их видеть много где — но в последних версиях Питона, например, это уже делается.


                        О чём опять же программисту сообщается сразу, а не когда в рантайме прилетит что-то не то.

                        Это если он не сумел вывести тип. А если вывел, но не тот, что думал программист?


                        Как правило программисту всё-равно какой там тип, лишь бы крякал как утка.

                        Так в том и дело, что он хочет утку, а получается вдруг амадина. Она тоже крякает, но как-то невыразительно :)

                        • 0
                          Ну да. А вы считаете это недостаточным?

                          Я считаю это бесполезным, чуть менее, чем полностью.


                          С Питоном у меня это реально работало.

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


                          Это если он не сумел вывести тип. А если вывел, но не тот, что думал программист?

                          Он не может "не суметь". Всё, что он может — вывести тип, не совместимый с другим кодом и упасть. Если не упал, значит тип совместим и не важно, что там думал программист. Например, он мог думать, что вернётся User, а вернулся Proxy, который реализован так, что мимикрирует под User. Ну и какая программисту разница, если всё работает корректно?

                          • 0
                            Я считаю это бесполезным, чуть менее, чем полностью.

                            Почему бесполезно?
                            Оно подсказывает, какие слова тут можно подставить, и умеет их дополнять. Также — показывает имена и типы аргументов, по которым можно понять, что и как задавать.
                            Оно может анализировать код и показывать ошибочные (например, опечатка в имени метода) или подозрительные конструкции.
                            Я не знаю, может, Вы имеете в виду какую-то злобную специфику JS. Я с ним слишком мало работал, чтобы знать такие тонкости. Но для Питона и для >95% случаев меня функциональность таких средств устраивает; когда она не работает — уже описывал раньше — когда я сам как автор кода вмешиваюсь в логику и делаю её изменчивой в рантайме.


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

                            В большинстве случаев — да. Я не могу, например, писать


                            if isinstance(max_time, str):
                                max_time = int(max_time)

                            да и безусловную конверсию лучше не применять (хотя если средство хоть чем-то умнее крышки дубового стола, оно будет работать в логике, аналогичной Static Single Assignment).


                            Или, я не смогу использовать присвоение некоторой переменной одного из трёх значений True, False или None (у меня это долго был любимый приём) — оно выведет тип для этого значения, только если придумает внутри себя enum, а так как все три в Питоне это синглтоны двух разных типов, ему будет сложновато это сделать.


                            Но таких случаев в реальной практике оказалось ой немного. А вот простые варианты типа "в этой переменной всегда целое" оно вывело бы на ура. Точно так же как упомянутая рядом схема Хиндли-Милнера работает в ML и семействе.


                            Он не может "не суметь".

                            См. выше пример с конверсией в целое на ходу. Вывод типа "местами снег, местами град, местами variant — или int, или str" и есть то "не шмогла".


                            Ну и какая программисту разница, если всё работает корректно?

                            Разница в том, что мимикрия подобного рода означает слишком динамическую типизацию. И в Питоне, и в JS это поиск по названию метода в цепочках словарей методов суперклассов (прототипов).


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

                            • 0
                              Почему бесполезно?

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


                              показывает имена и типы аргументов,

                              Только для стандартных API и для JSDoc аннотаций (привет, статическая типизация)


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

                              Эту задачу решают "типы-суммы" и "типы-произведения". TypeScript, например, это умеет. При этом он умеет ограничивать такие типы в разных ветках кода:


                              let foo : number|string = Math.random() > .5 ? '0' : 0
                              if( typeof foo === 'number' ) {
                                  console.log( foo.toFixed(2) )
                              } else {
                                  console.log( foo.toLowerString() )
                              }

                              Вывод типа "местами снег, местами град, местами variant — или int, или str" и есть то "не шмогла".

                              Как программист написал — так оно и вывело. Или приведите пример, что ли.


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

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

                              • 0
                                Потому, что выдаёт вообще всё, что угодно, кроме того, что там в объекте действительно есть.

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


                                Только для стандартных API и для JSDoc аннотаций (привет, статическая типизация)

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


                                Эту задачу решают "типы-суммы" и "типы-произведения". TypeScript, например, это умеет.

                                Хорошо, значит, прогресс в эту сторону идёт.


                                Скорее динамическую диспетчеризацию (которая приятно дополняет статическую типизацию).

                                Да, это правильный термин для данного случая.

                                • 0
                                  Насколько я вижу, всё, что он выдал там, действительно есть в объекте

                                  Нету там этого. Уж поверьте мне на слово :-) Он просто нашёл левый файл, где встречается одноимённый ключ объекта:


                                          express:{
                                              tasks:['express:src'],
                                              options:{
                                                  spawn:false
                                              },
                                              files:["**/*.js"]
                                          },
                            • 0
                              Разница в том, что мимикрия подобного рода означает слишком динамическую типизацию. И в Питоне, и в JS это поиск по названию метода в цепочках словарей методов суперклассов (прототипов).

                              А если в хаскеле выведется Userlike a, то это статическая или динамическая типизация? Словарик с методами-то таскать придётся!

                    • +3
                      Пока что это достаточно малая часть таких языков. И даже в них автоматический вывод работает не всегда и не везде адекватно.

                      Хиндли-Милнеру лет 30. Работает адекватно во всяких ML'ях и прочих хаскелях и не требует аннотаций в подавляющем большинстве случаев.


                      А главное — что для уверенности программиста, что тип получился именно нужный, приходится его указывать явно.

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

                      • 0

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


                        Типы (топ-левел-функций) лучше указывать потому, что типы — это документация, да и явное указание типов позволяет сделать более читабельные сообщения об ошибках типизации (вы накладываете больше констрейнтов, у тайпчекера меньше вариантов).

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

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

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


                          Да, это тот же самый принцип, что я описал абзацем выше.

                          Ну, учитывая вышесказанное — не совсем.


                          Но и типы промежуточных переменных — это такая же документация

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

              • +3

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

          • +2
            Лехко. Какие-то странные люди, которые до этого делали Delphi, а затем c#, придумали TypeScript. Так вот сам попробовал: 90% глюков отлавливается на этапе компиляции остается только вопрос неправильнлого алгоритма.
            Ещё одни странные люди, сменив версию Ангулара с 1 на 2, тоже перешли на TypeScript (жесткая типизация). Наверное, им надоело есть кактус.
          • +2

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

        • 0

          Строгая типизация не означает, что в рантайме у вас внезапно что-нибудь не отвалится)

      • +3

        В C++ нет строгой типизации.

        • +3
          Строгость типизации — понятие достаточно относительное. По сравнению с JavaScript, где преобразование между числом и строкой — норма, C++ строго типизирован, а по сравнению с Go, где даже если int 32-битный, то преобразовывать между int и int32 можно только явно — нестрого типизирован.
          Поэтому, при рассмотрении строгости типизации лучше вводить несколько канонических уровней и сравнивать с ними:
          1. Вообще никаких неявных конверсий (Go, или близко к нему).
          2. Конверсии по умолчанию — между числами, или только явно разрешённые функции, или только в особых контекстах (основной компилируемый майнстрим, C++, C#, Java и т.п.)
          3. Размыта граница между строкой и тем, что она представляет (JavaScript, Perl...)
          4. Вообще всё есть строка, оптимизации есть, но это принципиально не отменяют (sh, Tcl...)
          • 0

            Ну, я бы не стал ставить C++ в один ряд с C# и Java по строгости типизации при всей моей любви к нему, и конверсии между разными скалярными типами — не единственный аргумент. Больше непроверяемых кастов, операторы приведения и implicit-конструкторы, всякое такое счастье.


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

      • 0
        не могут. С++ не является строго типизированным языком
    • +1
      Есть случаи когда человек так и остается «новичком» используя только короткие шаблоны языка/тулзы/фрейморка.
    • –1

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


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

      • +1
        А можете привести несколько примеров языков, «которые как-будто делают с целью упрощения написания хеллоу-ворлд-ов и написания красивых, но далёких от жизни туториалов»?
        • –2

          Синтатиксис и семантика примера из статьи демонстрирует аналогичный код на многих современных распространённых языках программирования.

          • +3
            Это понятно из статьи, спасибо. Я прошу привести примеры «нового Того-самого-лучшего-языка», желательно несколько. А то пока обсуждаются отличия Ады от чего-то абстрактного.
            • –1

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

              • +3
                Речь не о человеке, не о религии, и т.д. Мы говорим о языках программирования — продуманных системах, спроектированных и использующихся множеством людей. И разговор происходит на техническом ресурсе для профессионалов.

                А пока что вы в статье сравниваете конкретный язык с придуманным негативным кейсом. Очень знаете ли похоже на «Ариэль вс. Обычный стиральный порошок».
                • –1

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

                  • +3
                    Я не хочу ничего приводить, я ничего не утверждал о хорошести или плохости других языков. Я хочу увидеть от вас пример того, что, как вы утверждаете, имеет место быть.
                    • –1

                      Пример языка, в которых используется обработка ошибок на основе условий и исключений, как в примере? Java, C#, C++ и т. д.

                      • +2
                        Т.е. вы утверждаете, что Java, C#, C++ — это те, «которые как-будто делают с целью упрощения написания хеллоу-ворлд-ов и написания красивых, но далёких от жизни туториалов»?

                        Ясно, спасибо, ваша мысль ясна.
                        • –2

                          Java, C#, C++ — примеры языков, в которых используется обработка ошибок на основе условий и исключений, если с первого раза было не понятно.

                          • +5
                            А ада с помощью духа святого выходит из щекотливых ситуаций, стало быть?
                            • 0

                              Ada ещё добавляет проверки диапазонов на основе типов данных.

                              • +2
                                А это как и зачем?
                                Запретить вводить людей младше года и старше 120 — это совершенно искусственный пример, хуже Hello World. А в реальности это зачем?
                                • –1

                                  Лучше писать проверки диапазона руками в коде? Возможно, дело вкуса.

                                  • 0
                                    Это из серии «предполагается, что новичку, заинтересовавшемуся языком, понравится очередной упрощенный вариант»?

                                    Не знаю на счёт «лучше», но после «вынесите границы в настроечки!» вписывать эти проверки точно не хочется.
                                • –1

                                  У вас в любом случае будут ограничения диапазонов. Заданные вами или компилятором или архитектурой процессора. Максимально компактные диапазоны позволяют оптимизировать вычисление и хранение данных, обнаруживать большее число ошибок. Разумеется число 120 неоправданно занижено. А вот диапазон {0… 256} выглядит вполне разумно на ближайшие 100 лет.

                                  • 0
                                    Задавать возраст таким образом — готовый антипример. Особенно в 21 веке, когда проблема 2000 далеко позади, а проблема 2038 приближается.
                                    • 0

                                      И что вы имеете против такого задания возраста?

                                      • +1
                                        Это приблизительно как придумать свою реализацию целочисленной арифметики, причём на bash и в столбик. И это в 21 веке. Возраст человека измеряют от дня рождения, так что детализацию возраста, если по уму, нужно делать до дня. А чтобы не наступать на грабли типа 29 февраля, лучше брать стандартное, с детализацией до секунды.
                                        • 0

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

                                          • +1
                                            Вот зачем-то в программе делается сравнение возрастов. Алиса и Боб родились в один год. Алиса родилась 1 января. Боб родился 31 декабря. Кто из них старше? В модели, где все округляется до года, они одного возраста.
                                            • 0

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

                                              • 0
                                                Ок, пусть будет, тем более что это хелловорлд :). И какой профит в использовании такого типа-поддиапазона int? Компилятор сгенерирует код, который при каждом присваивании будет проверять диапазон и кидать exception? А для float так тоже можно? А диапазон может состоять из поддиапазонов? А если поддиапазонов maxint/2? А если присваивание в цикле?
                                                КМК, лучше условия проверять явно и там где это необходимо.
                                                • 0
                                                  Все зависит от того в скольких в местах вам проверить нужно. Смотрите на єто на как VO. Раз он создался, значит он 100% валидный.
                                                • 0

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

                                                  • 0
                                                    Я программист ненастоящий… И подобная компиляторная магия, как по мне, особенность, которой лучше избегать. Если нужна проверка входных данных — почему бы её не сделать как нужно? Например, если логика требует не exception, а специального кода возврата? Если сгорел датчик, например — перезагружаться?
                                                    • 0

                                                      try-catch позволит вам обработать нештатную ситуацию по своему.

                                                      • 0
                                                        Теперь понятно, откуда берутся программы, внезапно кидающие невнятный exception, но продолжающие как бы работать.
                                                        • 0

                                                          У вас какое-то странное представление об обработке исключений.

                                                    • 0

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

    • –24
      ПОМОЕМУ МОРАЛЬ ВПОЛНЕ ВЫВЕДЕНА В КОНЦЕ СТАТЬИ АЛЕ
  • +3

    Хорошо, а покажете, как будет выглядеть на Аде такой код?


    const MALE = 'M';
    const FEMALE = 'F';
    const PENSION_AGE = {
        [MALE]: 65,
        [FEMALE]: 60
    };
    
    const pensioners = persons.filter(person => person.age > PENSION_AGE[person.gender]);
    
    console.log('Всего пенсионеров:', pensioners.length);
    
    pensioners.forEach(pensioner => {
        console.log(pensioner.name);
    });
    • –14

      А зачем на Аде писать говнокод?


      1. persons должен уже содержать нормализованные объекты, а не сырые данные, полученные с сервера. Иначе при изменении ответа сервера вам придётся рефакторить код всего приложения. А поддержать несколько серверов вообще не представляется возможным.
      2. Для итерирования есть удобные циклы с полноценной поддержкой break и continue. Незачем лепить замыкания на ровном месте.
      • +4
        1. Не всегда программист может влиять на серверный код. Например, сервером может выступать API «одноклассников» и задача — посчитать количество пенсионеров в друзьях.

        2. Я много программировал до и после удобного использования замыканий. Разница огромная. Где раньше надо было написать портянку циклов с break, continue, и пятёркой вспомогательных переменных, сейчас без напряжения мозга просто пишешь, что ты хочешь получить, сберегая мыслительные ресурсы для остальной части задачи.

        Как по мне, поддержка запросов к коллекциям сейчас является необходимой чертой современного языка. Даже мастодонты java и c++ сдались и ввели эту поддержку. Это как раньше были языки без поддержки рекурсии и динамической памяти, но вымерли.
        • –8
          1. О том и речь, что программист должен изолировать от приложения то, на что он не влияет — ввод пользователя и ответ сервера.


          2. Циклы не требуют каких-то особых "мыслительных ресурсов". Написанный вами код ничем от циклов не отливается. Ну вот совсем. Только отлаживать его — то ещё "удовольствие". ни промежуточные результаты выполнения не посмотреть, ни по шагам пройтись, ни даже прочитать содержимое переменных из родительского скоупа, на которые нет ссылки из вложенного (хвала JIT).

          А как по мне — обобщённое программирование и исполнение кода времени компиляции являются необходимыми чертами современного языка :-) А ещё, как по мне, forEach — не является "запросом к коллекции", а является он обычным циклом, но зачем-то сделанным на редкость неудобным.

          • +6
            Циклы не требуют каких-то особых «мыслительных ресурсов». Написанный вами код ничем от циклов не отличается
            Банально устаёшь писать длинные тупые циклы, где нужно найти сумму элементов, максимальный элемент, минимальный среди максимальных. Внимание ослабевает, сажаешь ошибки. Проще написать запрос в декларативном стиле, что тебе нужно от данных, и идти дальше, не заостряя внимания на подобных задачах.
            Только отлаживать его — то ещё «удовольствие». ни промежуточные результаты выполнения не посмотреть, ни по шагам пройтись, ни даже прочитать содержимое переменных из родительского скоупа, на которые нет ссылки из вложенного (хвала JIT).
            Конкретно в Visual Studio (C#/C++) в режиме DEBUG нет никаких проблем: брейкпоинты можно ставить на любой оператор внутри лямбды, переключая контекст в окне Call Stack, можно добраться до любых переменных в watches. Если где-то это не так, это проблема IDE, а не подхода в целом.
            • +4
              К сожалению, это правда.
              Два года разработки на golang, где все на циклах и очень мало обобщений, и сейчас отдыхаю, изучая python. Да, он медленнее, не такой клевый для параллельного кода, но насколько же быстрее попробовать какую-то идею! Одна-две строки, вместо десятка строк кода, который может быть и выкинешь после…
            • 0
              Проще написать запрос в декларативном стиле, что тебе нужно от данных, и идти дальше

              const users_by_name = users.reduce( ( index , user )=> {
                  const name = `${ user.name_first() } ${ user.name_last() }`
                  return { ...index , [ name ] : user }
              } , {} )
              
              const users_by_age = users.reduce( ( index , user )=> {
                  const age = user.age()
                  return { ...index , [ age ] : [ ...( index[ age ] || [] ) ,  user ] }
              } , {} )

              const users_by_name = {}
              const users_by_age = {}
              
              for( let user of users ) {
                  const name = `${ user.name_first() } ${ user.name_last() }`
                  users_by_name[ name ] = user
              
                  const age = user.age()
                  if( users_by_age[ age ] ) users_by_age[ age ].push( user )
                  else users_by_age[ age ] = [ user ]
              }

              Конкретно в Visual Studio (C#/C++) в режиме DEBUG нет никаких проблем

              Это замечательно, но человек привёл пример на JS.

              • 0
                Это замечательно, но человек привёл пример на JS
                То есть, с лямбдами писать пока рановато, потому что ваш IDE их хуже поддерживает, чем классический императивный код. Ну, ок.
                • –1

                  IDE тут, к сожалению, ни при чём. Это свойства языка / виртуальной машины.

                  • +1
                    Ставить брейкпоинты не на строку целиком, а на оператор внутри строки — это свойство IDE.

                    Не вижу никаких проблем сделать DEBUG-режим запуска VM, в котором будут фиксироваться стек-фреймы и их контекст. Чтобы дебаггер мог получить список стекфреймов, отобразить его в отдельном окне и двойным кликом переключать контекст так, чтобы при наведении мыши на переменные (или добавление переменных в watches) их значение показывалось в соотвествии с выбранным контекстом.
                    • 0

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


                      Это всё уже давно реализовано. Проблема "декларативного" fluent-кода, на который сейчас многие молятся, в том, что в нём у вас промежуточные вычисления находятся на стеке, а не помещаются в какую-то переменную.


                      image


                      Вот как тут узнать, что вернул filter?

                      • 0
                        Вот как тут узнать, что вернул filter?
                        Не знаю, как в вашей среде, но обычно в отладчиках можно выделить часть выражения
                        a.filter(i=>i>b).map(i=>i*i)
                        и нажать хоткей 'evaluate', или добавить в watches и исследовать там структуру выражения.
                        • 0
                          Я вам больше скажу: IDEA вполне позволяет ставить точки останова на лямбды. Если ты тыкаешь в строку типа приведенной выше — то у тебя спросят, куда именно поставить останов — на все выражение в целом, на filter или на map.

                          Если кто-то этого не умеет — это его проблемы, а не лямбд.
                          • 0

                            Особенное удобство — ставить точки остановка в каждой "лямбде", чтобы можно было пройтись по шагам. Ну или очень внимательно на каждом шаге выбирать между "step into" и "step over", а если ошибся — начинать заново.

                            • +1
                              Так у вас претензии к лямбдам, к fluent, или их к поддержке IDE/VM?

                              Вы пользовались IntelliTrace? Оно решает проблему с «на каждом шаге выбирать между „step into“ и „step over“, а если ошибся — начинать заново».
                              • 0

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

                                • +1
                                  Тут имеем выбор

                                  1) Написать понятный код, в терминах задачи, с использованием цепочки filter, reduce и прочих высокоуровневых конструкций (код, который очевиден и не требует дебага),

                                  либо
                                  2) Написать цикл, в котором происходит какая-то обработка. Человек, читающий код, всё равно должен будет прокрутить в уме выполнение кода и привести этот цикл в термины задачи, чтобы понять, что тут делается.
                                  • –1

                                    Вы правда считаете первый код "простым, понятным и очевидным"? Боюсь вы один такой, для обычных людей итеративный алгоритм проще, понятней и очевидней рекурсивного.


                                    Вы хотите сказать, что пишите код без ошибок, раз он у вас "дебага не требует"?

                                    • +1
                                      Вы про это?
                                      const users_by_name = users.reduce( ( index , user )=> {
                                          const name = `${ user.name_first() } ${ user.name_last() }`
                                          return { ...index , [ name ] : user }
                                      } , {} )
                                      

                                      Тут же нет рекурсии (если я правильно понимаю написанное).

                                      Ну и таки лично я подобное пишу без дебага, как
                                      users_by_name = users.ToDictionary(user => user.name_first() + ' ' + user.name_last());
                                      

                                      В JS и с этим проблемы?
                                      • 0
                                        Тут же нет рекурсии (если я правильно понимаю написанное).

                                        Под капотом, конечно, нет, но описание рекурсивное.


                                        Ну и таки лично я подобное пишу без дебага, как

                                        У массивов нет такого метода. Впрочем, не важно. Требования изменились, теперь надо искать как по имя-фамилия, так и по фамилия-имя. Ваши действия?


                                        Мои будут такими:


                                        const users_by_name = {}
                                        
                                        for( let user of users ) {
                                            const names = [
                                                `${ user.name_first() } ${ user.name_last() }` ,
                                                `${ user.name_last() } ${ user.name_first() }` ,
                                            ]
                                            for( let name of names ) {
                                                users_by_name[ name ] = user
                                            }
                                        }
                                        • 0
                                          users.Single(user => 
                                             user.name_first() + ' ' + user.name_last() == searchterm 
                                          || user.name_last() + ' ' + user.name_first() == searchterm)
                                          


                                          Ну, или ближе к вашему варианту
                                          users.SelectMany(user => new []{
                                          new KeyValuePair(user.name_first() + ' ' + user.name_last(), user),
                                          new KeyValuePair(user.name_last() + ' ' + user.name_first(), user)
                                          }).ToDictionary()
                                          


                                          > но описание рекурсивное.

                                          Не вижу
                                          • 0
                                            Тут не учтены повторы ключа. Если name_first == name_last, свалится с исключением «An item with the same key has already been added». Нужен Distinct перед ToDictionary
                                          • 0
                                            Решение с Single не подходит, линейный поиск. Неспроста же требуется построить словарь.
                                            • 0
                                              > Неспроста же требуется построить словарь.

                                              Незнаю-незнаю, удваивать размер словаря и одновременно оптимизировать… Если там следующим шагом отчество комбинаторно добавится — Single может выиграть )

                                              > Если name_first == name_last

                                              А так бывает? )
                                              Проще найти полных тёзок, там и ваш вариант упадёт.
                                              • 0
                                                А так бывает? )
                                                В тестах бывает )))
                                                найти полных тёзок
                                                По ТЗ, должна быть возможность однозначного поиска человека по ФИО, значит, таких данных нет на входе. Хотя, защитное программирование никто не отменял… С другой стороны, код vintage с циклом будет глючить, выдавая только первого тёзку. Может, лучше бы падал, чтобы привлечь внимание к проблеме.
                                                • 0

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

                                                • +1
                                                  > По ТЗ, должна быть возможность однозначного поиска человека по ФИО, значит, таких данных нет на входе

                                                  Значит неправильное ТЗ )

                                                  Его не надо реализовывать, надо найти ответственного и вставить выяснить как так получается, что на массиве, где тормозит линейный поиск, нет тёзок.
                                                  • 0

                                                    Очень просто: у нас онлайн-игра, где ещё на стадии регистрации отсекаются похожие имена, чтобы игроки не путались.

                                                    • 0
                                                      Значит нет и проблемы с «name_first == name_last», и нет исключений при построении словаря, ок.
                                                      • 0
                                                        Значит, полных тёзок быть в принципе не может.

                                                        Но без учёта случая имя==фамилия, есть проблема, что юзер может уронить приложение, введя некоторые данные )))
                                                        • 0

                                                          Ввод данных пусть заботит того, кто его пишет

                                                          • 0
                                                            А откуда он знает, что реализация следующего сервиса падает, если имя==фамилия. Такая проверка точно его забота?
                                                            • 0
                                                              Дык, «ещё на стадии регистрации отсекаются похожие имена, чтобы игроки не путались.»
                                                              • 0
                                                                Похожие на другие имена, видимо, а не на себя.
                                                                Tom Tom на кого похож?
                                                                • 0
                                                                  Давайте ещё раз:
                                                                  1) Это выдуманный пример, который вырос из конкретного кода на JS.
                                                                  2) Этот код на JS не имеет подобных проверок. Совсем.
                                                                  3) Не имеет он их потому, что данные были проверены на стадии регистрации. На этапе регистрации ещё надо решить вопрос обновления нашего словаря-кеша, Tom Tom уронит сначала там, там же уточнятся допустимость имён и, как следствие, правила составления словаря.
                                                                  4) Если вам очень хочется вписать Distinct — я не против, но на большом словаре работу это не ускорит, а мы тут вроде что-то преждевременно оптимизируем. Ради Tom Tom прогонять весь массив через Distinct едва ли правильно.
                                                                  5) Ещё раз, пример выдуманный. В реале это придётся переписать как только клиенту скажут «только имя-фамилия надо писать полностью и с 1 пробелом».
                                                                  6) Да и это всё не важно, тут ТЗ: мой код должен зеркалить код vintage. Вписывать туда проверки, которых нет в исходном коде — вообще не правильно.
                                                                  • 0
                                                                    2) Этот код на JS не имеет подобных проверок. Совсем.
                                                                    3) Не имеет он их потому, что данные были проверены на стадии регистрации.
                                                                    6) Да и это всё не важно, тут ТЗ: мой код должен зеркалить код vintage. Вписывать туда проверки, которых нет в исходном коде — вообще не правильно.

                                                                    В том-то и дело, что исходный пример vintage корректно обработает «Tom Tom». А с парой «John Smith», «John Smith» не упадёт, но с точки зрения логики приложения отработает неверно. Значит, вход с полными тёзками не предусматривался. А «Tom Tom» — допустим.
                                                                    • 0
                                                                      Уточню, «зеркалить» — не значит «быть абсолютно идентичным». У него корректно обработает Tom Tom, у меня корректно грохнется на «John Smith». А править код под изменяющиеся задним числом требования я тут не буду.

                                                                      Давайте вы с vintage составите ТЗ, vintage под него напишет корректный код на JS, а потом можно будет обсуждать насколько моя реализация ему не соответствует. Если мне кто-то объяснит зачем.
                                                                      • 0

                                                                        Не, давайте завязывать с этим холиваром :-) Думаю тут уже все высказали всё, что хотели.

                                              • 0

                                                Обычно индекс вводят когда линейный поиск явно не справляется с объёмами данных. Удвоение размера индекса лишь незначительно замедлит поиск.

                                          • +1

                                            Первый вариант, как уже сказали, никуда не годится. Второй — тот ещё ребус. Тут дело даже не в том, как написать по короче. Всегда можно применить специфичную функцию, которая решает твою проблему одной строкой кода. Но поскольку проблем великое множество, то и функций таких в асенале нужно иметь соответствующее число. И не просто иметь, нужно помнить как каждая работает и в каких случаях какую лучше применить. У нас тут было две похожие задачи — вы решили их двумя совершенно разными способами. Можно ещё слегка изменить условия (например, мы хотим получить список всех ключей но только в формате имя-фамилия, без дубликатов пользователей) и вам придётся снова всё переписать с нуля.


                                            И вся эта свистопляска для чего? Потому что кто-то вам сказал, что LINQ — круто, модно, молодёжно. А циклы — удел старпёров и так уже никто не пишет в 2k17?


                                            Не вижу

                                            Присмотритесь:


                                            calc( result => ({ ...result() , [ x ] : 1 }) , {} )

                                            Чтобы понять, что будет на выходе, нужно рекурсивно развернуть result.

                                            • 0

                                              Вам почему то кажется, что «переписать с нуля» — это проблема. Нет, это не циклы, это занимает 10 секунд.


                                              Напомню, что выше у вас проблема была в том, что бы что то дебажить. Мне не надо дебажить ни этот код, ни код qw1, что бы знать как он будет работать.


                                              А в чем «разные» способы? Там только SelectMany добавился для задвоения. В Linq не тысячи функций, а десяток. И при этом qw1 решил ту же задачу другим набором, ничего специфического для задачи там тоже нет.


                                              Простите, но я не вижу проблемы в том, что проблема решается в одну строку кода.

                                              • 0
                                                Вам почему то кажется, что «переписать с нуля» — это проблема.

                                                Дополнительное время как на написание, так и на проверку эквивалентности старой логики, новые баги, конфликты при мёрже… чур меня, показалось.


                                                Нет, это не циклы, это занимает 10 секунд.

                                                Ухты, 10 секунд. Вы под спидами программируете?


                                                Мне не надо дебажить ни этот код, ни код qw1, что бы знать как он будет работать.

                                                Мне этот простой цикл тоже "не надо дебажить". К сожалению, реальные задачи чуть по сложнее.


                                                А в чем «разные» способы? Там только SelectMany добавился для задвоения.

                                                И пачка new KeyValuePair вместо лямбд.

                                                • 0
                                                  на проверку эквивалентности старой логики, новые баги, конфликты при мёрже…

                                                  Нет в этой строчке кода ни багов, ни эквивалентности, а конфликтов не больше, чем в циклах.


                                                  К сожалению, реальные задачи чуть по сложнее.

                                                  И там разница между парой строчек и 2 экранами циклов ещё заметнее )


                                                  И пачка new KeyValuePair вместо лямбд.

                                                  Лямбда на месте. Слово KeyValuePair можно выбросить, тогда надо взять старую перегрузку с явным указанием ключа, дело вкуса.


                                                  Мне этот простой цикл тоже "не надо дебажить".

                                                  Напомню, что начинали мы с «Вы хотите сказать, что пишите код без ошибок, раз он у вас "дебага не требует"?». Ну да, придуманные Вами задачи пишутся без ошибок просто потому, что решаются в одну строчку. Где ошибки в JS всплывают я так и не понял.

                                        • 0
                                          Не знаю js, в c# просто
                                          users_by_name = 
                                            users.Select(x => new {u = x, name = $"{x.name_first} {x.name_last}"})
                                            .Union(users.Select(x => new {u = x, name = $"{x.name_last} {x.name_first}"}))
                                            .ToDictionary(x => x.name, x => x.u);
                                          
                                          • +1

                                            Какая длинная… макаронина :-)


                                            Компилятор сможет соптимизировать создание промежуточных объектов до простого засовывания записи в словарь?

                                            • 0
                                              Теоретически… да ))) Имеет право.
                                              Но практически, я думаю, он оптимизирует только очень небольшое количество случаев, как цепочку filter складывает в один, объединяя условия по AND, или выполняя цикл до первого найденного элемента, если после filter стоит first
                                              • 0

                                                Подозреваю это даже не какая-то оптимизация компилятора, а обычные ленивые вычисления. То есть filter возвращает не реальный массив, а range над предыдущим.

                                                • 0
                                                  С точки зрения библиотеки классов — да, это всё IEnumеrable, но компилятор инлайнит реализации linq-функций, после чего смотрит, что получилось, и оптимизирует.

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

                          Это вас не спасёт, когда:


                          1. используется транспайлер и вы смотрите в код через сорсмапы.
                          2. функция окажется не "чистой" и повторный вызов приведёт к совершенно иным последствиям.
                          3. у вас нет доступа к нужным переменным, как на втором скриншоте.

                          Ну и это банально не удобно.

                      • 0
                        Вот как тут узнать, что вернул filter?

                        В Chrome Devtools выделяете мышкой, наводите курсор — и он показывает:
                        image
              • 0

                А какие проблемы с отладкой лямб в js? (По крайней мере, относительно отладки любого другого js кода).

              • +1

                Только вот что-то Ваш второй пример попахивает. В одном цикле у Вас выполняется 2 разных по смыслу действия, это не очень хорошо. Давайте разделим его на 2 отдельных цикла.


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


                А потом, чтобы не нарушать принцип DRY, вынесем в отдельную функцию обработку массива, чтобы достаточно было прописать только действие над каждым элементом. И каждый обработанный элемент будем мержить с результатом.


                Черт, кажется, мы только что изобрели reduce...

                • 0
                  В одном цикле у Вас выполняется 2 разных по смыслу действия, это не очень хорошо.

                  Да нет, действие одно — взять user и распихать его по индексам. Какой смысл дважды подряд итерироваться по одной и той же коллекции? Более того, если коллекция генерируется налету, то двойной проход по ней может давать разные объекты со всеми вытекающими.


                  for( let user of User.genegate( 1e6 ) ) {
                      // process user
                  }

                  Тут вам возвращается итератор, который налету создаёт вам объекты. Вы берёте каждый объект и обрабатываете как пожелаете.


                  const users = User.genegate( 1e6 ).toArray()
                  users.reduce( ... )
                  users.reduce( ... )

                  А тут вам необходимо сначала сериализовать итератор в массив, чтобы потом дважды по нему пробежаться, после чего массив выбрасывается (привет, GC).


                  DRY — замечательный принцип, но начиная с определённого порога он приводит к катастрофическому возрастанию Complexity. Поэтому в борьбе с копипастой важно знать меру и не превращать простую строчку кода в ребус.

              • 0

                Строго говоря, код неэквивалентен: в примере с reduce будут создаваться новые экземпляры объектов на каждой итерации. Но согласен, это было сделано с целью демонстрации "императивный vs декларативный стиль".

        • 0

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

          • 0
            Возможно, вы отвечаете на какой-то другой комментарий, т.к. в этом ничего нет про экономию на условиях.
            • 0

              Тогда не совсем понял, зачем в циклах break, continue без условий?


              Так-то семантически что написать map (someArray, someFunc), что написать for x in someArray { someFunc(x) } — большой разницы не видно. До недавнего времени про эти map, reduce вообще никто не вспоминал, кроме функциональщиков.

              • 0
                Тогда не совсем понял, зачем в циклах break, continue без условий?
                break — очевидно, поиск первого элемента, удовлетворяющего условию (что заменяется на функциональный first), а continue — применение filter в начале цепочки, в императивном коде способ уменьшить вложенность, замена
                foreach (var x in arr) {
                    if (x < 100) {
                        // обрабатываем x
                    }
                }
                на
                foreach (var x in arr) {
                    if (x >= 100) continue;
                    // обрабатываем x
                }
      • +1
        1. А с чего вы взяли, что persons приходят с сервера? Какая вообще разница, какой источник данных, если эти данные нужно просто отфильтровать?
        • –1

          У вас есть иное объяснение, почему в качестве имён полей используются буквы "M" и "F" и приходится вводить человекопонятные константы, чтобы с ними работать?

          • +1

            В базе лежат так, в вёрстке так сделаны select'ы, модуль получения persons писал пенсионер, который с Fortran только слез

    • –1

      map, filter и reduce в стандартную библиотеку Ada не завезли, так что это будет цикл. Всё равно в реальном коде хорошим тоном считается прятать такое в отдельную функцию getPensioners(persons).

      • +2

        В вашем примере в статье необходимость констант была изящно подменена работой с типами. Здесь подобное может сработать с константами пола. Что тут может предложить Ада? А то ведь "выгодный" синтетический пример можно для любого языка привести.

        • 0

          Ну конкретно для вашего примера тип-перечисление (enum) как раз для таких случаев, причём он появился ещё в С.

      • 0

        Окей, такие вещи, допустим, не везде есть. Но как в Ada с аналогами Iterable/Enumerable/Range для пользовательских типов? И что с передачей функций по ссылке в другие функции? Что с замыканиями и лямбдами?

        • 0

          EDIT: про наличие subprogram access уже нашёл. Вопрос по лямбдам пока остаётся.

  • +8

    Я не очень понял, как вы определили операции сложения и вычитания на множестве {1..120}, ведь они не замкнуты на нём. Кольцом вычетов оно тоже не является, так как не содержит нуля. Если операции выводят из множества, то в чём тогда вообще смысл этого типа данных? Чему будет равно a - b, если обе переменных типа Age и равны 120?

    • –5

      Случай равенства a и b в данном примере обрабатывается отдельным условием.

      • +2

        Тем не менее, чему будет равно a - b, если обе переменных типа Age и равны 120? Я не хочу каждый раз писать тонну условий, я хочу, чтобы язык предоставлял удобные и безопасные типы, тем более когда его систему типов так расхваливают.

        • 0

          Если убрать условие равенства возрастов и добавить условие ageOfBob <= ageOfMary, то программа скажет Mary is older than Bob by 0 years. В данном случае это странно, поэтому добавлено условие.


          Другое дело — если создать переменную ageDiff : Age и добавить строку ageDiff := abs ageOfBob - ageOfMary, а в блоке exception дописать when others => put_line("Something went wrong!"), то при равенстве ageOfBob и ageOfMary будет генерироваться исключение и результат будет Something went wrong!.

        • 0
          Я не берусь утверждать, как оно в аде работает, но вообще во многих языках даты (а возраст — это, можно сказать, дата: либо дата рождения, либо дата в летоисчислении от момента рождения человека) и временные интервалы.

          Т.е. «a» и «b» — это числа в диапазоне 1..120 (почему 1, кстати? и почему есть максимум?), а их разница — это целое число, у которого минимум и максимум другие. По смыслу и по свойствам это разные сущности: «возраст» и «разница в возрасте».

          Правда, сомневаюсь, что ада сама выводит этот тип — «разница в возрасте».
    • –5

      А что вас смущает в том, что операция над одними типами возвращает другой? Не знаю как в Аде, но в идеальном ЯП {1..120} - {1..120} должно давать тип {-119..119}.

      • +3
        Идеальный язык должен валиться с exception.
        • +1

          С чего бы вдруг?

          • 0
            Выход за пределы диапазона.
            • 0

              Так исходные переменные никто и не меняет — они за пределы диапазона не выходят. А вот разность двух чисел, очевидно, имеет совершенно другой диапазон. Если бросать исключение при любых операциях над числами из ограниченных диапазонов, то какой смысл вообще использовать ограниченные диапазоны? Какой смысл от чисел, над которыми нельзя производить никакие операции, так как почти любая операция даёт числа из другого диапазона? А вот от автоматического выведения диапазонов — пользы предостаточно. Компилятор может обнаружить недостижимость веток кода, переполнение максимального числового диапазона, не учёт некоторых комбинаций условий.

              • +2

                Всё зависит от того, какое подмножество взять и как определить на нём разность. Если, например, взять множество {0..n}, а разность определить как разность по модулю, получим кольцо вычетов — красивую и полезную на практике структуру.

                • 0

                  Можно определить тип


                  type R is mod 10;


                  В этом случае сложение чисел этого типа будет происходить по модулю, то есть R(7) + R(8) будет давать 5.

                  • +2
                    Синтаксический сахарок для очень частных случаев.

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

                      Есть в Ada и перегрузка операторов для типов данных. И перегрузка функций.

              • 0

                Или, например, взять все чётные числа {2n | n <- Z} и определить операции сложения и вычитания естественным образом. Получим коммутативное ассоциативное кольцо, чьими свойствами сможем в полной мере пользоваться и знать, что все операции с нашим типом безопасны и не выведут из множества чётных чисел.


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

                • 0

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

                  • 0

                    Наоборот! Вдруг я ошибся и определённая мной операция выводит за пределы установленного множества? Тогда я хочу получить ошибку компиляции, а не исполнения. Если мои операции намеренно выводят за границы установленного множества, то какой в этом множестве тогда смысл? Я лучше буду просто использовать Integer, или хотя-бы явное приведение типов.

                    • –1

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


                      Int a = - persons.length // a has type Int{ -MAX_INT .. 0 }
                      Int b = random( 10 ) // b has type Int{ 0 .. 10 }
                      return a + b >= 10 // compile error: always false
                      • 0
                        Not always false. Но не думаю, что минус вам влепили за это.
                        • 0

                          Вот видите, вы не заметили ошибки, а компилятор заметил :-) random( 10 ) возвращает значения от 0 до 9 включительно. В комментариях, я, конечно, накосячил.


                          Да мне ещё и карму слили. Хабр — торт :-)

                          • 0
                            Int a = - persons.length // a has type Int{ -MAX_INT .. 0 }
                            Int b = random(random( 10 ) ) // компилятор правда знает , чему равен этот диапазон?
                            return a + b >= 10 
                            
                            • +1

                              Область значения функции он всегда знает:


                              random( n ) is Int[ 0 .. n )
                              random( random( n ) ) is random( Int[ 0 .. n ) ) is Int[ 0 .. random( n ) ) is Int[ 0 .. Int[ 0 .. n ) ) is Int[ 0 .. n )
                              • 0
                                А если что-то более сложное, чем встроенная функция? Рекурсивное что-то типа ряда Фибоначчи?
                                • +1

                                  При детектировании рекурсии можно возвращать максимально возможный диапазон

                                  • 0
                                    Можно, наверное. А это все ещё про существующую реализацию Ada (автоматическое угадывание диапазонов, и проверка, если угадать удалось. )?
                                    • 0

                                      Не, я с Ада не знаком, я так, фантазирую на тему.

        • 0

          Ответил здесь

      • +5

        1) Если мы вводим тип с ограниченным набором значений, мы, скорее всего, делаем это не просто так а с вполне определённой целью: мы хотим, чтобы любая переменная данного типа содержала значения только из заданного интервала. Очевидно, что и операции над такими переменными должны иметь вполне определённый смысл. Иначе зачем вообще вводить собственный тип, ведь можно использовать Integer?


        2) Хорошо, когда тип данных образует некоторую математическую структуру с доказанными свойствами. Тогда производить операции над этим типом становится просто и приятно, а рассуждать о программе — легче. В любом другом случае ограничения становятся искусственными, а свойства — некрасивыми и не очевидными. Это приведёт только к большему числу ошибок.

        • –5
          Очевидно, что и операции над такими переменными должны иметь вполне определённый смысл. Иначе зачем вообще вводить собственный тип, ведь можно использовать Integer?

          Затем, чтобы компилятор нам помогал. Например, если мы складываем два Integer, то компилятор должен ругнуться, так как возможно переполнение, которое мы не предусмотрели. Предусмотреть это можно следующими способами:


          1. Использовать операцию "сложение по модулю" вместо "сложения".
          2. Предварительно проверить, что число меньше половины от MAX_INT.

          if( user_input < MAX_INT / 2 ) {
              // here user_input has type Int{ 0 .. MAX_INT/2 }
              log( user_input + user_input ) // all ok
          } else {
              // here user_input has type Int{ MAX_INT/2 .. MAX_INT }
              log( user_input + user_input ) // compile error: possible integer overflow
          }

          В любом другом случае ограничения становятся искусственными, а свойства — некрасивыми и не очевидными. Это приведёт только к большему числу ошибок.

          Приведите конкретный пример, не будьте голословными.

          • 0
            Насчёт целочисленного переполнения не понял. Есть места, где переполнение не возникнет никак (итерация по массиву/строке. Там может быть выход за пределы строки, да. ). Если имеет смысл контролировать переполнения в математике — есть библиотеки типа safeint (C++). Зачем делать проверки всего подряд, если можно проверять то, что необходимо?
            • 0

              Ну так, при итерации счётчик сравнивается с длинной массива, а значит не может её превысить. Тут никакие дополнительные проверки не нужны.

    • +1
      Вы еще про операции сравнения забыли. Как работает a < b, тоже далеко не очевидно.
      • 0

        Подмножество Age множества целых чисел наследует операции над этим множеством.

        • +2
          Оно не может их просто так наследовать. Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.

          Так вот — если вы хотели показать, как это круто, иметь в языке вот такие типы, то у вас это прямо скажем не очень пока получилось.

          type Age is range 1… 120;

          А почему собственно 120? Почему это вообще константы? Может ли этот тип быть параметризован другим типом, задающим границы? Что будет, если в операции участвуют Int и Range одновременно?

          Этот пример на сегодня вызывает больше вопросов, чем дает ответов. И это все далеко не тривиальные вопросы по системе типов языка.
          • +1
            Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.

            А с чего вы взяли, что разность возрастов должна являться возрастом? Возрастом кого может являться "разница возрастов"? :-)

            • 0
              Поэтому для операций с временем используют специальные типы данных. А то возраст как подмножество int без 0 выглядит странно. В пролёте долгожители, новорожденные… Невозможно сравнить возраст 2 пенсионеров, один из которых старше на полгода: обоим по 65, разница 0.
              И специальный тип данных «разница возрастов» не нужен.
              • +1

                Открываем ISO8601 и видим следующие типы:


                1. момент времени (4 марта)
                2. временной период (37 дней)
                3. временной диапазон (с 5 по 27 число)
                4. повторяющийся временной диапазон (каждый день с 5 до 6)

                К "5 марта в 18:00" можно прибавить "1 месяц" и получить "5 апреля в 18:00".

                • 0
                  Непонятно написал, сорри. Имелось в виду, что если уж реализован вменяемый тип для времени, то он будет включать и поддержку операции с временем. А не так как в статье.
            • 0
              Возрастом кого может являться «разница возрастов»? :-)
              Очевидно, возрастом родителя на момент, когда у него появился ребёнок :)
              • 0
                Едва ли. Там же целые числа, точность плюс-минус год.
            • 0
              В Ada результатом арифметических операций над некоторым типом является «базовый» тип этого типа (для определения вида range 1..120, насколько я помню, это будет Integer), но при присвоении переменной любого ограниченного типа будет выполнена проверка на вхождение в диапазон этого типа.

              Поэтому, например, если мы считаем A1+A2-A3, где все три типа Age, и A1+A2 вылазит за его диапазон, но A1+A2-A3 не вылазит, промежуточная ошибка не будет замечена, но если результат всего выражения будет присвоен AR типа Age и вылезет за 1..120, будет исключение.

              К вопросу о корректности разности возрастов это не относится.

              Чтобы сделать, что разность возрастов была отдельным типом, нужно создать «пакет» в терминах Ada (это лучше всего соответствует «классу» в C++ и аналогах) и для него уже определить function "-". Механизм, таким образом, для этого есть, хоть и громоздкий. Там уже можно определить и все необходимые прочие операции и ограничения этого типа.

              Если исключить возможность явной конверсии своих типов данных в стандартные (как целые), то можно обеспечить и типобезопасность для контроля размерностей (например, не присваивать километры миллиграммам). В смысле этих возможностей Ada не уступает какому-нибудь C++, хотя и выражает свои возможности более громоздко.
              • 0
                если мы считаем A1+A2-A3, где все три типа Age, и A1+A2 вылазит за его диапазон, но A1+A2-A3 не вылазит, промежуточная ошибка не будет замечена

                Так это и не ошибка.

          • 0
            Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.

            ЕМНИП, вывалится исключение в run-time. Т.е. когда в Ada описывается тип-диапазон, то при работе с экземплярами этого типа в run-time добавляются необходимые проверки, а в compile-time, там где компилятор видит константы, он может выдать предупреждение.
          • 0
            А почему собственно 120?

            По этому поводу — случай из трудовых будней. В медицинское учреждение, где работаю, недавно установили (за нехилые такие деньги) Лабораторную информационную систему (ЛИС). В ней для каждого анализа (ну, типа, сахар, холестерин, билирубин и т.п.) предусмотрены нормы в зависимости от возраста (чтобы, значит, человек понимал — жить ему или достаточно). Так вот, максимальный возраст, который там можно ввести = 99 (не больше двух циферок). А на днях пришёл дедушка 1916 года рождения и нормы на бланке с его анализами, естественно, не распечатались. Пришлось «скидывать» долгожителю пару годиков, чтобы выдать осмысленный результат.

            Справедливости ради замечу, что ЛИС таки в трудовой процесс вписался достаточно хорошо (хоть и понаписат на 1С) — много чего автоматизирует и по-крупному не лажает. Однако и мелких забавных косячков (удивляющих до изумления программерской безалаберностю) всплывает тоже прилично. Как говорится — се ля ви.
          • +1
            Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.

            Но сравнению это не мешает: если возраст это не отдельный тип со своими операциями, а уточнение Integer, то сравнение выполняется по правилам Integer, и выход разности за допустимые пределы игнорируется. Тем более что сравнение может вылиться, например, в инструкцию SLT процессора стиля MIPS/Risc-V, которая вообще формально ничего не вычитает :)


            А почему собственно 120? Почему это вообще константы? Может ли этот тип быть параметризован другим типом, задающим границы?

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


            Что будет, если в операции участвуют Int и Range одновременно?

            В общем случае операции над range-типами исполняются так же, как над их базовым типом, а проверка на диапазон производится уже при присвоении целевой переменной (или можно форсировать конверсией к её типу какой-то части выражения). То есть, пока вы результат никуда не присвоили или не проконвертировали, сумма Integer + range 1..120 будет считаться так же, как сумма двух Integer. Если это 32-битные, то переполнение будет диагностировано по выходу любого промежуточного результата за общеизвестные -2147483648...2147483647. Более узкий диапазон, как уже сказал, будет проверяться, если вы присвоите переменной типа Age или напишете что-то в стиле Age(A1+X) как одну из компонент более сложного выражения.


            Режим с игнорированием всех переполнений (аналогично unsigned в современном C, всей числовой арифметике на стандартных операциях в Java...) возможен с использованием определений типа mod, например mod 2**32 значит 32-битное беззнаковое с заворотом результата (обрезанием до 32 бит). Если нужно считать таким образом, требуется явная конверсия в такой модулярный тип и обратно. Модулярные — только целые без знака.


            Резюмируя, всё это в языке хорошо определено (иначе бы его не приняли для DoD:)), и достаточно оптимально, как для задачи "добиться отсутствия неожиданных эффектов чуть менее, чем везде". Так что Ваши вопросы по системе типов всего лишь требуют внимательного похода в место типа такого.

            • 0
              Я вовсе не хотел сказать, что в Ada все так плохо :)

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

          У меня к вам вопрос с подвохом. Позволяет ли Ada объявить пользовательский тип, который можно потом "ограничить"?

  • +8
    То есть люди поторые пишут hello world туториалы молчат у существовании языка Ada?
  • –16
    СКОЛЬКО ЖЕ НЕАНДЕРТАЛЬЦЕВ ОКАЗЫВАЕТСЯ ЗДЕСЬ
    • +4
      Вопрос задан некорректно. Также вы пропустили вопросительный знак — утверждаю, что их (неандертальцев здесь) намного больше, чем минусов у вашего комментария — практически все!

      В современных неафриканских популяция вида Homo Sapiens содержится в среднем (с вариациями по планете) от ~2% до ~4.5% генов «неандертальского» человека, у всех без исключения людей — в том числе и у вас. Данные примеси не содержатся лишь в ДНК популяций народов Африки, которые и являются единственными «не-неандертальскими», строго говоря — так как неандертальцы обратно в Африку не совались.
  • +1
    Не согласен с идеей статьи

    1) Не всегда код предполагает все эти проверки и исключения — задача может быть простая и одноразовая — сделали какую-то выборку на ходу, получили данные и больше нам этот код не нужен — под это все тоже тащить кучу встроенных проверок? Даже если мы 100% уверены в наших исходных данных которые УЖЕ прошли это все в другом месте?

    2) Первый пример проверок выше — это стандартный подход, встречающийся во многих языках, изучив его раз, не надо ломать голову при изучении нового языка, что же тут наворотили эдакого и зачем.

    PS: Ада разрабатывалась как язык промышленного применения, где цена ошибки ОЧЕНЬ высока — там подобный подход действительно оправдан. Но использовать его же, например для скриптовых языков? Зачем?
    • 0

      Я бы не отказался использовать такой подход хотя бы для экономии собственного времени на отладке и юнит-тестах.

      • +1
        странно, но я для экономии времени 1-2-5 разовые штуки пишу на питоне, а всё остальное — на скале. Тоже, для экономии времени.

        И конечно, довольно частая ситуация, когда «помнишь, ты писал скриптик, вот нам такое нужно запустить в работу» — берется скрипт на питоне, обзывается «прототип», собираются требования, и новая задача решается на скале.

        Мне чем-то поможет Ада, в которую map/filter не завезли? А еще был (да есть, конечно), Eiffel, тоже хороший язык для разработки программ, где цена ошибки высока.
        • –2

          Мне кажется, что области применения Ada и Python-скриптов как-то не особенно пересекаются. Scala — окей, дело вкуса, хотя по сравнению с Ada её область применения довольно узкая.

          • +1
            вроде эта ветвь началась с тезиса «экономия времени».

            Ок, значит там, где питон экономит время, Аде точно нечего делать.

            А там, где скала — дело вкуса, но скала не так хорошая в каких-то вещах.
            В каких? Не компилирует код в бинарник, требует jvm?

            Почему вообще сюда влез — про язык Ада слышу давно, и что характерно — ни разу не то, что ни возникало желания «взять и что-то сделать», а даже понимания, мне вот это действительно нужно вот тут?

            Почему вспомнил про Eiffel — однажды удивительно «срослось» — требования задачи, и то, что предлагал язык. И он очень «легко зашел» в той задаче. Правда, это было уже настолько давно, то сейчас бы ни разу не зашел.

            В какие задачи Ада «легко заходит», и вот реально экономит время, по сравнению с современными инструментами? Ответ на такой вопрос был бы гораздо интереснее, чем рассказ, как компилятор ловит всякие тонкие моменты в Hello World.
            • –3

              А есть ли вообще существенные основания говорить, что Scala годится для создания сложных систем и не создаст проблем в будущем? Чем обоснованы синтаксис и семантика этого языка, есть ли средства формальной верификации? Привязка к JVM — уже существенное ограничение, поскольку это не годится для многих встраиваемых систем, микроконтроллеров и просто когда не хочется писать bloatware.


              Ada — несовременный инструмент? Несколько странная точка зрения.

              • +1
                очень странный ответ. Мне не интересно про скалу, мне инетерсно про Ада.
                И вы, вместо рассказа, чем Ада хороша, спрашиваете у меня про скалу?

                Давайте исходить из того, что я знаю «скала не очень-то», и выбираю чем бы заменить. Расскажите, почему если я заменю скалу на аду, я буду экономить больше времнеи, каких проблем у меня не будет (не важно, есть они сейчас или нет).

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

                ALGOL 68, Pascal, C++ (Ada 95), Smalltalk (Ada 95), Java (Ada 2005), Eiffel (Ada 2012)

                И конечно, «история» — новый стандарт — Ада 2012. Предыдущий Ада 2005.
                Ок, с «несовременный», я ошибался (в 2000 ых Ада совсем пропала из мира вокруг меня, и я даже не подозревал что два стандарта выкатили за это время), давайте выберем слово «не популярный в индустрии».

                В качестве показателя популярности… Вот, есть статья про всякие рейтинги https://habrahabr.ru/company/kingservers/blog/307012/
                там про Ада нет ни слова. Это, конечно, мало что говорит о языке — у всех рейтингов методики странные.

                Так расскажите что на этом языке хорошо пишется?
                Почему, если я возьму этот инструмент, смогу победить конкурентов, которые пишут на яве (эти ваще просто массой давят), скала, котлин, го, С++ в конце концов, а вон еще хаскель медленно, но растет.
                • –1

                  Статья не является рекламой Ada, Ada не является серебрянной пулей. Хорош или плох язык для какого-то конкретного проекта определяется требованиями к проекту — может быть там вам Pascal будет лучше всего или Assembler…


                  Если судить о дизайне языков по этим рейтингам, то получится, что самые совершенные и продвинутые языки — JavaScript, Java, PHP, Python и C#.

          • +1
            У Scala — не узкая, а другая. Я бы поспорил, что на сегодня уже. Scala это например Spark, узкая такая область в строне Big Data.
            • 0

              Довольно трудно обосновать, зачем вместо Java нужно использовать Scala, поэтому большая часть потенциальной области Scala таки занята Java-ой.

  • –23
    Во что превратился хабр. Люди бездумно не прочитав статьи не поняв смысла (возможно эти люди даже не способны понять смысла статьи) начинают комментировать. Вот что значит комменты открыли для всех
    • +12
      Вот что значит комменты открыли для всех

      Сказал человек с тремя комментариями, два из которых написаны капсом.

      • –11
        и?
        • +13
          Есть такой механизм психологической защиты (или психологической адаптации — короче, один из способов самообмана), когда человек какие-то качества, отрицаемые в себе, усиленно изобличает в окружающих. Проекция называется. Хрестоматийный пример — «и что ты смотришь на сучок в глазе брата твоего, а бревна в твоем глазе не чувствуешь?» :)
          • –16
            подозреваю что тебя плюсанули те же самые люди, которые плюсанули коммент Bringoff. А по твоему комменту… Меня то и изобличить не в чем, что плохого в капсе и почему завести новый аккаунт по каким-то причинам стало вдруг плохо?
            • +1
              В субботу-то вечером? Ну-ну :)
            • +8
              что плохого в капсе
              Во что превратился хабр.
  • +2
    Странная статья, код на аде тоже весьма странен.
    Особенно when others => null

    Я уж молчу про обработку исключений в конце модуля, в Аде вполне есть классический try-catch и пользователю можно говорить об ошибке, не вываливаясь с нулевым кодом возврата из программы.
    • 0

      В этом примере появление других исключений маловероятно.


      Не могли бы вы привести пример "классического" блока try-catch на Ada? Вроде бы исключения всегда обрабатываются в отдельном блоке exception.

      • 0
        Адовский

        begin
          Work;
        exception
          when X1 =>
             Y1;
        end;
        


        как по мне, ничем, кроме выбора и группировки ключевых слов, не отличается от

        try
          Work;
        catch X1 =>
             Y1;
        end;
        


        А вот то, что исключения в Ada не параметризованы — не переменные, а только типы — сильно усложняет передачу обстоятельств исключения.
        • –1

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

  • 0
    const MAX_PERSON_AGE = 120

    Ну зачем же 120. А если кому-то будет 125? 130? Придется программу ведь переписывать) давайте сразу 150-200.
    • +1

      Будет пропатчено в следующей версии ;)

      • 0
        Будут пачтить каждый год, пока долгожитель не умрет :)
        А как умрет, то назад вернут :)
        • 0

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

  • 0
    Мне кажется, автор, хоть и косвенно и спорно, но затронул одну важную тенденцию последних лет. На самом деле, имхо, очень плохую тенденцию.

    Звучит она, на мой взгляд как «Больше языков. Аляпистых. Новых. МОДНЫХ. И не важно что будет потом».

    Лично мое впечатление такое, что подавляющее число «создателей новых языков программирования», не имеют серьезного опыта разработки, и вообще подходят к этому делу как к приключению, к блогу, созданию развлечения для фанатов.

    — «Обратная совместимость выпускамых ими версий языков? Зачем, ведь у нас есть новая вкуссная фича, мы впилили ее — вот, смотрите!». А то что вы не сможете перевести свой проект на новую версию потому, что вам надо будет переписывать мегатонны кода — это их уже не волнует.

    «Ну и что! Зато смотрите как у нас бложик с этой фичей красиво обрабатывает тысячи твитов в секунду!» — Ну вот разве не так? Посмотрите на новость о снятии с поддержки руби 1.8 (кажется, поправьте меня). Мол, «грустно, конечно, что снимается с поддержки, прошло 2.5 года с момента выпуска, но эта версия до сих пор используется, потому, что код написанный для руби 1.8 не работает на интерпретаторе версии 2». (Так, или почти дословно.).
    Занавес. Привлекли, поигрались, и кинули.

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

    В общем статья хоть и «кривоватая», но она о правильной проблеме — будьте осторожны с очередным «самым новым языком». Скорее всего это просто очередная мода.

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

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

    Об этом статья. Имхо.
    • +3
      новые языки появляются потому что к ЯП предъявляется много требований (в том числе противоречивых и неформализуемых), всех их удовлетворить невозможно, писать софт (и языки) сейчас легко и приятно, поэтому почему-бы и не пописать?

      Какие из языков всплывут и выживут — трудно сказать. И не всегда понятно почему одни хорошие языки всплывают, а другие — нет. и почему некоторые плохие тонут, а другие — нет, тоже не всегда ясно.
      • –1

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

        • +1
          Это вы какие-то страшилки рассказываете.

          Конечно, если мозгов у руководства компании нет — то компания окажется на свалке, но не потому, что ошиблась в ставке на какую-то технологию, а именно потому, что мозгов нет :)
        • 0
          Хотя в реальности зачастую it-компании и продукты в этом плане опережают технологии -)

        • +1
          Полагаю, что «простого программиста» и «среднестатистическую IT-компанию» модные технологии занимают не особо, поэтому бояться в этом смысле им по большому счёту нечего. Кстати, ставка на «проверенных лошадок» вовсе не гарантирует многолетнего процветания — в некоторых сферах риск через пару лет оказаться на свалке (проморгав молодого резвого рысака) тоже весьма велик.
      • 0
        В подавляющем большинстве случаев понять не сложно.
        Куда вливают бабло — то и всплывает.

        Кажется, даже на хабре писали, про Оберон и ветку «языков Вирта». То, что тогда происходило — хороший пример того, как бабло вливаемое Sun, IBM, Oracle подняло наверх Java и сопутствующие технологии.
        • +1
          но есть хорошие языки в которые бабло не вливали и они вполне живы.
          • 0
            Это какие же?
            • 0
              «хорошие» — понятие субъективное. Что-бы избежать холивара ппредлагаю смотреть в рейтинги языков. Посмотрите в первую десятку любого рейтинга, вы там найдёте пару-тройку языков за которыми не стояли корпорации с вливаниями денег, девелоперов, инфраструктуры.
              • –1
                Так не пойдёт. Выкладывайте тут ваши десятку и пару-тройку и тогда будем предметно дискутировать.
          • 0
            Эти редкие исключения настолько хороши, что в них вливают «бабло» люди, которые ими пользуются.

            А с какого-то момента срабатывает «количество переходит в качество». Когда закончился хайп вокруг явы? Можно сказать, что он закончился примерно в тот момент, когда перестали вливать бабло. Но к этому времени язык уже набрал массу, и если не стал первым, то был в пятерке популярных инструментов уж точно. С этого момента активные вливания ненужны — все кто поставил на эту технологию вливали по капле.
        • +2
          Но я бы, все таки, не ставил знак равно между вливанием бабла и успехом.

          Понятно, что серьезная поддержка требует денег и не малых — я даже не представляю затраты на разработку новой спецификации Java в условиях обеспечения 100% обратной совместимости, или на разработку тестов джавамашины.

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

          Джаве это удалось.

          Я сомневаюсь, что Нуралиев, принимая решения о начале работ над «1С Enterprise Tools», был под чьим то денежным вливанием, когда принимал решение о том, что это будет написано на Java под Eclipse.

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

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

          Имхо, вливание не гарантирует. Хотя и необходимо.
          • 0
            Конечно, там не равенство, но «бабло» — самый весомый коэффициент этой функции.

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

            Отличный пример 1С, просто прекрасный. Давайте крутанём историю, и посмотрим, а что вообще можно было взять когда они начинали готовое? VBA? Была цель, было понимание чего нужно получить в итоге, и были ресурсы. Наверное тогда написать свой интерпретатор было совсем недорого, а плюсы огромные — полная независимость от кого-либо. А сейчас оказалось, что Java и Eclipse выгоднее, и это тоже бизнес-решение.

            История Javascript'а интересная, кто-то может примерно показать момент, когда он начал взлетать? Когда из инструмента «что бы подгружать в фоне картинки» он начал набирать массу. Что происходило? jQuery? Или уже когда Node.js появилось?
            Но в плане JS гораздо интереснее посмотреть на «запрос рынка». Какие стояли задачи, и какие в тот момент были доступные инструменты, было что-то лучше чем JS для решения тех вот задач? «Лучше» по множеству критериев, в первую очередь, по бизнес-критериям, а не по «удобству разработки».
            • 0
              > История Javascript'а интересная, кто-то может примерно показать момент, когда он начал взлетать?

              Когда Apple сказал, что плагинов c другими языками на iphone не будет.
              • 0
                а потом они подумали, что всё равно фигня получается, дали SDK для Objective C, а теперь вообще продвигают Swift. Я по времени не путаю?

                Тогда еще вопрос — ну вот, Apple «отпустило», но получается пинок JS'у дали настолько хороший, что он уже сам дальше полетел?
                • –1
                  Нет так. Они убили flash/silverlight, с тех пор на клиенте монополия JS