Обычно адепты нового Того-самого-лучшего-языка пишут свои рекламные мини-программы для обучения и продвижения примерно так:
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 сложный, многословный и трудный для изучения. Здесь приведён пример того, как "лёгкие" на первый взгляд языки просто перекладывают часть работы на программиста, в то время как "сложные" языки, на изучение фишек которых может и вправду понадобиться больше времени, позволяют сэкономить время на отладке и проверке при реальной разработке.
Комментарии (330)
staticlab
04.03.2017 12:52+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); });
vintage
04.03.2017 13:34-14А зачем на Аде писать говнокод?
- persons должен уже содержать нормализованные объекты, а не сырые данные, полученные с сервера. Иначе при изменении ответа сервера вам придётся рефакторить код всего приложения. А поддержать несколько серверов вообще не представляется возможным.
- Для итерирования есть удобные циклы с полноценной поддержкой break и continue. Незачем лепить замыкания на ровном месте.
qw1
04.03.2017 14:21+41. Не всегда программист может влиять на серверный код. Например, сервером может выступать API «одноклассников» и задача — посчитать количество пенсионеров в друзьях.
2. Я много программировал до и после удобного использования замыканий. Разница огромная. Где раньше надо было написать портянку циклов с break, continue, и пятёркой вспомогательных переменных, сейчас без напряжения мозга просто пишешь, что ты хочешь получить, сберегая мыслительные ресурсы для остальной части задачи.
Как по мне, поддержка запросов к коллекциям сейчас является необходимой чертой современного языка. Даже мастодонты java и c++ сдались и ввели эту поддержку. Это как раньше были языки без поддержки рекурсии и динамической памяти, но вымерли.vintage
04.03.2017 14:35-8О том и речь, что программист должен изолировать от приложения то, на что он не влияет — ввод пользователя и ответ сервера.
- Циклы не требуют каких-то особых "мыслительных ресурсов". Написанный вами код ничем от циклов не отливается. Ну вот совсем. Только отлаживать его — то ещё "удовольствие". ни промежуточные результаты выполнения не посмотреть, ни по шагам пройтись, ни даже прочитать содержимое переменных из родительского скоупа, на которые нет ссылки из вложенного (хвала JIT).
А как по мне — обобщённое программирование и исполнение кода времени компиляции являются необходимыми чертами современного языка :-) А ещё, как по мне, forEach — не является "запросом к коллекции", а является он обычным циклом, но зачем-то сделанным на редкость неудобным.
qw1
04.03.2017 19:05+6Циклы не требуют каких-то особых «мыслительных ресурсов». Написанный вами код ничем от циклов не отличается
Банально устаёшь писать длинные тупые циклы, где нужно найти сумму элементов, максимальный элемент, минимальный среди максимальных. Внимание ослабевает, сажаешь ошибки. Проще написать запрос в декларативном стиле, что тебе нужно от данных, и идти дальше, не заостряя внимания на подобных задачах.
Только отлаживать его — то ещё «удовольствие». ни промежуточные результаты выполнения не посмотреть, ни по шагам пройтись, ни даже прочитать содержимое переменных из родительского скоупа, на которые нет ссылки из вложенного (хвала JIT).
Конкретно в Visual Studio (C#/C++) в режиме DEBUG нет никаких проблем: брейкпоинты можно ставить на любой оператор внутри лямбды, переключая контекст в окне Call Stack, можно добраться до любых переменных в watches. Если где-то это не так, это проблема IDE, а не подхода в целом.JekaMas
04.03.2017 20:58+4К сожалению, это правда.
Два года разработки на golang, где все на циклах и очень мало обобщений, и сейчас отдыхаю, изучая python. Да, он медленнее, не такой клевый для параллельного кода, но насколько же быстрее попробовать какую-то идею! Одна-две строки, вместо десятка строк кода, который может быть и выкинешь после…
vintage
04.03.2017 21:21Проще написать запрос в декларативном стиле, что тебе нужно от данных, и идти дальше
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.
qw1
04.03.2017 21:55Это замечательно, но человек привёл пример на JS
То есть, с лямбдами писать пока рановато, потому что ваш IDE их хуже поддерживает, чем классический императивный код. Ну, ок.vintage
04.03.2017 23:30-1IDE тут, к сожалению, ни при чём. Это свойства языка / виртуальной машины.
qw1
05.03.2017 08:49+1Ставить брейкпоинты не на строку целиком, а на оператор внутри строки — это свойство IDE.
Не вижу никаких проблем сделать DEBUG-режим запуска VM, в котором будут фиксироваться стек-фреймы и их контекст. Чтобы дебаггер мог получить список стекфреймов, отобразить его в отдельном окне и двойным кликом переключать контекст так, чтобы при наведении мыши на переменные (или добавление переменных в watches) их значение показывалось в соотвествии с выбранным контекстом.vintage
05.03.2017 11:00Отладчик в мире JS принадлежит виртуальной машине, а не среде разработки. Среда разработки может, конечно, поключиться к виртуальной машине и управлять отладчиком удалённо, но только в рамках его функционала.
Это всё уже давно реализовано. Проблема "декларативного" fluent-кода, на который сейчас многие молятся, в том, что в нём у вас промежуточные вычисления находятся на стеке, а не помещаются в какую-то переменную.
Вот как тут узнать, что вернул filter?
qw1
05.03.2017 12:42Вот как тут узнать, что вернул filter?
Не знаю, как в вашей среде, но обычно в отладчиках можно выделить часть выражения
и нажать хоткей 'evaluate', или добавить в watches и исследовать там структуру выражения.a.filter(i=>i>b)
.map(i=>i*i)
sshikov
05.03.2017 15:11Я вам больше скажу: IDEA вполне позволяет ставить точки останова на лямбды. Если ты тыкаешь в строку типа приведенной выше — то у тебя спросят, куда именно поставить останов — на все выражение в целом, на filter или на map.
Если кто-то этого не умеет — это его проблемы, а не лямбд.vintage
05.03.2017 15:51Особенное удобство — ставить точки остановка в каждой "лямбде", чтобы можно было пройтись по шагам. Ну или очень внимательно на каждом шаге выбирать между "step into" и "step over", а если ошибся — начинать заново.
areht
05.03.2017 19:07+1Так у вас претензии к лямбдам, к fluent, или их к поддержке IDE/VM?
Вы пользовались IntelliTrace? Оно решает проблему с «на каждом шаге выбирать между „step into“ и „step over“, а если ошибся — начинать заново».vintage
05.03.2017 19:13У меня претензии только к тем, кто пишет код без оглядки на то, как его потом дебажить. Я верю, что для других языков есть всякие крутые инструменты, но в JS если есть возможность использовать циклы и переменные, то лучше использовать циклы и переменные, а не изобретать звездолёт, для которого нужен специальный дебаггер, который изобретут только в следующем году и то, только под одну платформу.
qw1
05.03.2017 20:10+1Тут имеем выбор
1) Написать понятный код, в терминах задачи, с использованием цепочки filter, reduce и прочих высокоуровневых конструкций (код, который очевиден и не требует дебага),
либо
2) Написать цикл, в котором происходит какая-то обработка. Человек, читающий код, всё равно должен будет прокрутить в уме выполнение кода и привести этот цикл в термины задачи, чтобы понять, что тут делается.vintage
05.03.2017 20:30-1Вы правда считаете первый код "простым, понятным и очевидным"? Боюсь вы один такой, для обычных людей итеративный алгоритм проще, понятней и очевидней рекурсивного.
Вы хотите сказать, что пишите код без ошибок, раз он у вас "дебага не требует"?
areht
05.03.2017 21:16+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 и с этим проблемы?vintage
05.03.2017 21:33Тут же нет рекурсии (если я правильно понимаю написанное).
Под капотом, конечно, нет, но описание рекурсивное.
Ну и таки лично я подобное пишу без дебага, как
У массивов нет такого метода. Впрочем, не важно. Требования изменились, теперь надо искать как по имя-фамилия, так и по фамилия-имя. Ваши действия?
Мои будут такими:
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 } }
areht
05.03.2017 22:17users.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()
> но описание рекурсивное.
Не вижуqw1
05.03.2017 22:31Тут не учтены повторы ключа. Если name_first == name_last, свалится с исключением «An item with the same key has already been added». Нужен
Distinct
передToDictionary
qw1
05.03.2017 22:34Решение с Single не подходит, линейный поиск. Неспроста же требуется построить словарь.
areht
05.03.2017 23:18> Неспроста же требуется построить словарь.
Незнаю-незнаю, удваивать размер словаря и одновременно оптимизировать… Если там следующим шагом отчество комбинаторно добавится — Single может выиграть )
> Если name_first == name_last
А так бывает? )
Проще найти полных тёзок, там и ваш вариант упадёт.qw1
06.03.2017 00:27А так бывает? )
В тестах бывает )))
найти полных тёзок
По ТЗ, должна быть возможность однозначного поиска человека по ФИО, значит, таких данных нет на входе. Хотя, защитное программирование никто не отменял… С другой стороны, код vintage с циклом будет глючить, выдавая только первого тёзку. Может, лучше бы падал, чтобы привлечь внимание к проблеме.vintage
06.03.2017 00:36Заметьте, что лучше получить эксепшен "пользователь такой-то и такой-то имеют одинаковые имена, что не допустимо по такой-то причине", чем "ой, что-то пошло не так не могу добавить объект абракадабра в какой-то словарь, так как там уже есть какая-то белиберда".
areht
06.03.2017 03:53+1> По ТЗ, должна быть возможность однозначного поиска человека по ФИО, значит, таких данных нет на входе
Значит неправильное ТЗ )
Его не надо реализовывать, надо найти ответственного ивставитьвыяснить как так получается, что на массиве, где тормозит линейный поиск, нет тёзок.vintage
06.03.2017 09:21Очень просто: у нас онлайн-игра, где ещё на стадии регистрации отсекаются похожие имена, чтобы игроки не путались.
areht
06.03.2017 11:06Значит нет и проблемы с «name_first == name_last», и нет исключений при построении словаря, ок.
qw1
06.03.2017 15:15Значит, полных тёзок быть в принципе не может.
Но без учёта случая имя==фамилия, есть проблема, что юзер может уронить приложение, введя некоторые данные )))areht
06.03.2017 17:18Ввод данных пусть заботит того, кто его пишет
qw1
06.03.2017 23:18А откуда он знает, что реализация следующего сервиса падает, если имя==фамилия. Такая проверка точно его забота?
areht
07.03.2017 00:07Дык, «ещё на стадии регистрации отсекаются похожие имена, чтобы игроки не путались.»
qw1
07.03.2017 13:33Похожие на другие имена, видимо, а не на себя.
Tom Tom на кого похож?areht
07.03.2017 16:53Давайте ещё раз:
1) Это выдуманный пример, который вырос из конкретного кода на JS.
2) Этот код на JS не имеет подобных проверок. Совсем.
3) Не имеет он их потому, что данные были проверены на стадии регистрации. На этапе регистрации ещё надо решить вопрос обновления нашего словаря-кеша, Tom Tom уронит сначала там, там же уточнятся допустимость имён и, как следствие, правила составления словаря.
4) Если вам очень хочется вписать Distinct — я не против, но на большом словаре работу это не ускорит, а мы тут вроде что-то преждевременно оптимизируем. Ради Tom Tom прогонять весь массив через Distinct едва ли правильно.
5) Ещё раз, пример выдуманный. В реале это придётся переписать как только клиенту скажут «только имя-фамилия надо писать полностью и с 1 пробелом».
6) Да и это всё не важно, тут ТЗ: мой код должен зеркалить код vintage. Вписывать туда проверки, которых нет в исходном коде — вообще не правильно.qw1
07.03.2017 19:362) Этот код на JS не имеет подобных проверок. Совсем.
3) Не имеет он их потому, что данные были проверены на стадии регистрации.
6) Да и это всё не важно, тут ТЗ: мой код должен зеркалить код vintage. Вписывать туда проверки, которых нет в исходном коде — вообще не правильно.
В том-то и дело, что исходный пример vintage корректно обработает «Tom Tom». А с парой «John Smith», «John Smith» не упадёт, но с точки зрения логики приложения отработает неверно. Значит, вход с полными тёзками не предусматривался. А «Tom Tom» — допустим.areht
07.03.2017 20:40Уточню, «зеркалить» — не значит «быть абсолютно идентичным». У него корректно обработает Tom Tom, у меня корректно грохнется на «John Smith». А править код под изменяющиеся задним числом требования я тут не буду.
Давайте вы с vintage составите ТЗ, vintage под него напишет корректный код на JS, а потом можно будет обсуждать насколько моя реализация ему не соответствует. Если мне кто-то объяснит зачем.vintage
07.03.2017 22:29Не, давайте завязывать с этим холиваром :-) Думаю тут уже все высказали всё, что хотели.
vintage
06.03.2017 00:32Обычно индекс вводят когда линейный поиск явно не справляется с объёмами данных. Удвоение размера индекса лишь незначительно замедлит поиск.
vintage
06.03.2017 00:13+1Первый вариант, как уже сказали, никуда не годится. Второй — тот ещё ребус. Тут дело даже не в том, как написать по короче. Всегда можно применить специфичную функцию, которая решает твою проблему одной строкой кода. Но поскольку проблем великое множество, то и функций таких в асенале нужно иметь соответствующее число. И не просто иметь, нужно помнить как каждая работает и в каких случаях какую лучше применить. У нас тут было две похожие задачи — вы решили их двумя совершенно разными способами. Можно ещё слегка изменить условия (например, мы хотим получить список всех ключей но только в формате имя-фамилия, без дубликатов пользователей) и вам придётся снова всё переписать с нуля.
И вся эта свистопляска для чего? Потому что кто-то вам сказал, что LINQ — круто, модно, молодёжно. А циклы — удел старпёров и так уже никто не пишет в 2k17?
Не вижу
Присмотритесь:
calc( result => ({ ...result() , [ x ] : 1 }) , {} )
Чтобы понять, что будет на выходе, нужно рекурсивно развернуть result.
areht
06.03.2017 00:44Вам почему то кажется, что «переписать с нуля» — это проблема. Нет, это не циклы, это занимает 10 секунд.
Напомню, что выше у вас проблема была в том, что бы что то дебажить. Мне не надо дебажить ни этот код, ни код qw1, что бы знать как он будет работать.
А в чем «разные» способы? Там только SelectMany добавился для задвоения. В Linq не тысячи функций, а десяток. И при этом qw1 решил ту же задачу другим набором, ничего специфического для задачи там тоже нет.
Простите, но я не вижу проблемы в том, что проблема решается в одну строку кода.
vintage
06.03.2017 01:02Вам почему то кажется, что «переписать с нуля» — это проблема.
Дополнительное время как на написание, так и на проверку эквивалентности старой логики, новые баги, конфликты при мёрже… чур меня, показалось.
Нет, это не циклы, это занимает 10 секунд.
Ухты, 10 секунд. Вы под спидами программируете?
Мне не надо дебажить ни этот код, ни код qw1, что бы знать как он будет работать.
Мне этот простой цикл тоже "не надо дебажить". К сожалению, реальные задачи чуть по сложнее.
А в чем «разные» способы? Там только SelectMany добавился для задвоения.
И пачка new KeyValuePair вместо лямбд.
areht
06.03.2017 02:52на проверку эквивалентности старой логики, новые баги, конфликты при мёрже…
Нет в этой строчке кода ни багов, ни эквивалентности, а конфликтов не больше, чем в циклах.
К сожалению, реальные задачи чуть по сложнее.
И там разница между парой строчек и 2 экранами циклов ещё заметнее )
И пачка new KeyValuePair вместо лямбд.
Лямбда на месте. Слово KeyValuePair можно выбросить, тогда надо взять старую перегрузку с явным указанием ключа, дело вкуса.
Мне этот простой цикл тоже "не надо дебажить".
Напомню, что начинали мы с «Вы хотите сказать, что пишите код без ошибок, раз он у вас "дебага не требует"?». Ну да, придуманные Вами задачи пишутся без ошибок просто потому, что решаются в одну строчку. Где ошибки в JS всплывают я так и не понял.
qw1
05.03.2017 22:25Не знаю 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);
vintage
05.03.2017 23:10+1Какая длинная… макаронина :-)
Компилятор сможет соптимизировать создание промежуточных объектов до простого засовывания записи в словарь?
qw1
06.03.2017 00:09Теоретически… да ))) Имеет право.
Но практически, я думаю, он оптимизирует только очень небольшое количество случаев, как цепочку filter складывает в один, объединяя условия по AND, или выполняя цикл до первого найденного элемента, если после filter стоит firstvintage
06.03.2017 00:38Подозреваю это даже не какая-то оптимизация компилятора, а обычные ленивые вычисления. То есть filter возвращает не реальный массив, а range над предыдущим.
qw1
06.03.2017 07:27С точки зрения библиотеки классов — да, это всё IEnumеrable, но компилятор инлайнит реализации linq-функций, после чего смотрит, что получилось, и оптимизирует.
Например, проход по массиву с вызовом простой лямбды точно не приводит к вызову функции для каждого элемента. Соответственно, память для хранения переменных, попадающих в замыкание, тоже не выделяется. То есть, оптимизации могут быть весьма сложными.
vintage
05.03.2017 15:43Не знаю, как в вашей среде, но обычно в отладчиках можно выделить часть выражения
Это вас не спасёт, когда:
- используется транспайлер и вы смотрите в код через сорсмапы.
- функция окажется не "чистой" и повторный вызов приведёт к совершенно иным последствиям.
- у вас нет доступа к нужным переменным, как на втором скриншоте.
Ну и это банально не удобно.
YemSalat
06.03.2017 12:28Вот как тут узнать, что вернул filter?
В Chrome Devtools выделяете мышкой, наводите курсор — и он показывает:
kahi4
04.03.2017 22:15А какие проблемы с отладкой лямб в js? (По крайней мере, относительно отладки любого другого js кода).
ookami_kb
05.03.2017 00:28+1Только вот что-то Ваш второй пример попахивает. В одном цикле у Вас выполняется 2 разных по смыслу действия, это не очень хорошо. Давайте разделим его на 2 отдельных цикла.
Теперь метод получается довольно длинный, давайте вынесем эти циклы с инициализацией пустого объекта в отдельные функции и будем просто возвращать результат в переменную.
А потом, чтобы не нарушать принцип DRY, вынесем в отдельную функцию обработку массива, чтобы достаточно было прописать только действие над каждым элементом. И каждый обработанный элемент будем мержить с результатом.
Черт, кажется, мы только что изобрели reduce...
vintage
05.03.2017 11:16В одном цикле у Вас выполняется 2 разных по смыслу действия, это не очень хорошо.
Да нет, действие одно — взять user и распихать его по индексам. Какой смысл дважды подряд итерироваться по одной и той же коллекции? Более того, если коллекция генерируется налету, то двойной проход по ней может давать разные объекты со всеми вытекающими.
for( let user of User.genegate( 1e6 ) ) { // process user }
Тут вам возвращается итератор, который налету создаёт вам объекты. Вы берёте каждый объект и обрабатываете как пожелаете.
const users = User.genegate( 1e6 ).toArray() users.reduce( ... ) users.reduce( ... )
А тут вам необходимо сначала сериализовать итератор в массив, чтобы потом дважды по нему пробежаться, после чего массив выбрасывается (привет, GC).
DRY — замечательный принцип, но начиная с определённого порога он приводит к катастрофическому возрастанию Complexity. Поэтому в борьбе с копипастой важно знать меру и не превращать простую строчку кода в ребус.
staticlab
05.03.2017 18:15Строго говоря, код неэквивалентен: в примере с reduce будут создаваться новые экземпляры объектов на каждой итерации. Но согласен, это было сделано с целью демонстрации "императивный vs декларативный стиль".
yarric
05.03.2017 14:08А можно пример, когда замыкания помогают сэкономить на условиях? Как я понимаю, если нужно проверить 10 условий, то так или иначе их придётся вписывать руками.
qw1
05.03.2017 14:27Возможно, вы отвечаете на какой-то другой комментарий, т.к. в этом ничего нет про экономию на условиях.
yarric
06.03.2017 19:19Тогда не совсем понял, зачем в циклах
break
,continue
без условий?
Так-то семантически что написать
map (someArray, someFunc)
, что написатьfor x in someArray { someFunc(x) }
— большой разницы не видно. До недавнего времени про эти map, reduce вообще никто не вспоминал, кроме функциональщиков.qw1
06.03.2017 23:23Тогда не совсем понял, зачем в циклах 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 }
bohdan4ik
04.03.2017 14:41+1- А с чего вы взяли, что
persons
приходят с сервера? Какая вообще разница, какой источник данных, если эти данные нужно просто отфильтровать?
vintage
04.03.2017 14:46-1У вас есть иное объяснение, почему в качестве имён полей используются буквы "M" и "F" и приходится вводить человекопонятные константы, чтобы с ними работать?
VolCh
04.03.2017 17:12+1В базе лежат так, в вёрстке так сделаны select'ы, модуль получения persons писал пенсионер, который с Fortran только слез
- А с чего вы взяли, что
yarric
04.03.2017 14:50-1map
,filter
иreduce
в стандартную библиотеку Ada не завезли, так что это будет цикл. Всё равно в реальном коде хорошим тоном считается прятать такое в отдельную функциюgetPensioners(persons)
.staticlab
04.03.2017 16:18+2В вашем примере в статье необходимость констант была изящно подменена работой с типами. Здесь подобное может сработать с константами пола. Что тут может предложить Ада? А то ведь "выгодный" синтетический пример можно для любого языка привести.
yarric
04.03.2017 21:44Ну конкретно для вашего примера тип-перечисление (enum) как раз для таких случаев, причём он появился ещё в С.
TargetSan
05.03.2017 01:05Окей, такие вещи, допустим, не везде есть. Но как в Ada с аналогами Iterable/Enumerable/Range для пользовательских типов? И что с передачей функций по ссылке в другие функции? Что с замыканиями и лямбдами?
TargetSan
05.03.2017 01:10EDIT: про наличие subprogram access уже нашёл. Вопрос по лямбдам пока остаётся.
devpony
04.03.2017 13:02+8Я не очень понял, как вы определили операции сложения и вычитания на множестве
{1..120}
, ведь они не замкнуты на нём. Кольцом вычетов оно тоже не является, так как не содержит нуля. Если операции выводят из множества, то в чём тогда вообще смысл этого типа данных? Чему будет равноa - b
, если обе переменных типаAge
и равны 120?yarric
04.03.2017 13:13-5Случай равенства
a
иb
в данном примере обрабатывается отдельным условием.devpony
04.03.2017 14:29+2Тем не менее, чему будет равно
a - b
, если обе переменных типаAge
и равны 120? Я не хочу каждый раз писать тонну условий, я хочу, чтобы язык предоставлял удобные и безопасные типы, тем более когда его систему типов так расхваливают.yarric
04.03.2017 15:01Если убрать условие равенства возрастов и добавить условие
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!
.
alexkunin
04.03.2017 15:34Я не берусь утверждать, как оно в аде работает, но вообще во многих языках даты (а возраст — это, можно сказать, дата: либо дата рождения, либо дата в летоисчислении от момента рождения человека) и временные интервалы.
Т.е. «a» и «b» — это числа в диапазоне 1..120 (почему 1, кстати? и почему есть максимум?), а их разница — это целое число, у которого минимум и максимум другие. По смыслу и по свойствам это разные сущности: «возраст» и «разница в возрасте».
Правда, сомневаюсь, что ада сама выводит этот тип — «разница в возрасте».
vintage
04.03.2017 13:24-5А что вас смущает в том, что операция над одними типами возвращает другой? Не знаю как в Аде, но в идеальном ЯП
{1..120} - {1..120}
должно давать тип{-119..119}
.iig
04.03.2017 13:40+3Идеальный язык должен валиться с exception.
vintage
04.03.2017 14:13+1С чего бы вдруг?
iig
04.03.2017 14:33Выход за пределы диапазона.
vintage
04.03.2017 14:45Так исходные переменные никто и не меняет — они за пределы диапазона не выходят. А вот разность двух чисел, очевидно, имеет совершенно другой диапазон. Если бросать исключение при любых операциях над числами из ограниченных диапазонов, то какой смысл вообще использовать ограниченные диапазоны? Какой смысл от чисел, над которыми нельзя производить никакие операции, так как почти любая операция даёт числа из другого диапазона? А вот от автоматического выведения диапазонов — пользы предостаточно. Компилятор может обнаружить недостижимость веток кода, переполнение максимального числового диапазона, не учёт некоторых комбинаций условий.
devpony
04.03.2017 15:15+2Всё зависит от того, какое подмножество взять и как определить на нём разность. Если, например, взять множество
{0..n}
, а разность определить как разность по модулю, получим кольцо вычетов — красивую и полезную на практике структуру.yarric
04.03.2017 22:07Можно определить тип
type R is mod 10;
В этом случае сложение чисел этого типа будет происходить по модулю, то есть
R(7) + R(8)
будет давать 5.qw1
04.03.2017 23:00+2Синтаксический сахарок для очень частных случаев.
В общем случае, надо делать перегрузку операторов как в c++, чтобы на каждую арифметическую операцию можно было написать свою логику и свои проверки целостности данных. И операторы прозрачной конвертации, если своему новому типу присваиваем значение системного или другого пользовательского типа (и наоборот).
devpony
04.03.2017 15:22Или, например, взять все чётные числа
{2n | n <- Z}
и определить операции сложения и вычитания естественным образом. Получим коммутативное ассоциативное кольцо, чьими свойствами сможем в полной мере пользоваться и знать, что все операции с нашим типом безопасны и не выведут из множества чётных чисел.
Можно рассмотреть бесконечное множество типов и соответствующих структур, но лишь некоторые из них будут полезны и безопасны на практике. Об этом нужно думать при проектировании системы и с этим должен помогать язык программирования.
vintage
04.03.2017 15:29Операции возвращающие значения из того же диапазона будут работать абсолютно одинаково, хоть компилятор будет кидать исключение, хоть выводить новый диапазон (который эквивалентен исходному). Разница будет лишь для операций, для которых выходной диапазон отличается от входного. И вот в этом случае кидать исключение — глупо.
devpony
04.03.2017 15:33Наоборот! Вдруг я ошибся и определённая мной операция выводит за пределы установленного множества? Тогда я хочу получить ошибку компиляции, а не исполнения. Если мои операции намеренно выводят за границы установленного множества, то какой в этом множестве тогда смысл? Я лучше буду просто использовать
Integer
, или хотя-бы явное приведение типов.vintage
04.03.2017 16:04-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
rafuck
05.03.2017 22:50Not always false. Но не думаю, что минус вам влепили за это.
vintage
06.03.2017 00:41Вот видите, вы не заметили ошибки, а компилятор заметил :-) random( 10 ) возвращает значения от 0 до 9 включительно. В комментариях, я, конечно, накосячил.
Да мне ещё и карму слили. Хабр — торт :-)
iig
07.03.2017 19:01Int a = - persons.length // a has type Int{ -MAX_INT .. 0 } Int b = random(random( 10 ) ) // компилятор правда знает , чему равен этот диапазон? return a + b >= 10
vintage
07.03.2017 22:35+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 )
devpony
04.03.2017 15:05+51) Если мы вводим тип с ограниченным набором значений, мы, скорее всего, делаем это не просто так а с вполне определённой целью: мы хотим, чтобы любая переменная данного типа содержала значения только из заданного интервала. Очевидно, что и операции над такими переменными должны иметь вполне определённый смысл. Иначе зачем вообще вводить собственный тип, ведь можно использовать Integer?
2) Хорошо, когда тип данных образует некоторую математическую структуру с доказанными свойствами. Тогда производить операции над этим типом становится просто и приятно, а рассуждать о программе — легче. В любом другом случае ограничения становятся искусственными, а свойства — некрасивыми и не очевидными. Это приведёт только к большему числу ошибок.
vintage
04.03.2017 15:49-5Очевидно, что и операции над такими переменными должны иметь вполне определённый смысл. Иначе зачем вообще вводить собственный тип, ведь можно использовать Integer?
Затем, чтобы компилятор нам помогал. Например, если мы складываем два Integer, то компилятор должен ругнуться, так как возможно переполнение, которое мы не предусмотрели. Предусмотреть это можно следующими способами:
- Использовать операцию "сложение по модулю" вместо "сложения".
- Предварительно проверить, что число меньше половины от 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 }
В любом другом случае ограничения становятся искусственными, а свойства — некрасивыми и не очевидными. Это приведёт только к большему числу ошибок.
Приведите конкретный пример, не будьте голословными.
iig
07.03.2017 23:07Насчёт целочисленного переполнения не понял. Есть места, где переполнение не возникнет никак (итерация по массиву/строке. Там может быть выход за пределы строки, да. ). Если имеет смысл контролировать переполнения в математике — есть библиотеки типа safeint (C++). Зачем делать проверки всего подряд, если можно проверять то, что необходимо?
vintage
07.03.2017 23:37Ну так, при итерации счётчик сравнивается с длинной массива, а значит не может её превысить. Тут никакие дополнительные проверки не нужны.
sshikov
04.03.2017 14:51+1Вы еще про операции сравнения забыли. Как работает a < b, тоже далеко не очевидно.
yarric
04.03.2017 15:04Подмножество
Age
множества целых чисел наследует операции над этим множеством.sshikov
04.03.2017 16:13+2Оно не может их просто так наследовать. Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.
Так вот — если вы хотели показать, как это круто, иметь в языке вот такие типы, то у вас это прямо скажем не очень пока получилось.
type Age is range 1… 120;
А почему собственно 120? Почему это вообще константы? Может ли этот тип быть параметризован другим типом, задающим границы? Что будет, если в операции участвуют Int и Range одновременно?
Этот пример на сегодня вызывает больше вопросов, чем дает ответов. И это все далеко не тривиальные вопросы по системе типов языка.vintage
04.03.2017 16:42+1Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.
А с чего вы взяли, что разность возрастов должна являться возрастом? Возрастом кого может являться "разница возрастов"? :-)
iig
04.03.2017 17:17Поэтому для операций с временем используют специальные типы данных. А то возраст как подмножество int без 0 выглядит странно. В пролёте долгожители, новорожденные… Невозможно сравнить возраст 2 пенсионеров, один из которых старше на полгода: обоим по 65, разница 0.
И специальный тип данных «разница возрастов» не нужен.vintage
04.03.2017 18:10+1Открываем ISO8601 и видим следующие типы:
- момент времени (4 марта)
- временной период (37 дней)
- временной диапазон (с 5 по 27 число)
- повторяющийся временной диапазон (каждый день с 5 до 6)
К "5 марта в 18:00" можно прибавить "1 месяц" и получить "5 апреля в 18:00".
iig
04.03.2017 19:25Непонятно написал, сорри. Имелось в виду, что если уж реализован вменяемый тип для времени, то он будет включать и поддержку операции с временем. А не так как в статье.
netch80
05.03.2017 14:00В Ada результатом арифметических операций над некоторым типом является «базовый» тип этого типа (для определения вида range 1..120, насколько я помню, это будет Integer), но при присвоении переменной любого ограниченного типа будет выполнена проверка на вхождение в диапазон этого типа.
Поэтому, например, если мы считаем A1+A2-A3, где все три типа Age, и A1+A2 вылазит за его диапазон, но A1+A2-A3 не вылазит, промежуточная ошибка не будет замечена, но если результат всего выражения будет присвоен AR типа Age и вылезет за 1..120, будет исключение.
К вопросу о корректности разности возрастов это не относится.
Чтобы сделать, что разность возрастов была отдельным типом, нужно создать «пакет» в терминах Ada (это лучше всего соответствует «классу» в C++ и аналогах) и для него уже определить function "-". Механизм, таким образом, для этого есть, хоть и громоздкий. Там уже можно определить и все необходимые прочие операции и ограничения этого типа.
Если исключить возможность явной конверсии своих типов данных в стандартные (как целые), то можно обеспечить и типобезопасность для контроля размерностей (например, не присваивать километры миллиграммам). В смысле этих возможностей Ada не уступает какому-нибудь C++, хотя и выражает свои возможности более громоздко.vintage
05.03.2017 15:57если мы считаем A1+A2-A3, где все три типа Age, и A1+A2 вылазит за его диапазон, но A1+A2-A3 не вылазит, промежуточная ошибка не будет замечена
Так это и не ошибка.
eao197
04.03.2017 16:48Тут уже привели минимум один пример: если из возраста 100 вычесть возраст 120 (оба валидны), получится отрицательное число, которое возрастом не является.
ЕМНИП, вывалится исключение в run-time. Т.е. когда в Ada описывается тип-диапазон, то при работе с экземплярами этого типа в run-time добавляются необходимые проверки, а в compile-time, там где компилятор видит константы, он может выдать предупреждение.
drafterleo
04.03.2017 17:31А почему собственно 120?
По этому поводу — случай из трудовых будней. В медицинское учреждение, где работаю, недавно установили (за нехилые такие деньги) Лабораторную информационную систему (ЛИС). В ней для каждого анализа (ну, типа, сахар, холестерин, билирубин и т.п.) предусмотрены нормы в зависимости от возраста (чтобы, значит, человек понимал — жить ему или достаточно). Так вот, максимальный возраст, который там можно ввести = 99 (не больше двух циферок). А на днях пришёл дедушка 1916 года рождения и нормы на бланке с его анализами, естественно, не распечатались. Пришлось «скидывать» долгожителю пару годиков, чтобы выдать осмысленный результат.
Справедливости ради замечу, что ЛИС таки в трудовой процесс вписался достаточно хорошо (хоть и понаписат на 1С) — много чего автоматизирует и по-крупному не лажает. Однако и мелких забавных косячков (удивляющих до изумления программерской безалаберностю) всплывает тоже прилично. Как говорится — се ля ви.
netch80
05.03.2017 14:18+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:)), и достаточно оптимально, как для задачи "добиться отсутствия неожиданных эффектов чуть менее, чем везде". Так что Ваши вопросы по системе типов всего лишь требуют внимательного похода в место типа такого.
sshikov
05.03.2017 14:57Я вовсе не хотел сказать, что в Ada все так плохо :)
Суть моих вопросов была в том, что все не так очевидно, если вдуматься, и накоторые вопросы по системе типов, по их неявному или явному приведению в операциях (а особенно у начинающих) вполне себе напрашиваются. И ответы на них далеко не всегда очевидны.
TargetSan
05.03.2017 00:43У меня к вам вопрос с подвохом. Позволяет ли Ada объявить пользовательский тип, который можно потом "ограничить"?
alesto
04.03.2017 13:20+8То есть люди поторые пишут hello world туториалы молчат у существовании языка Ada?
Umr001
04.03.2017 14:50-16СКОЛЬКО ЖЕ НЕАНДЕРТАЛЬЦЕВ ОКАЗЫВАЕТСЯ ЗДЕСЬ
wishnewski
05.03.2017 18:21+4Вопрос задан некорректно. Также вы пропустили вопросительный знак — утверждаю, что их (неандертальцев здесь) намного больше, чем минусов у вашего комментария — практически все!
В современных неафриканских популяция вида Homo Sapiens содержится в среднем (с вариациями по планете) от ~2% до ~4.5% генов «неандертальского» человека, у всех без исключения людей — в том числе и у вас. Данные примеси не содержатся лишь в ДНК популяций народов Африки, которые и являются единственными «не-неандертальскими», строго говоря — так как неандертальцы обратно в Африку не совались.
DaneSoul
04.03.2017 14:58+1Не согласен с идеей статьи
1) Не всегда код предполагает все эти проверки и исключения — задача может быть простая и одноразовая — сделали какую-то выборку на ходу, получили данные и больше нам этот код не нужен — под это все тоже тащить кучу встроенных проверок? Даже если мы 100% уверены в наших исходных данных которые УЖЕ прошли это все в другом месте?
2) Первый пример проверок выше — это стандартный подход, встречающийся во многих языках, изучив его раз, не надо ломать голову при изучении нового языка, что же тут наворотили эдакого и зачем.
PS: Ада разрабатывалась как язык промышленного применения, где цена ошибки ОЧЕНЬ высока — там подобный подход действительно оправдан. Но использовать его же, например для скриптовых языков? Зачем?yarric
04.03.2017 15:10Я бы не отказался использовать такой подход хотя бы для экономии собственного времени на отладке и юнит-тестах.
bormotov
04.03.2017 16:05+1странно, но я для экономии времени 1-2-5 разовые штуки пишу на питоне, а всё остальное — на скале. Тоже, для экономии времени.
И конечно, довольно частая ситуация, когда «помнишь, ты писал скриптик, вот нам такое нужно запустить в работу» — берется скрипт на питоне, обзывается «прототип», собираются требования, и новая задача решается на скале.
Мне чем-то поможет Ада, в которую map/filter не завезли? А еще был (да есть, конечно), Eiffel, тоже хороший язык для разработки программ, где цена ошибки высока.yarric
04.03.2017 21:54-2Мне кажется, что области применения Ada и Python-скриптов как-то не особенно пересекаются. Scala — окей, дело вкуса, хотя по сравнению с Ada её область применения довольно узкая.
bormotov
04.03.2017 23:11+1вроде эта ветвь началась с тезиса «экономия времени».
Ок, значит там, где питон экономит время, Аде точно нечего делать.
А там, где скала — дело вкуса, но скала не так хорошая в каких-то вещах.
В каких? Не компилирует код в бинарник, требует jvm?
Почему вообще сюда влез — про язык Ада слышу давно, и что характерно — ни разу не то, что ни возникало желания «взять и что-то сделать», а даже понимания, мне вот это действительно нужно вот тут?
Почему вспомнил про Eiffel — однажды удивительно «срослось» — требования задачи, и то, что предлагал язык. И он очень «легко зашел» в той задаче. Правда, это было уже настолько давно, то сейчас бы ни разу не зашел.
В какие задачи Ада «легко заходит», и вот реально экономит время, по сравнению с современными инструментами? Ответ на такой вопрос был бы гораздо интереснее, чем рассказ, как компилятор ловит всякие тонкие моменты в Hello World.yarric
04.03.2017 23:36-3А есть ли вообще существенные основания говорить, что Scala годится для создания сложных систем и не создаст проблем в будущем? Чем обоснованы синтаксис и семантика этого языка, есть ли средства формальной верификации? Привязка к JVM — уже существенное ограничение, поскольку это не годится для многих встраиваемых систем, микроконтроллеров и просто когда не хочется писать bloatware.
Ada — несовременный инструмент? Несколько странная точка зрения.
bormotov
04.03.2017 23:56+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/
там про Ада нет ни слова. Это, конечно, мало что говорит о языке — у всех рейтингов методики странные.
Так расскажите что на этом языке хорошо пишется?
Почему, если я возьму этот инструмент, смогу победить конкурентов, которые пишут на яве (эти ваще просто массой давят), скала, котлин, го, С++ в конце концов, а вон еще хаскель медленно, но растет.yarric
05.03.2017 10:57-1Статья не является рекламой Ada, Ada не является серебрянной пулей. Хорош или плох язык для какого-то конкретного проекта определяется требованиями к проекту — может быть там вам Pascal будет лучше всего или Assembler…
Если судить о дизайне языков по этим рейтингам, то получится, что самые совершенные и продвинутые языки — JavaScript, Java, PHP, Python и C#.
sshikov
05.03.2017 15:00+1У Scala — не узкая, а другая. Я бы поспорил, что на сегодня уже. Scala это например Spark, узкая такая область в строне Big Data.
yarric
06.03.2017 19:27Довольно трудно обосновать, зачем вместо Java нужно использовать Scala, поэтому большая часть потенциальной области Scala таки занята Java-ой.
Umr001
04.03.2017 15:07-23Во что превратился хабр. Люди бездумно не прочитав статьи не поняв смысла (возможно эти люди даже не способны понять смысла статьи) начинают комментировать. Вот что значит комменты открыли для всех
Bringoff
04.03.2017 18:07+12Вот что значит комменты открыли для всех
Сказал человек с тремя комментариями, два из которых написаны капсом.
Umr001
04.03.2017 18:18-11и?
drafterleo
04.03.2017 18:28+13Есть такой механизм психологической защиты (или психологической адаптации — короче, один из способов самообмана), когда человек какие-то качества, отрицаемые в себе, усиленно изобличает в окружающих. Проекция называется. Хрестоматийный пример — «и что ты смотришь на сучок в глазе брата твоего, а бревна в твоем глазе не чувствуешь?» :)
gryberg
04.03.2017 18:58+2Странная статья, код на аде тоже весьма странен.
Особенно when others => null
Я уж молчу про обработку исключений в конце модуля, в Аде вполне есть классический try-catch и пользователю можно говорить об ошибке, не вываливаясь с нулевым кодом возврата из программы.yarric
04.03.2017 22:00В этом примере появление других исключений маловероятно.
Не могли бы вы привести пример "классического" блока try-catch на Ada? Вроде бы исключения всегда обрабатываются в отдельном блоке
exception
.netch80
05.03.2017 13:46Адовский
begin Work; exception when X1 => Y1; end;
как по мне, ничем, кроме выбора и группировки ключевых слов, не отличается от
try Work; catch X1 => Y1; end;
А вот то, что исключения в Ada не параметризованы — не переменные, а только типы — сильно усложняет передачу обстоятельств исключения.yarric
05.03.2017 14:13-1Чисто эстетически — гораздо удобнее иметь отдельную секцию exception в конце блока, чем думать, куда вписать этот try (с которого по-хорошему должен начинаться каждый блок) и делать ещё один отступ.
TimsTims
04.03.2017 21:59const MAX_PERSON_AGE = 120
Ну зачем же 120. А если кому-то будет 125? 130? Придется программу ведь переписывать) давайте сразу 150-200.
dplsoft
04.03.2017 22:38Мне кажется, автор, хоть и косвенно и спорно, но затронул одну важную тенденцию последних лет. На самом деле, имхо, очень плохую тенденцию.
Звучит она, на мой взгляд как «Больше языков. Аляпистых. Новых. МОДНЫХ. И не важно что будет потом».
Лично мое впечатление такое, что подавляющее число «создателей новых языков программирования», не имеют серьезного опыта разработки, и вообще подходят к этому делу как к приключению, к блогу, созданию развлечения для фанатов.
— «Обратная совместимость выпускамых ими версий языков? Зачем, ведь у нас есть новая вкуссная фича, мы впилили ее — вот, смотрите!». А то что вы не сможете перевести свой проект на новую версию потому, что вам надо будет переписывать мегатонны кода — это их уже не волнует.
«Ну и что! Зато смотрите как у нас бложик с этой фичей красиво обрабатывает тысячи твитов в секунду!» — Ну вот разве не так? Посмотрите на новость о снятии с поддержки руби 1.8 (кажется, поправьте меня). Мол, «грустно, конечно, что снимается с поддержки, прошло 2.5 года с момента выпуска, но эта версия до сих пор используется, потому, что код написанный для руби 1.8 не работает на интерпретаторе версии 2». (Так, или почти дословно.).
Занавес. Привлекли, поигрались, и кинули.
Не предупреждают создатели «самых правильных языков» своих «последователей», и о тм, что «язык для быстрого написания прототипов», «динамической типизацией» и «идеально правильным ООП» — на самом деле крайне редко подходит для серьезных задач, больших систем со сложной логикой, и суровых будней поддержки промышленного кода. А может и не могут предупредить, потому, что сами не знают, или не думают что их последователи будут «пихать невпихуемое». А может и знают, но хотят посмотреть что из этого выйдет — ведь не своими деньгами рискуют, а «если что» скажут типа — «Ну наш язык же для быстрого написания прототипов, о чем вы, не надо было конечно так делать».
В общем статья хоть и «кривоватая», но она о правильной проблеме — будьте осторожны с очередным «самым новым языком». Скорее всего это просто очередная мода.
И особенно осторожны будьте, пока не увидите проблемы и границы применимости языка, пока язык не найдет свою нишу.
Как всякий логический и технический инструмент, язык программирования — имеет свою область применения. Применять технический инструмент, не понимая когда этого делать не надо — это чистой воды глупость. Но как раз о недостатках своего детища — в эпоху «рекламы и красивых бложиков» — создатель нового языка, "который, как оказалось, понравился не только ему" — не предупреждает.
Об этом статья. Имхо.ik62
04.03.2017 23:14+3новые языки появляются потому что к ЯП предъявляется много требований (в том числе противоречивых и неформализуемых), всех их удовлетворить невозможно, писать софт (и языки) сейчас легко и приятно, поэтому почему-бы и не пописать?
Какие из языков всплывут и выживут — трудно сказать. И не всегда понятно почему одни хорошие языки всплывают, а другие — нет. и почему некоторые плохие тонут, а другие — нет, тоже не всегда ясно.yarric
04.03.2017 23:45-1Простого программиста или среднестатистическую IT-компанию в таких условиях больше всего интересует вопрос, как не впутаться в какую-то модную технологию, которая может через пару лет оказаться на свалке вместе с ними.
bormotov
05.03.2017 00:04+1Это вы какие-то страшилки рассказываете.
Конечно, если мозгов у руководства компании нет — то компания окажется на свалке, но не потому, что ошиблась в ставке на какую-то технологию, а именно потому, что мозгов нет :)
d-stream
05.03.2017 00:05Хотя в реальности зачастую it-компании и продукты в этом плане опережают технологии -)
drafterleo
05.03.2017 00:19+1Полагаю, что «простого программиста» и «среднестатистическую IT-компанию» модные технологии занимают не особо, поэтому бояться в этом смысле им по большому счёту нечего. Кстати, ставка на «проверенных лошадок» вовсе не гарантирует многолетнего процветания — в некоторых сферах риск через пару лет оказаться на свалке (проморгав молодого резвого рысака) тоже весьма велик.
bormotov
05.03.2017 00:02В подавляющем большинстве случаев понять не сложно.
Куда вливают бабло — то и всплывает.
Кажется, даже на хабре писали, про Оберон и ветку «языков Вирта». То, что тогда происходило — хороший пример того, как бабло вливаемое Sun, IBM, Oracle подняло наверх Java и сопутствующие технологии.ik62
05.03.2017 00:18+1но есть хорошие языки в которые бабло не вливали и они вполне живы.
Londoner
05.03.2017 00:50Это какие же?
ik62
05.03.2017 11:49«хорошие» — понятие субъективное. Что-бы избежать холивара ппредлагаю смотреть в рейтинги языков. Посмотрите в первую десятку любого рейтинга, вы там найдёте пару-тройку языков за которыми не стояли корпорации с вливаниями денег, девелоперов, инфраструктуры.
Londoner
05.03.2017 13:27-1Так не пойдёт. Выкладывайте тут ваши десятку и пару-тройку и тогда будем предметно дискутировать.
bormotov
05.03.2017 12:16Эти редкие исключения настолько хороши, что в них вливают «бабло» люди, которые ими пользуются.
А с какого-то момента срабатывает «количество переходит в качество». Когда закончился хайп вокруг явы? Можно сказать, что он закончился примерно в тот момент, когда перестали вливать бабло. Но к этому времени язык уже набрал массу, и если не стал первым, то был в пятерке популярных инструментов уж точно. С этого момента активные вливания ненужны — все кто поставил на эту технологию вливали по капле.
dplsoft
05.03.2017 03:37+2Но я бы, все таки, не ставил знак равно между вливанием бабла и успехом.
Понятно, что серьезная поддержка требует денег и не малых — я даже не представляю затраты на разработку новой спецификации Java в условиях обеспечения 100% обратной совместимости, или на разработку тестов джавамашины.
Но помимо просто денежных вливаний (которые кстати, еще надо понимать как и куда вливать — в рекламу и откаты лицам принимающим решения, или вот в спецификации и стандартизацию) — есть некая техническая составляющая, которая делает инструмент надежным, прогнозируемым которому ты начинаешь доверять. Иначе после окончания вливаний и движухи — технология быстро сойдет на нет.
Джаве это удалось.
Я сомневаюсь, что Нуралиев, принимая решения о начале работ над «1С Enterprise Tools», был под чьим то денежным вливанием, когда принимал решение о том, что это будет написано на Java под Eclipse.
Удалось получить признание Джаваскрипту. даже стандартизирован, развивается.
Си с Плюсами развиваются тоже без особого жесткого маркетинга.
Дотнетам — имхо, ещё не факт, что удалось подобное. Пока майкрософт сам пилит продукт, пока платит за шумиху вокруг языка — оно будет жить. Но вот я не вижу пока такого же сообщества какое сложилось вокруг джавы. имхо.
Имхо, вливание не гарантирует. Хотя и необходимо.bormotov
05.03.2017 12:43Конечно, там не равенство, но «бабло» — самый весомый коэффициент этой функции.
Конечно, тупо лить — толку не будет. Что бы лить бабло, тоже нужны мозги. Но в моменты, когда появляются люди начинающие грамотно лить бабло, у функции «эффективный инструмент» меняется характер, если можно так выразиться. Вместо того что бы это был «эффективный инструмент для разработки», делают «эффективный инструмент для бизнеса». Само по себе, это может быть даже и лучше, но просто «разработка ПО» и «бизнес разработки ПО» — таки разные вещи. Акценты в разных местах.
Отличный пример 1С, просто прекрасный. Давайте крутанём историю, и посмотрим, а что вообще можно было взять когда они начинали готовое? VBA? Была цель, было понимание чего нужно получить в итоге, и были ресурсы. Наверное тогда написать свой интерпретатор было совсем недорого, а плюсы огромные — полная независимость от кого-либо. А сейчас оказалось, что Java и Eclipse выгоднее, и это тоже бизнес-решение.
История Javascript'а интересная, кто-то может примерно показать момент, когда он начал взлетать? Когда из инструмента «что бы подгружать в фоне картинки» он начал набирать массу. Что происходило? jQuery? Или уже когда Node.js появилось?
Но в плане JS гораздо интереснее посмотреть на «запрос рынка». Какие стояли задачи, и какие в тот момент были доступные инструменты, было что-то лучше чем JS для решения тех вот задач? «Лучше» по множеству критериев, в первую очередь, по бизнес-критериям, а не по «удобству разработки».areht
05.03.2017 12:52> История Javascript'а интересная, кто-то может примерно показать момент, когда он начал взлетать?
Когда Apple сказал, что плагинов c другими языками на iphone не будет.bormotov
05.03.2017 13:01а потом они подумали, что всё равно фигня получается, дали SDK для Objective C, а теперь вообще продвигают Swift. Я по времени не путаю?
Тогда еще вопрос — ну вот, Apple «отпустило», но получается пинок JS'у дали настолько хороший, что он уже сам дальше полетел?
dplsoft
05.03.2017 05:24+3Я не согласен с утверждением, что языки появляются потому, что к ЯП предъявляются требования. Далеко не всегда.
Возьмем историю C#. В следствии каких требований появился этот язык?
в следствии одного единственного, и не к языку, а вообще: майкрософту нужен был конкурент джаве. Не больше, ни меньше. Им плевать на ваши требования к языку. «Чисто бизнес, ничего личного». Они бы джаву использовали, но в своё время их за попытку создать несовместимую с эталонной реализацию языка джава — жестокопои…наказали юристы Sun
Или — в следствии какого требования в Питоне невидимые символы стали использоваться не только как разделители? это не требование к языку, это спорное решение его создателя, попытка навесить на язык функции, ему не свойственные (кслову — неужели, кто то думает, что перейдя с питона на си, люди будут продолжать форматировать код отступами, если они не понимают зачем они это делали, а теперь можно не форматировать? смешно как то...)
Или вот в следствии каких требований к языку, в руби класс (имя класса? поправьте) можно подменить объектом и поломать создание объектов во всей программе? это не требование к языку обусловленное решаемыми задачами, это желание его автора, воплотить некие идеи о «некой идеальной ооп-шности в вакууме». или вот какие требования к языку обуславливают, что разные реализации руби не совместимы друг сдругом?
Вот не связаны качества многих новых языков с требованиями, возникшими из задач, которые надо решать. Это решения их создателей, мода, красивый ближик, философия… но не требования к ЯП.
Языки сегодня создаются как стартапы — не от потребности решить проблему, а от других причин. от бизнесово-коммерческой жилки до банального ЧСВ.
Сегодня среди живых, распространенных языков, я вижу только 3 которые «возникли от требований к языкам», или сформировались исходя из задач, которые они решают: c/c++, java, javascript. Ну ещё куча ассемблеров под разные процессоры или микроконтроллеры.
Сишарп похоже начинает воплощаться в нечто похожее на потребное, но как то слишком многь в него впихивают «модностей».
Для утилитарного рабочего языка нужно далеко не это.
Практически все откровения про новые языки — выглядят как некие «нечистые манипуляции над неокррепшими умами неосиляторов с/с++» (да простят меня адепты модных языков).
Потому что когда я слышу объяснения, что вот тут новый язык, и он такой милый/удобный/красивы/удобный язык, в нем «так много синтаксического сахара», или в котором реализован «самый правильный философичный ооп» или «это самый функциональный из функциональных» — я начинаю сильно подозревать, что люди, которым это нравится, не собираются повседневные задачи решать, а собираются играться с логическими задачами из академической среды.
Я тоже не против почитать про хацкель, под бутылочку пива, дома вечером, но ребята, не на работе же, и не тогда когда нам надо остатки на складах считать и xml-ки валидные прогружать гигатонами! не надо писать на хацкеле учетную систему, не надо на пхп делать модуль обработки веб-сервиса )
И знаете, мне это напоминает анекдот про байкеров и стрит-рейсеров.
"- Давайте что ли знакомиться? Мы же тоже на мотоциклах гоняем, только на спортивных.
— А что с вами знакомиться — вы каждый год новые."
Каждый год новые откровения про очередную серебрянную пулю, новые языки, новые восторженные юнцы, которые бездумно лезут на новый лад очередного моднявого подобия бездумно все переделать… блин ) Рассказывал знакомый бывший рубист — из 200 рубистов собеседование прошел 1. Алгоритмов не знают, паттернов разработки не знают, структрур и технологий не знают, думать не хотят… зато они знают руби, самый модный язык, для решения всех каких ни попади задач, с высокой зарплатой программистов. Вот эти толпы хомячков и составляют основную массу последователей, как создающих, так и пользующих тысячи новых языков. Прилетающих как мухи на мед. А вы говорите… требования к ЯП… хи).
Что делать с этим не знаю. Но уж точно не пытаться оправдывать появление десятков новых языков некими «требованиями к ЯП». имхо)0xd34df00d
05.03.2017 10:40+1Я тоже не против почитать про хацкель, под бутылочку пива, дома вечером, но ребята, не на работе же, и не тогда когда нам надо остатки на складах считать и xml-ки валидные прогружать гигатонами! не надо писать на хацкеле учетную систему, не надо на пхп делать модуль обработки веб-сервиса )
А, кстати, почему не на хаскеле?
vintage
05.03.2017 11:27+1Или — в следствии какого требования в Питоне невидимые символы стали использоваться не только как разделители?
Требование простое — простота восприятия кода человеком.Фигурные скобки этого не обеспечивают. Отступы — обеспечивают. При наличие отступов, фигурные скобки не несут никакого дополнительного смысла.
Сегодня среди живых, распространенных языков, я вижу только 3 которые «возникли от требований к языкам», или сформировались исходя из задач, которые они решают: c/c++, java, javascript.
Вот уж что-что, а JS — всегда развивался стихийно и сильно подвержен модным тенденциям, не обусловленным ничем, кроме хайпа.
netch80
05.03.2017 13:38> При наличие отступов, фигурные скобки не несут никакого дополнительного смысла.
Есть один случай, когда отсутствие скобок таки вредно влияет на восприятие: это поиск конца блока, не охватываемого одним взглядом, а особенно, когда таких блоков несколько. Что-то вида
if условие1: ... if условие2: ... тут пара экранов всякого ... #end #end
Вот без этих #end увидеть конец блока может быть очень нетривиально.
Кто-то скажет, что нефиг такие крупные блоки создавать. Я не буду сильно возражать, но случай разный бывает, и код — тоже. (Особенно при отсутствии оптимизатора, который позволил бы вынести действия в отдельную внешне видимую функцию и потом успешно её заинлайнить.)
В остальном — поддерживаю.vintage
05.03.2017 16:10Ну и будет как с JSON — лесенка скобочек в середине файла и фиг поймёшь какая к какому блоку относится :-)
А когда начало блока сверху за границей экрана, а конец снизу за границей, то вообще не понятно с чем ты работаешь. Так что полноценно задача отображения контекста может быть решена лишь на уровне редактора, который бы отображал, на каком уровне какой контекст.
herr_kaizer
05.03.2017 19:25Выбранный уровень лесенки подсветит редактор или IDE. А как подсвечивать ничего в виде отступов?
vintage
05.03.2017 19:57+1Например, так: https://atom.io/packages/indent-tooltip
herr_kaizer
05.03.2017 21:06Это работает, если блок вмещается в один экран. Иначе придется считать палочки.
vintage
05.03.2017 21:21Присмотритесь внимательнее, что делает этот плагин.
qw1
05.03.2017 22:40Всё равно как-то неинтуитивно.
Вот как проблему невидимости заголовка блока решает R# для VS. Он его просто дорисовывает над окном редактора.
drafterleo
06.03.2017 01:49Присоединяюсь, средство, действительно, замечательное (многие редакторы так же перескакивают на парную скобку по «Ctrl + [»). Однако здесь хотелось бы отметить пару моментов:
- Если структурный блок не влазит в экран (а экраны теперь большие), вероятнее всего что-то не так с кодом — желательно подрефакторить;
- Возможно, блок не влазит в экран как раз из-за того, что в нём слишком высокая лесенка закрывающих скобок :)
drafterleo
06.03.2017 02:02P.S. Что примечательно, комбинация « Ctrl + ] / [ » прекрасно работает и в PyCharm — в питоновских блоках.
sergeperovsky
08.03.2017 22:09+1Разумеется, очень плохо, когда блок не помещается на экран. Хотя, у кого какой экран.
Речь идет о том, что запретить это нельзя. Случаются ситуации, когда приходится писать длинные строки и длинные тексты.
Поэтому любой стандарт форматирования имеет смысл, пока способствует удобочитаемости. Но необходимо иметь возможность от него отказаться в тех местах, где его соблюдение ухудшает читаемость кода.
Мне очень не хватает в большинстве языков возможности именования блоков. Просто ради того, чтобы осознать назначение некоторых закрывающихся скобок. Делаю это с помощью комментариев, но хотелось бы, чтобы компилятор мог убедиться в правильности вложений.,drafterleo
09.03.2017 16:08но хотелось бы, чтобы компилятор мог убедиться в правильности вложений
По-моему, это уже излишняя перестраховка из серии «бережёного Бог бережёт» (монашка, свечка, презерватив :)). Даже не припомню, чтобы у меня в программах когда-нибудь вылазила ошибка из-за неправильно поставленной блочной скобки. Влепить лишнюю или, там, случайно затереть — такое бывало. Но тогда вообще не компилируется. А чтобы в них запутаться — это, наверное, только с недосыпу возможно.
drafterleo
05.03.2017 20:06+1В современных редакторах есть такие пимпочки, которые позволяют сворачивать-разворачивать структурные блоки (а некоторые даже вертикальные полоски рисуют, чтобы ненароком не прокосоглазить :))
netch80
06.03.2017 09:05Ну и будет как с JSON — лесенка скобочек в середине файла и фиг поймёшь какая к какому блоку относится :-)
JSON не позволяет, к сожалению, ставить комментарии. Но само наличие {} скобок позволяет использовать '%' для перехода с начала на конец блока и обратно — это очень помогает в подобных случаях.
Для аналогичной функции в indent-based синтаксисе есть, например, vim-indentwise. Но с ним, если не ставить эти #end, а есть несколько вложенных блоков, после перехода на конец блока нельзя вернуться на начало блока, возврат будет неоднозначен.
А когда начало блока сверху за границей экрана, а конец снизу за границей, то вообще не понятно с чем ты работаешь.
Да, внутри блока это может быть сложно понять. В некоторых IDE (навскидку не помню) я видел показ этой позиции в заголовке окна (не так, как ниже на скриншоте, а в виде пути пакет-класс-функция).
ik62
05.03.2017 12:06+2Про C# не скажу, не в курсе. В любом случае не верю что у создателей языка в ТЗ мог быть только один пункт — создать конкурента Java. Этого просто не может быть.
Про хаскель, питон — эти языки создавались с очень четкими ориентирами и выбором компромиссов.
Сравните характеристики языков (они почти не пересекаются) и вам будет легко распознать что здесь хаскель, а что питон:
В качестве основных характеристик языка ХХХХХХХ можно выделить следующие:
недопустимость побочных эффектов (чистота языка); возможность писать программы с побочными эффектами без нарушения парадигмы функционального программирования с помощью монад;
статическая сильная полная типизация с автоматическим выведением типов, основанная на типизации Хиндли — Милнера;
функции высшего порядка, в том числе лямбда-абстракции;
частичное применение;
ленивые вычисления (lazy evaluation);
сопоставление с образцом (англ. pattern matching), функциональные образцы, охраняющие выражения (guards);
параметрический полиморфизм и его объедение с ad hoc полиморфизмом в единую модель посредством классов типов;
алгебраические типы данных, в том числе псевдобесконечные (за счёт ленивости);
генераторы списков (list comprehensions);
возможность интеграции с программами, реализованными на императивных языках программирования посредством открытых интерфейсов (стандартное расширение языка Foreign Function Interface (англ.)русск.[8]).
и
YYYYYYYYY is a clear and powerful object-oriented programming language, comparable to Perl, Ruby, Scheme, or Java.
Some of YYYYYYYY's notable features:
Uses an elegant syntax, making the programs you write easier to read.
Is an easy-to-use language that makes it simple to get your program working. This makes YYYYYYYY ideal for prototype development and other ad-hoc programming tasks, without compromising maintainability.
Comes with a large standard library that supports many common programming tasks such as connecting to web servers, searching text with regular expressions, reading and modifying files.
YYYYYYYYY's interactive mode makes it easy to test short snippets of code. There's also a bundled development environment called IDLE.
Is easily extended by adding new modules implemented in a compiled language such as C or C++.
Can also be embedded into an application to provide a programmable interface.
Runs anywhere, including Mac OS X, Windows, Linux, and Unix.
Is free software in two senses. It doesn't cost anything to download or use YYYYYYYY, or to include it in your application. YYYYYYYYY can also be freely modified and re-distributed, because while the language is copyrighted it's available under an open source license.
Если вы спросите — «зачем нужны эти странные разнообразные требования», то ответ прост — и люди, и задачи у них разные. Кому-то и бэйсик — язык программирования, а кому-то и хаскель недостаточно чист.yarric
05.03.2017 14:26Вообще-то dplsoft про это и говорит. Характеристики написаны в духе "хочу лямбды и монады", "хочу как в Java и чтобы всё было легко писать". Хотелось бы по-другому: "согласно опыту разработки нужны фичи X и Y, поскольку они полезны в случаях A и B". Для примера почитайте MISRA C — стандарт, который определяет подмножество языка C для применения в mission-critical проектах. Там перечисляются конкретные требования и обосновывается, зачем они нужны.
sergeperovsky
08.03.2017 22:12+1Вы хотите от индустрии научного подхода. Увы.
Пока будешь семь раз мерить, все уже изрежут.yarric
09.03.2017 19:03MISRA — вполне индустриальная организция. И Ada тоже создавалась с учётом опыта разработки. Сейчас с языками часто обратная ситуация: язык создан для встраиваемых систем — а его прикручивают к мейнфреймам, или язык создан теоретиками, а его пытаются в продакшн.
bormotov
05.03.2017 12:54про форматирование, — отлично. Современная наука software ingenering показывает, что единый coding style — выгодно с точки зрения бизнеса. Есть экономия, которая на больших объемах выливается в заметные деньги. Перенос этого момента на уровень языка, может быть и спорный шаг, но он не противоречит бизнес-целям. Возьмите любой популярный ныне инструмент, у всех есть те или иные средства форматирования исходников к единому виду.
0xd34df00d
06.03.2017 02:45Я с вами полностью согласен, но ради будущих холиваров с коллегами, на что можно было бы предметно сослаться по части показывающей выгодность единого стиля науки?
bormotov
06.03.2017 20:51Для меня это, как «солнце встает на востоке», но отличный вопрос, некоторым людям нужен пруфлинк, иначе они не воспринимают, даже очевидные вещи.
Первое, что я сделал — погуглил «why styleguide matter». Но из более-менее общего сходу вылезла только статья https://www.smashingmagazine.com/2012/10/why-coding-style-matters/
на какое-то исследование не тянет совсем. Еще вылезли пачки готовых style guide, на очень много какие вещи.
Но самоеудивтельное для меня оказалось статья в википедии https://en.wikipedia.org/wiki/Style_guide
если точнее, кот этот абзац:
A style guide establishes and enforces style to improve communication. To do that, it ensures consistency within a document and across multiple documents and enforces best practice in usage and in language composition, visual composition, orthography and typography. For academic and technical documents, a guide may also enforce the best practice in ethics (such as authorship, research ethics, and disclosure), pedagogy (such as exposition and clarity), and compliance (technical and regulatory).
еще интерсная цитата нашлась в PEP-8 питоновском:
Donald Knuth explains the traditional rule in his Computers and Typesetting series: «Although formulas within a paragraph always break after binary operations and relations, displayed formulas always break before binary operations»
со ссылкой Donald Knuth's The TeXBook, pages 195 and 196.
Дальше — больше! нашелся документ 1995 года, с такими словами
The classic text on programming style remains Kernighan and Plauger The Elements of Programing Style (1974)
И к этой книге нашлась интересная аннотация (и небольшой обзор), которая начинается со слов
This book written by Brian W. Kernighan and P. J. Plauger is one of the first studies of programming style. It advocates the notion that computer programs should be written not so much to satisfy the compiler or personal programming «style», but also should strive for general «readability» and «understandability» by humans with the specific goal to make software maintenance easier.
вот еще статья, но опять-таки, не научная, без цифр
http://www.perlmonks.org/?node_id=776607
А еще нашлась ссылка на McConnel, «Code Complete», которая тоже просто книга, а не научный труд. Но там в PDF'е 897 страниц, из них последние 30 — ссылки на источники. Есть ссылка в том числе и на «The Elements of Programming Style», и например, на
Woodfield, S. N., H. E. Dunsmore, and V. Y. Shen. 1981. «The Effect of
Modularization and Comments on Program Comprehension.» Proceedings of the Fifth
International Conference on Software Engineering, March 1981, 215–23
туда ссылается вот такой текст:
Woodfield, Dunsmore, and Shen conducted a study in which
graduate and senior undergraduate computer-science students
answered questions about two programs: one that was divided into
eight routines along functional lines, and one that was divided into
eight abstract-data-type routines (1981). Students using the
abstract-data-type program scored over 30 percent higher than
students using the functional version.
Конечно, это не только про форматирование текста исходника…
Думаю, коллег, которым именно нужен пруфлинк, можно смело отсылать в «Code Complete», и далее по ссылкам.
p.s. Нет, я не знаю как научно обосновать, что солнце встает на востоке0xd34df00d
07.03.2017 06:03Спасибо за ссылки!
Я встречался с довольно любопытным аргументом, что отсутствие глобального стиля кода и, как следствие, разрешение на использование произвольного собственного стиля даже в уже имеющихся проектах, если изменения достаточно изолированы, приводит к тому, что разработчики более счастливы и более удовлетворены жизнью, что повышает их продуктивность.
bormotov
07.03.2017 08:10Как только в проекте работает людей больше чем один, важно, что бы они не просто были каждый счастлив, а двигались в одну сторону.
Посмотрите по ссылкам, везде разговор о стиле начинается со слов «общий стиль облегчает общение». Чем больше людей, тем важность «двигались в одну сторону» сильнее перевешивает важность личного счастья каждого, потому что количество общения внутри коллектива растет экспоненциально. И явного, и неявного — через написание-чтение кода.
Нет, я не верю, в сферических коней в вакууме, и в то, что солнце встает на западе.
Не понимаю, как два разработчика, если одному нравится отступ в два пробела а другому в четыре, могут работая в одном проекте, быть более счастливы, более удовлетворены жизнью, и работать эффективнее, если им разрешить каждому писать код в своём стиле.
Для бизнеса выгоднее подавлять «собственный стиль» даже выраженный в алгоритмах и архитектуре. Единственный практический способ не подавлять — «взваливать» на такого человека «всех остальных», что бы он всех тянул. Но это равнозначно тому, что у всем остальным насаждать его стиль. Иначе, если код понятен только одному человеку, то фактически один человек работает. А нужно, что бы десяток или несколько десятков.
Это же простая арифметика: если написать код красиво у «художника» занимает 5 минут, а потом у десятка «маляров», чтение кода вместо 5 минут занимает 30, то бизнес теряет 250 минут. Если «художник» потратит 30 минут на написание кода, который «маляр» поймет за 5 минут, то потери будут всего 25 минут (плюс, конечно, личное несчастье, и неудовлетворенность «художника»). А что бы бизнес ничего не терял — разрыв нужно сокращать.
Внезапно, единый стиль оформления исходника — это первый шаг к сокращению разрыва, к увеличению эффективности общения в команде. Как только команда начинает говорить на «одном языке», начинает сокращаться разрыв между «художником» и «малярами» по остальным пунктам.vintage
07.03.2017 08:36Учитывайте ещё, что время художника стоит гораздо больше времени маляра.
А так, не стоит смешивать в одну кучу атипаттерны типа "всё в одну строку и фиг разберёшься" и вкусовщину типа "ай, пробел не с той стороны, дизлайк, отписка". Нужно всё же вырабатывать в себе толерантность к чужому коду, чтобы не испытывать потом боль и страдания, при переходе в другой проект, с другими правилами расстановки пробелов.
bormotov
07.03.2017 09:33Конечно художник в единицу времени приносит больше ценности бизнесу. Но если он за дополнительные 25 минут сможет заменить 250 часов работы маляров — зачем тогда маляры?
О какой толерантности может идти речь, если яркие личности со своим стилем становятся счастливее и работают эффективнее? (на минутку стал на другую сторону)
На мой взгляд, единый стиль — это и есть та самая середина, которая не вызывает боль и страдания ни у кого в команде, к которой все как минимум толерантны, и соблюдение которой не снижает персональную эффективность.
И хочу акцентировать — в общем случае стиль затрагивает не только в форматировании исходников.vintage
07.03.2017 12:06Боль, страдание и шизофрению вызывает необходимость придерживаться разных стилей, работая в разных проектах попеременно. Поэтому вместо того, чтобы заставлять всех писать как один, лучше учиться читать, не обращая внимания на несущественные особенности. А то доходит до смешного — некоторые не могут прочитать JS код пока не расставят точки с запятой в конце каждой строки.
bormotov
07.03.2017 12:47У нас в рамках компании один стиль. Сколько бы проектов не было.
не обращая внимания на несущественные особенности
Вы считаете, что мои слова «стиль затрагивает не только форматирвание» — это несущественные особенности, и не обращаете на них внимание?
Есть еще интересный факт — умение читать разные стили, совсем не избавляет от требования писать в рамках одного проекта в одном стиле (который принят в этом проекте). Даже не касаясь производительности и бизнес-показателей — это элементарное уважение к коллегам.
Точки с запятой в конце строк — смешно, а вот, скажем, алиасы таблиц в sql-запросах, единый подход к выбору имен идентификаторов — тоже смешно? Идем дальше — единый порядок аргументов функций для API?
Вот, из недавного, что меня удивляло: модуль, который дает удобные методы работы с целым пластом данных. Среди них всякие выборки/срезы. Среди них — есть срезы по времени. Для меня очевидно, что если функция принимает аргументы date_start, date_end, то у всех функций с аналогичной семантикой в этом модуле (а лучше, во всём проекте), если есть два таких аргумента, они как минимум должны называться одинаково.
А еще, например, в Oracle PL/SQL, нет удобного способа вернуть курсор с данными поэтому у процедур из которых нужно возвращать данные, последний аргумент что-то типа P_RC IN OUT REF_CURSOR.
Хорошее требование, всегда этому аргументу давать одинаковое имя? Или RC, RC1, P_RC, P_RC1, P_CURS, CUR — нормально, все ведь прочтут, все натренировали толерантность и не испытывают боль?
Пример с RC/итд из реального исходника -
— 91 штука всего, 6 разных вариантов, преобладает P_RC.grep "OPEN .* FOR" some_source.pkg
vintage
07.03.2017 21:56А у нас много компаний, и каждая устанавливает свои требования.
Хорошие примеры и все их можно обосновать как "хорошие практики". Поэтому я и написал:
не стоит смешивать в одну кучу атипаттерны типа "всё в одну строку и фиг разберёшься" и вкусовщину типа "ай, пробел не с той стороны, дизлайк, отписка".
К сожалению, хорошие практики довольно плохо поддаются линтингу в отличие от поверхностной вкусовщины.
sergeperovsky
08.03.2017 23:15+2Пока программист не осознАет, что пишет не только ( и не столько) для компилятора, но и для других программистов, которым придется изучать и модифицировать его текст, его нельзя брать в серьезную разработку.
Конструкторов «с детства» учат соблюдать ЕСКД. Проектировщиков-строителей ЕСПД. Зачем? Чтобы ЛЮДИ легко друг друга понимали.
Программы типа «Hello, World!» плохи тем, что скрывают эту сторону вопроса. Написал, запустил, работает. Просто и понятно. Буду программистом.
yarric
05.03.2017 13:31Было бы хорошо, если бы новые языки действительно предлагали какие-то новые подходы к решению задач программирования. А поскольку какие-то новые подходы придумать трудно, то в большинстве случаев "новый" язык — это просто случайная смесь фич Java, Haskell, Ada, C++, LISP и т. д. по вкусу авторов, в итоге вместо программирования приходится тратить время на изучение взглядов автора на схему наименования стандартных библиотечных функций, указателей на собственный и родительский класс и названия стандартных типов данных.
alexeikh
05.03.2017 04:01+1В целом с комментом согласен, но наезд на Руби не в тему.
Руби 1.8 вышел в 2003 году, а Руби 2.0 в 2013. Так что с «2.5 года» вы как-то погорячились. И уж не помню, что там сломали в 2.0, но помню, что весь старый код, который я пробовал с новой версией интерпретатора, продолжал работать. Не спорю, что какие-то мелочи может и сломали, но, читая вас, создаётся впечатление, что прям вообще всё сломали и на новой версии даже и пытаться не стоит запускать старый код. Это, конечно, не так.
Выберите лучше другой пример. И проверьте его на достоверность. Или не приводите вообще.dplsoft
05.03.2017 05:41http://citforum.ru/news/30515/
ну я же говорил, что меня надо поправить:
* не 1.8, а 1.8.7
* не 2.0 а «1.9.3, которую постепенно вытеснит ветка Ruby 2.0».
* не 2.5 года, а почти 5 лет.
так, что это не наезд, а констатация типовых проблем. это мы еще дотнет 3.5 и дотнет4 не обсуждали.
весь старый код, который я пробовал с новой версией интерпретатора, продолжал работать.
вам крупно повезло, имхо. а большой ли был проект, сколько человек и как долго писали, часто ли перерабатывали?
я же не говорю что проблемы будут гарантированно. вернее они будут почти гарантированно только при большом объеме кода, но на маленьких скриптах, это почти не заметно, и даже не факт что вы наступите на то, что поломали создатели языка.khanid
05.03.2017 13:34дотнет 3.5
Не знаю, как у программистов, но вот этот 3.5 — головная боль администраторов.
Mingun
05.03.2017 13:13Все-таки наезд на обратную совместимость необоснован. В современном мире это должно делаться автоматическим рефакторингом. В конце концов, если проект развивается, то он должен следовать в общей канве, а не отдаляться от нее, иначе через какое-то время его просто некому будет развивать. Если не развивается, то какая вам разница, что там поломали в версии Y? Пользуйтесь версией X
dplsoft
06.03.2017 02:14В современном мире это должно делаться автоматическим рефакторингом.
Эээ… а что вы имеете в виду под «автоматическим рефакторингом» в контексте языков с динамической типизацией? руби же, кажется, язык с динамической типизаицей?
Как уже было упомянуто, нет методов провести автоматический рефакторинг кода для языка с динамической типизацией — потому, что вы не можете узнать, что именно вам подсунут в значение переменной до момента рантайма и сработает у вас обращение к измененному методу или нет. Это не говоря о ситуации, когда у вас функция вызывается в нескольких местах, и вам подсовывают разные типы в один параметр.
Весь рефакторинг кода при динамической типизации — это смесь ручного труда и обработки баг-репортов после того как у вас не прошел очередной тест.
А когда у вас большой продукт — ручной рефакторинг с масштабным тестрованием — это ОЧЕНЬ серьезная проблема. Иногда даже проще написать продукт с нуля. На языках со статической типизацией. джаве/сиДиезе/паскале/сиПлюсПлюс — там, где автоматический рефакторинг возможен.
Если не развивается, то какая вам разница, что там поломали в версии Y? Пользуйтесь версией X
«Нельзя пользоваться версией X, потому что она снимается с поддержки» => никаких исправлений ошбок, заплаток для безопасности, никакой линии техподдержки и ответов производителя по вашим багам.
MrGobus
05.03.2017 09:37Ахаха, забавная статья про «Hello World» в которой ни разу не используется фраза «Hello World». Прямо «Hello World» а 0 байт =)
Dessloch
05.03.2017 10:48А по-моему авторы «Hello, World» правы:
a="" a='' a=120 a=120.0
и нет необходимости в дополнительных string, char, integer, double.netch80
05.03.2017 13:24А почему Вы решили, что 120.0 будет обязательно double? Может, это single («float» в C и потомках). А может, вообще должно быть тут десятично точное значение и применена десятичная арифметика, чтобы никакие 1.1 не надо было округлять в мелких знаках.
120 — почему integer, а не short или long? И даже почему не double?
На примере констант это всё не видно, но как только начинаются реальные операции с данными, тип вдруг начинает иметь значение.masai
05.03.2017 17:08Скажем, в C литерал 120 — это точно int, а 120.0 — точно double.
netch80
06.03.2017 11:03Скажем, в C литерал 120 — это точно int
120 — да. 120000, например, уже не обязательно. В C99, например, сказано следующими словами:
The type of an integer constant is the first of the corresponding list in which its value can be represented.
Если на платформе int 16-битный, 120000 автоматически станет long.
Но тип константы тут не так важен. Важнее то, что будет, если мы обопрёмся на это вычисление типа при назначении его переменной, и значение потом будет меняться, а тип — нет. Например, если кто-то захочет опереться на то, что в Java или Go целочисленная арифметика всегда урезает до последних N бит, даже знаковые, и у него тип переменной всегда окажется int64 вместо int32, результаты будут неожиданными. Или в C# написать 2.5 вместо 2.5m; многим такое очень тяжело распознать, даже вглядываясь в код.
Поэтому автоматическое определение типа хорошо, если далее значение переменной не заменяется. "Внутреннее" изменение может быть; есть случаи, когда автоопределение удобно и при изменении — например, итераторы. Но даже "x += 1" уже даёт стрёмный вариант (а что, если это оказался float более чем 2**24 по модулю?), лучше избегать автоопределения и задавать тип явно.
(Хм, тут исключений больше. Например, стандартная арифметика над float?ами может адекватно работать при любом их типе. Но это опять же не общий принцип.)
yarric
05.03.2017 14:34+4Самое весёлое начинается тогда, когда нужно передать значение в функцию, а функция даже не декларирует, какого типа значение ей нужно (типы-то нигде не указаны). То ли функции нужна строка-имя файла, то ли открытый поток ввода-вывода — иди, читай доки или исходный код. И так каждый раз.
sergeperovsky
08.03.2017 23:31Все зависит от цели.
Одна команда написала систему анализа движения воздуха методом конечных объемов.
Как в большинстве вычислительных задач, алгоритм и тип данных оптимизирован под скорость работы.
Встал вопрос о точности результатов. Я предложил использовать вместо double новый тип «физическая величина» из двух double — «значение» и «погрешность». И заменить стандартную арифметику на принятую в физике: при сложении и вычитании погрешности складываются, при умножении и делении подсчитываются соответствующим образом.
Работать будет гораздо медленнее, но для исследования применимости расчетов к различным задачам очень удобно.
Вроде бы, задача не много сложнее рефакторинга. Но программисты дружно отвергли идею — слишком сложно в реализации и отладке.
Интересно, в каком языке такое изменение не вызовет сложностей?vintage
09.03.2017 08:45В любом, поддерживющем алиасы типов и перегрузку операторов.
0xd34df00d
10.03.2017 21:45C++ в ваших терминах поддерживает алиасы типов?
vintage
11.03.2017 00:13+2Ну да. Только алиасы надо сразу использовать. Впрочем, поменять везде типы — не ахти сложности задача.
khrundel
06.03.2017 19:29+1На самом деле тип данных «целое в интервале» не является чем-то принципиально недостижимым в других языках. В том же C++ можно сделать шаблон с целочисленными параметрами и реализовать операторы, возвращающие обычный int и имплицитный конструктор из инта, бросающий исключение в случае не попадания в диапазон. Был бы
и дальше то же самое.typedef IntOfRange<1,120> Age;
Но я не замечал активной работы в этом направлении. Видимо, идея не то что бы полезная, так как варианты с несколькими значениями лучше реализуются перечислениями, а там где действительно нужно число, но не любое, ограничения обычно более сложные, по типу «индекс меньше или равен длине» или хотя бы «только чётные», так что валидацию делают внутри более сложного типа.
alexBondar
08.03.2017 08:54Не хватает только подписи «Информация прозвучала на правах рекламы продукта Ada… » :)
А если серьезно, то непонятно в чем мысль статьи. Как по мне здесь «смешались в кучу люди, кони...» — тут тебе и DRY и unit-тестирование и exception handling. Связь с hello world прототипами какая-то очень навязанная.
qbz
Ничего не понял, прочитав до конца. Причем тут Ада? Чем классический не раздутый хеллоу-ворлд плох для новичка? Новичек он поэтому и новичек, что потом только узнает про все остальное, а не сейчас.
Странная статья.
iig
Строгая типизация == раз скомпилировалось, значит работает. Спорное утверждение, программисты на С++ могут не согласиться.
yarric
Строгая типизация в этом сильно помогает.
Apathetic
Есть какие-то результаты статистических или иных исследований в поддержку этого утверждения?
yarric
Та же Ada ведь не с потолка была придумана.
Apathetic
При всём уважении, это вообще ни разу не ответ на мой вопрос. То, что Ада является языком со статической сильной типизацией — это не доказательство того, что сильная типизация в чем-то лучше слабой. Это лишь следствие того, что все варианты языка, представленные на конкурс, были основаны на Паскале.
yarric
Требования к языку Ada были разработаны на основе опыта проектирования больших программных систем, так что этот язык сам по себе является стандартом, на который можно ссылаться в подобных вопросах.
Apathetic
Нет, не является.
yarric
Ой, всё :)
Нет, ну если серьёзно — я не знаю ни одного другого языка, дизайн которого был бы так же хорошо обоснован на основе формальных требований, составленных по результатам опыта реальной разработки сложных систем. Есть ещё пара стандартов для языка C: MISRA C и Frama-C.
Apathetic
Формальные требования были описаны для конкретного контекста. В другом контексте требований могут быть (и будут) совсем другими.
dplsoft
java?
дизайн, скорректированный опытом реальной разработки сложных и «сверхбольших» систем на протяжении почти 20 лет. не? :)
yarric
Java вообще задумывался как язык для программирования умных микроволновок, о сверхбольших системах авторы и не думали.
dplsoft
но в итоге то, микроволновки выросли в майфреймы.
yarric
Тем не менее никто не пытается её применять в медоборудовании или авиации. А какая область применения у неё на мейнфреймах?
dplsoft
гм… «основной язык для описания бизнес-приложений», если это можно так назвать. ibm-ы традиционно выделяют отдельный процессор специально под задачи джавы. (я говорю про айбиэмовские майнфреймы, потому, что иные производители, куда-то исчезли)
в нашей стране я видел следующее: нашу с вами пенсию учитывают, грузовыми жд-перевозками по стране управляют, банковские платежи в цб обрабатывают.
ps: это такой тролинг, или вы серьезно не в курсе «области применения на майнфреймах»? давайте обсудим.
dplsoft
Насчет медицины и авиа ничего не могу сказать, не сталкивался. Лично по мне, так си для этих целей — самое то.
Вы хотите сказать что ада применяется в медицине и авиа (в смысле «до сих пор»)?
Я не для спора, мне скорее интересна фактическая ситуация.
drafterleo
В медицине можно выделить два полюса приложений — учётно-статистические и оперативно-мониторящие. В чистой учётке каждый извращается как может — вплоть до 1С и макросов на Excel, но чем ближе к пульсирующему в realtime телу (когда жизненно критична скорость обработки и отклика), тем больше C\C++ и меньше экзотики. Полагаю, в авиации похожая ситуация. А так, в целом, и SCADA-системы на C# пишут и ЧПУ-станки питоном программируют.
yarric
Насколько мне известно, для серьёзных бизнес-задач серьёзные конторы используют специализированные языки, вроде SAP ABAP. Понимаю, что в целях экономии многие готовы писать свои системы хоть на PHP, но честно говоря, специально спроектированные инструменты вызывают гораздо больше доверия.
Есть немалый список проектов, в которых используется Ada. Обычный C в mission-critical проектах не используется, используется подмножество MISRA C или Frama-C. Кстати интересно заметить, что для обеспечения надёжности эти стандарты выкинули из C много фич — к теме о бесполезных нововведениях в языках. Что касается C++, то преимущества его использования по сравнению с Ada мне не понятны, разве что программистов найти легче.
drafterleo
По поводу SAP ABAP углубляться не буду, однако замечу, что случаев удачного внедрения (выливающегося в экономический плюс) ненамного больше случаев неудачного или даже катастрофического (непомерные затраты и адские страдания рядовых пользователей, которые помимо своих основных обязанностей оказываются вынуждены обслуживать это инфомационное чудище-юдище), при том, что разработчки и внедренцы при любом раскладе получают нехилый доход (ибо казино всегда в выигрыше).
Иногда мне кажется, что если бы разработка шла с нуля, снизу-вверх (пусть бы даже и c привлечением PHP :)), получилось бы куда эффективней. По крайней мере, разработчики реально бы вжились в бизнес-процессы, а не пытались навязывать «универсальное решение».
Против Ada (как подхода) ничего не имею, идея синтезировать статический анализатор с компилятором вполне себе разумная мысль (нечто подобное пытаются осуществить и упомянутая Farma-C, и набирающий популярность Rust). Но ведь это же не единственный подход. Есть, например, TDD. Опять же, статический анализатор к C++ всегда можно подключить «сбоку». Это как выбор между кухонным комбайном (всё в одном) и набором специализированных устройств (блендер, соковыжималка, мясорубка и т.д.).
Что касается «новых фич», уверяю, ни в одном из языков (в классической вычислительной парадигме) никогда не будет «новой фичи», которой бы до этого не было в Lisp. Да и фича, фиче рознь. По крайне мере то, что я использую из C++[11-14], (на мой взгляд) существенно понижает вероятность ошибок, повышая удобство разработки.
0xd34df00d
Лучше, когда статический анализ сделан гармонично с языком в виде системы типов.
drafterleo
Кто ж спорит, однако у этой гармоничности есть своя цена. В качестве аналогии — ифкуиль на уровне грамматики «заставляет» выражать свои мысли чётче и однозначней и в этом гораздо круче любого из естественных языков. Засада в том, что мало кто из реальных людей (даже теоретически) способен его выговорить (58 фонем с тонами!), свободно сочетая многомерные грамматические таблицы по вариативными правилами организации формативов.
Vjatcheslav3345
А зачем собственно что то выговаривать? Главное — суметь точно мысль выразить (как в математике).
Вполне можно представить себе спецификацию С++ написанную рабочей группой по языку на урезанном подмножестве ифкуиля (без фонетики, которая там создаёт проблемы — только смысл, воспринимаемый с экрана) с введёнными дополнениями, которые будут аналогами команд Git, нужными для работы редакторов и автоматического слияния веток с логическими (а не символьными) изменениями а также — разрешения конфликтов. Тогда ифкуиль и его подмножество будут соотноситься как SGML и XML.
drafterleo
Для этого придуман ихьтаиль (ictail) — оригинальная система письма ифкуиля :). Графический вариант, действительно, считывать проще, хотя семантические корни, основанные на котрежах согласнных фонем (да и вообще — фонотактика) и разноплановые сочетания многомерных грамматических матриц никуда не деваются.
0xd34df00d
Я правильно понимаю, что эта аналогия призвана показать, что на каком-нибудь там хаскеле мало кто из реальных людей писать может?
drafterleo
На провокационные вопросы не отвечаю :). Однако обращу внимание, что в данной теме речь не о каком-нибудь хаскеле (который по сравнению с «массовыми языками» скорее непривычен, чем сложен), а про вполне конкретную Аду. Кроме того замечу, что знаком с упомянутыми языками весьма поверхностно и все мои высказывания по их поводу следует воспринимать не иначе, как личное мнение при отсутствии профессионального опыта применения.
drafterleo
P.S. Кстати, я не считаю Ada сложным языком (учитывая, что в своё время съел собаку на Object Pascal) и поэтому, если бы, вдруг, пришлось включаться в проект на этом языке, не морщился бы и не плевался. А вот, например, сказать, что достаточно хорошо знаю C++ (несмотря на то, что программирую на на нём много лет), я бы, положа руку на сердце, не рискнул ;).
netch80
LISP до начала стандартизации Common LISP или вообще весь со всеми диалектами? (тогда — нечестно)
Системы типов с автовыводом, а-ля ML, Haskell?
Или это уже не "классическая вычислительная парадигма"?
drafterleo
Я скорей подразумевал Lisp как унифицированный синтаксический ассемблер (в том смысле, что программа на нём имеет максимально «плотный» доступ к своему синтаксическому дереву). Поэтому при большом желании в Lisp можно реализовать любую идею (в том числе и статический вывод типов) не выходя за рамки языка (кроме «синтаксического сахара» — всякий реализованный принцип будет иметь специфический лисповский видок :)).
dplsoft
>>Насколько мне известно, для серьёзных бизнес-задач серьёзные конторы используют специализированные языки, вроде SAP ABAP
«Бизнес-задачи» бывают разные. «Бизнес-задачи» — это не только то, чем занимается «коммерция» и «бизнес».
Не путайте конкретные учетные задачи, на которые заточены компоненты SAP и задачи автоматизации бизнес-процессов.
Например АСУТП писать на SAP — это надо сильно удариться головой.
Не стоит писать систему реального времени контроля за грузоперевозками в масштабе страны на SAP. Можно, но в здравом уме это никто делать не будет. Потому там майнфреймы IBM, DB2, Java.
А вот бухгалтерию и зарплату под объемы РЖД — самое дело — SAP там лег как родной) (а на самом деле просто потому, что 1С не тянул эти объемы).
ABAP хорош только в контексте учетных механизмов SAP, а как сам язык, сужу по отзывам — не очень катит супротив даже 1С.
ABAP — конкурент 1С, а не Java/С++…
sshikov
Вы знаете, я читал еще самые первые документы по Ada. Так уж сложилось — я как раз делал диплом, и записался в ГПНТБ, и почитывал все что попадалось интересного. Так вот — то что было изначально заложено, это опыт разработки сложных систем на тот момент. Т.е. грубо говоря — 70-е годы прошлого века для первой версии языка.
С тех пор много воды утекло, очень много. Что-то конечно в Ada меняли, но что-то и осталось с тех пор. А взгляды на то, как нужно делать сложные системы, они с тех пор могли и поменяться — сложность-то поменялась.
Я к тому, что наличие формальных требований — это не догма. В этой области тоже много чего изменилось с тех пор.
yarric
В Ada тоже добавляют новые фичи, последний стандарт вышел в 2012-м году. Большинство широко используемых сейчас современных языков появились в конце 80-х — начале 90-х. В мейнстримовых языках вообще-то довольно трудно найти какую-то концепцию, которой меньше 20-ти — 30-ти лет.
sergeperovsky
Ада — одно из величайших разочарований в моей жизни :)
Хорошо составленные требования были реализованы без какого-либо анализа. Есть требование — вводится соответствующая конструкция.
В результате, описание синтаксиса Паскаля укладывается в 30 определений, а Ада потребовала 180. Держать в голове такой компилятор не удается, а без этого производительность программиста резко падает.
Так что, «хорошо обоснован» еще не означает «хорошо сделан».
yarric
Интересно тогда, во сколько определений укладывается описание C++ или Java, или Rust. Подозреваю, что не сильно меньше. При этом, например в случае с C++, всяких undefined behaviour и подводных камней явно больше.
sergeperovsky
По моему, никто и не пытался составить формальное описание С++. Дело в том, что С++ создавался без формализованных требований. В первых изданиях своей книги Страуструп подробно описывал, что пытался (и не сумел) сделать. Ему требовался инструмент имитационного моделирования. Описание Симулы-67 он нашел, а работающую реализацию нет. Он стал дописывать С, постепенно добавляя необходимые механизмы. Работа осталась незавершенной. Но появились пользователи и первоначальная цель была отставлена.
ЗЫ: Борланд делал компилятор С++, там в документации, что-то похожее на формальное описание было. На полсотни страниц.
yarric
Ну вот, к примеру, стандарт C++ 2011 — 1336 страниц. Причём за него надо будет ещё заплатить.
0xd34df00d
Это не формальное описание. Формальное описание — это строго математическое описание в терминах, скажем, денотационной или операционной семантики.
Как пример попыток.
sergeperovsky
Это совсем другое дело.
Давным давно, я пытался доказывать, что язык, это не только синтаксис, но и словарь. И среди языков выиграет тот, в стандарт которого будет включена обширная библиотека.
Собственно, это и случилось. При сложном и, я бы сказал, неопрятном синтаксисе, С++ стал популярен, благодаря стандартизации библиотек. Это, действительно, требует сотен страниц описаний.
yarric
Кстати такой же стандарт ISO/IEC по Ada 2012-го года почти на 400 страниц меньше — тоже с описанием библиотеки.
Судя по тому, что для С++ придумано много сторонних библиотек типа Qt, Boost и т. п., со стандартной библиотекой там как раз не всё гладко.
sergeperovsky
Встроенные библиотеки, библиотеки сторонних разработчиков, библиотеки команды разработчиков и личные библиотеки программиста всегда были есть и будут. Идет миграция функций из частных библиотек в общие, но конца ей нет.
Я говорил только о том, что в «языках первого поколения» описывался только синтаксис, а встроенная библиотека отдавалась на произвол конкретной реализации языка.
Как только встала задача перехода к индустриальному производству программ и обеспечении реальной переносимости ПО, стандартизация встроенной библиотеки на уровне языка, а не его реализации, стала необходимостью.
Некоторое противоречие в том, что по единству и компактности синтаксиса предпочтительнее «авторские» языки — созданные одиночками. Но создание достаточно обширной и хорошо структурированной библиотеки одиночке не под силу.
Поэтому и были надежды на проект Ада: за основу берется «ортогональный» синтаксис Паскаля, позволяющий почти все минимальным количеством конструкций, и дополняется обширными хорошо оттестированными библиотеками, создающими следующий слой языка. Но расширение синтаксиса, оказалось необходимым и сделано оно было крайне неряшливо. Не просто дополнение несколькими новыми конструкциями, а многократное увеличение их количества.
dplsoft
прямых доказательств нет и не будет))) зато есть куча косвенных. умеющий уши да слышит))) шучю. но ситуация примерно такова и есть, имхо.
полномасштабных исследований наверное нигде нет, разве что какой нибудь институт, который поддерживает методы оценки трудоемкости разработки, в результате своих исследований, выставит завышенные коэффициенты связанные с языком напротив языков с динамической типизацией.
но вот проблема в том, что серьезные большие институты, ориентированные на исследование промышленных методов разработки ПО — не очень интересуются языками с динамической типизацией. ну… мне так показалось, когда я исследовал вопрос о методиках оценки трудоемкости разработки.
я говорю о методиках оценки типа 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 часов.
то что вы скептически относитесь к утверждениям, что статическая/жесткая типизация помогает контроллировать код — мне очень понятно. я сам был таким))
но поразмыслите детально, и педантично.
dplsoft
PS: Прошу прощения за очепятки и «прямоток» без заглавных букв.
Apathetic
Мы вроде говорили о сильной/слабой, а не о статической/динамической типизациях. Ортогональные друг другу вещи, тот же Руби, о котором вы часто пишете, — язык с сильной, хоть и динамической, типизацией. Как и Python, например. При этом в C++ типизация слабая, но — статическая.
> я сам был таким))
Не очень понятен этот снисходительный тон.
Скажу сразу — я сам адепт typescript, например, и мне глубоко понятно желание видеть в своем коде сильную статическую типизацию.
Но, тем не менее, да, я скептически отношусь к утверждениям, что сильная типизация однозначно «лучше» слабой, потому что существуют разные кейсы и разные контексты применения языков. Где-то важнее надежность, которой сильная типизация безусловно способствует (хоть и не гарантирует), где-то важнее скорость разработки.
И до тех пор, пока не будет исследований, показывающих, какая типизация действительно лучше с точки зрения, подчеркиваю, бизнеса/заказчика в различных контекстах применения, я всегда на подобные споры и попытки их разведения буду смотреть осуждающе.
areht
> где-то важнее скорость разработки.
«Строгая типизация == раз скомпилировалось, значит работает» — а где здесь про скорость разработки? Или про «бизнеса/заказчика в различных контекстах применения»?
Похоже, Вы сами в какой-то момент подменили тезис на «лучший язык — со статической типизацией», который и опровергаете.
Apathetic
Во-первых, опять же, при чем тут статическая типизация? Речь в комментарии идет о сильной.
И я ничего не подменял. Вы цитируете мой комментарий, который является ответом уже на совершенно другое высказывание. Не надо демагогией заниматься.
areht
Во-первых, вы не ответили на вопросы. Так с каким именно тезисом из какого комментария вы спорите то?
Apathetic
Я вообще не спорю. Я дискутирую.
Прочитайте еще раз комментарий, на который я отвечал, и вопросы у вас отпадут.
areht
То есть по делу ответить нечего, так и запишем.
> Я вообще не спорю. Я дискутирую.
> ДИСКУ?ССИЯ, -и, жен. Спор, обсуждение какого-н. вопроса на собрании, в печати, в беседе.
> СОФИЗМ (от греч. sophisma — уловка — выдумка, головоломка), мнимое доказательство, в котором обоснованность заключения кажущаяся, порождается чисто субъективным впечатлением, вызванным недостаточностью логического или семантического анализа.
Apathetic
Вы сейчас демагогией занимаетесь. Повторяю: прочитайте комментарий, на который я отвечал, и ваши вопросы отпадут.
areht
Уже отпали, что уж. Argumentum ad nauseam на мне можно не практиковать.
Apathetic
Какой вы интересный, однако. Такое ощущение, что вы сюда в демагогии приходите упражняться =)
Всего доброго!
areht
Повторяю, обвинять меня в демагогии можно сколько угодно, но от прямого ответа уклонились вы.
Apathetic
Давайте я объясню попроще. Вы процитировали мой комментарий, подразумевая, что он является ответом на комментарий yarric, в то время, как на самом деле он является ответом на комментарий dplsoft. Ваш вопрос (и обвинение в том, что я выдумываю какие-то тезисы и с ними спорю) основывается на ложной предпосылке. Я указал на это. Вы настаиваете и снова обвиняете меня в уходе от ответа. Это называется демагогия =)
Я в очередной раз рекомендую вам ознакомиться с комментарием dplsoft. Я отвечал на этот комментарий, и ни на какой другой.
areht
Вы отвечали на оба. А рассказывать мне что я подразумеваю не надо.
Apathetic
О, а это уже прогресс!) Осталось совершить последние усилие над собой — и таки прочитать тот самый комментарий, на который я отвечал!
areht
Прогресс? То, что вы передёргиваете и занудствуете было понятно сразу. Там же я написал, что вопросов больше не имею. Что-то ещё?
Apathetic
И это я-то передергиваю) Оставлю это на вашей совести =) Еще раз — всего доброго!
dplsoft
Крайне редко говорят про сильную и слабую типизацию.
Вы первый или второй. Обычно говорят про динамическую-vs-статическую типизацию.
Особой корелляции между сильной-слабой типизацией и проблемами с разработкой не отмечал.
Проблемы с динамической типизацией изложил — с ростом объема кода, проблемы в разработке на динамических языках прастут «очень не линейно». Проблемы со статической типизацией — относительно линейно. Это мои оценки и наблюдения.
Тогда у нас дед-лок))
Исследований нет, и наверное не будет, ибо некому за это заплатить. нет — исследований — будет постоянное осуждение. Будет осуждение — проблему скорее всего не решить. Но принимать-то архитектурные решения надо! И стажеров надо обучать! Как быть?
Хотя согласен, в том плане, что попытки разведения неконструктивных споров надо подавлять в зародыше.
С моей же точки зрения, если выбирать между «статикой и динамикой» в типизации при выборе языка разработки, то мой опыт говорит мне следующее: выгода зависит от объема разрабатываемого кода. ( и я сейчас буду говорить только выгодах проекта, вытекающих из минимизации трудозатрат и технических рисков. Если последствия неадекватных технических решений будут лежать на ком-то другом, или «бизнес» планирует «отскочить» до проявления проблем или «еще как-то» (вплоть до полного наплевательства на последствия, или полного одупления (непонимания лицами принимающими решения технических проблем) ) — то «бизнес», естественно, будет принимать решения совсем с других, не связаных с техническими аспектами, позиций )
и так:
* на коротком периоде, для небольших задач, одноразовых, скриптования поведения «жесткой части» — удобнее/выгоднее динамическая типизация. Низкий порог входа, быстрое прототипирование — позволят экономить бюджет. До тех пор, пока кривая проблем не начала расти (не сдвинулась с места), или не начался рефакторинг, или число модулей в вашей системе не выросло до двух.
Отсюда и область применения: скрипты инициализации, тестирование, небольшие хранимки в бд, сайты с линейной логикой работы, различные примитивные СМО и пр.
* на длинном периоде, при относительно большом коде, с рисками изменения бизнес-логики, для задач сложнее чем на одну неделю, для кода которые надо будет поддерживать — лучше сразу начать писать на языках со статической типизацией ( возможно с включением скриптов на динамических языках ). Потому что линейный рост числа проблем (а не галлопирующий экспонентой) в зависимости от объема кода — очень даже комфортен. Причем выбирать стоит те языки, у которых как можно меньше проблем с обратной совместимостью.
А корелляции между «сильной» и «слабой» типизацией я не заметил.
Я не могу запретить вам его осуждать, но рассуждения, особенно логически стройные, буду рад услышать.
Я принял вас за очередного молодого адепта «новомодных языков». Извиняюсь)
vintage
На самом деле, в современных языках статическая типизация оказывается даже быстрее при написании скриптов, потому что:
String( arg ).toLowerCase()
netch80
> на надо гуглить какое там апи у такого-то модуля — среда разработки сама адекватно всё подскажет.
IDE с поддержкой динамически типизированных языков уже существуют и активно развиваются.
> не надо запускать и дебажить для того, чтобы обнаружить глупую опечатку — компилятор и даже среда разработки понимают, что ты написал, и как можно раньше рассказывают тебе о проблемах.
Аналогично, для динамически типизированных языков есть средства линтинга, и профессионалы не выпускают код без него. Для Python я с ходу могу назвать 4 таких средства, для Javascript — 2, для многих других есть встроенные анализаторы (например, в Perl исключение по use strict генерируется именно таким контролем, а не ошибкой собственно рантайма типов).
Но есть таки принципиальная разница. Грубо говоря, для статически типизированных языков то, что вычисляется при компиляции, будет аксиомой при выполнении (объект типа X => у его методов будет именно та сигнатура, что у класса X). Для динамически типизированных это не обязательно — Python, Javascript позволяют подменять подложку реализации на ходу. Линтинг в принципе не способен отловить такие ситуации. Их запрет для возможности статического анализа — уже вопрос административной политики при разработке, а не собственно языка.
> не надо постоянно вручную проверять и преобразовывать типы, на случай, если на вход передадут какую-то дичь. Типичный пример: String( arg ).toLowerCase()
Да, фактор существенный. Мне в этом смысле понравились правила от Hola для Javascript: например, если требуется, чтобы на входе функции было число, пишется +x, а если строка — ""+x. Рантайм умеет опознавать эти ситуации и переводить их в конверсию типа.
Но вот lowercase это уже за пределами обычной возможности системы типов, надо делать свой враппер.
> типы вручную пишутся только там, где пользователю это важно — в остальных местах они выводятся автоматически
Пока что это достаточно малая часть таких языков. И даже в них автоматический вывод работает не всегда и не везде адекватно. А главное — что для уверенности программиста, что тип получился именно нужный, приходится его указывать явно.
vintage
Выглядит это примерно так:
Линтеры тут ничем не помогут. Вообще, существование линтеров говорит о косяках в языке, который допускает "не кошерные" выражения. Если же речь не о линтерах, а тайпчекерах, то для них опять же нужны тайпхинты, что возвращает нас к статической типизации.
О чём опять же программисту сообщается сразу, а не когда в рантайме прилетит что-то не то.
Как правило программисту всё-равно какой там тип, лишь бы крякал как утка.
staticlab
Линтеры существуют не только, чтобы указывать на явные косяки программиста, но и чтобы унифицировать стиль кода внутри проекта.
vintage
Наоборот, они по определению контролируют только форму, но не содержание.
staticlab
Это всё демагогия. Линтером сейчас называют любой статический анализатор. По ссылке статический анализатор, проверяющий семантику кода, то есть содержание. Eslint для JavaScript — линтер, в основном проверяющий стиль написания, то есть форму.
vintage
"сообщал о подозрительных или непереносимых на другие платформы выражениях" — не тянет это на семантику.
netch80
А что же это, если не семантика? В понятие синтаксиса уже не влазит.
vintage
Но и до семантики как до луны. Типа "ставьте точки с запятой в конце строк" — это синтаксис, а "не используйте arguments" — это уже семантика?
netch80
Мнэээ… перед этим шла речь о непереносимых выражениях, например. Если кто-то напишет x<<200, где x типа int, что это, как не проблема семантики (содержания, смысла… проигнорируем сверхтонкие отличия)? Ну или пора вводить новую систему терминов.
Что за arguments тут — я не понял. Имеется в виду массив всех аргументов функции в JS? Вот тут, да, это настолько специфично для языка, что ближе к синтаксису, чем к семантике. Но я таки предпочёл бы тут видеть какое-то новое слово...
vintage
Вы правы. К сожалению для JS линтеры проверяют в основном довольно бесполезные правила.
netch80
Ну да. А вы считаете это недостаточным?
Я видел последующую дискуссию про то, что такое линтер. Я тут имел в виду статический анализатор любого вида, который способен опознать ситуации типа неиспользованных переменных, типичных опечаток и тому подобного. Такие средства вполне способны проанализировать код и найти, например, неверно передаваемый параметр. С Питоном у меня это реально работало.
Если такое средство видит, что параметр используется как индекс в списке, оно предположит, что тут должно быть целое. Если его индексируют строкой — оно должно быть совместимым со словарём. И так далее. Явные хинты тут не обязательны. Хотя, да, желательны — я бы предпочёл их видеть много где — но в последних версиях Питона, например, это уже делается.
Это если он не сумел вывести тип. А если вывел, но не тот, что думал программист?
Так в том и дело, что он хочет утку, а получается вдруг амадина. Она тоже крякает, но как-то невыразительно :)
vintage
Я считаю это бесполезным, чуть менее, чем полностью.
Это замечательно, но это лишь малая часть того, что может обнаружить компилятор в статически типизированном языке. Кроме того, чтобы все эти тайпчекеры работали, вам так и так придётся отказаться от динамических возможностей языка.
Он не может "не суметь". Всё, что он может — вывести тип, не совместимый с другим кодом и упасть. Если не упал, значит тип совместим и не важно, что там думал программист. Например, он мог думать, что вернётся User, а вернулся Proxy, который реализован так, что мимикрирует под User. Ну и какая программисту разница, если всё работает корректно?
netch80
Почему бесполезно?
Оно подсказывает, какие слова тут можно подставить, и умеет их дополнять. Также — показывает имена и типы аргументов, по которым можно понять, что и как задавать.
Оно может анализировать код и показывать ошибочные (например, опечатка в имени метода) или подозрительные конструкции.
Я не знаю, может, Вы имеете в виду какую-то злобную специфику JS. Я с ним слишком мало работал, чтобы знать такие тонкости. Но для Питона и для >95% случаев меня функциональность таких средств устраивает; когда она не работает — уже описывал раньше — когда я сам как автор кода вмешиваюсь в логику и делаю её изменчивой в рантайме.
В большинстве случаев — да. Я не могу, например, писать
да и безусловную конверсию лучше не применять (хотя если средство хоть чем-то умнее крышки дубового стола, оно будет работать в логике, аналогичной Static Single Assignment).
Или, я не смогу использовать присвоение некоторой переменной одного из трёх значений True, False или None (у меня это долго был любимый приём) — оно выведет тип для этого значения, только если придумает внутри себя enum, а так как все три в Питоне это синглтоны двух разных типов, ему будет сложновато это сделать.
Но таких случаев в реальной практике оказалось ой немного. А вот простые варианты типа "в этой переменной всегда целое" оно вывело бы на ура. Точно так же как упомянутая рядом схема Хиндли-Милнера работает в ML и семействе.
См. выше пример с конверсией в целое на ходу. Вывод типа "местами снег, местами град, местами variant — или int, или str" и есть то "не шмогла".
Разница в том, что мимикрия подобного рода означает слишком динамическую типизацию. И в Питоне, и в JS это поиск по названию метода в цепочках словарей методов суперклассов (прототипов).
Если мы вводим синтаксическую поддержку статической типизации, её лучше иметь в вариантах указания "тут постарайся оптимизировать" или "тут ты обязан вывести конкретный целочисленный тип, а его размер, так уж и быть, я тебе доверю выбирать".
vintage
Потому, что выдаёт вообще всё, что угодно, кроме того, что там в объекте действительно есть. Потыкавшись в эту менюшку, быстро отучаешься использовать подсказки.
Только для стандартных API и для JSDoc аннотаций (привет, статическая типизация)
Эту задачу решают "типы-суммы" и "типы-произведения". TypeScript, например, это умеет. При этом он умеет ограничивать такие типы в разных ветках кода:
Как программист написал — так оно и вывело. Или приведите пример, что ли.
Скорее динамическую диспетчеризацию (которая приятно дополняет статическую типизацию). Без неё многие задачи вообще не решаются.
netch80
Насколько я вижу, всё, что он выдал там, действительно есть в объекте (то есть, при вызове по имени будет поднято по цепочке прототипов и найдено). Да, их много, но самое важное находится вверху списка. Если бы не было такой сортировки, разбираться было бы в разы сложнее.
Так это и хорошо, что он умеет читать эти аннотации. Проверить соответствие аннотации сути одной функции просто и это локальное действие, не требуется никуда далеко заглядывать, и потом опираться на эту аннотацию — это как раз то, как это всё может работать в рантайме с "высочайшей" динамичностью типизации.
Хорошо, значит, прогресс в эту сторону идёт.
Да, это правильный термин для данного случая.
vintage
Нету там этого. Уж поверьте мне на слово :-) Он просто нашёл левый файл, где встречается одноимённый ключ объекта:
0xd34df00d
А если в хаскеле выведется
Userlike a
, то это статическая или динамическая типизация? Словарик с методами-то таскать придётся!0xd34df00d
Хиндли-Милнеру лет 30. Работает адекватно во всяких ML'ях и прочих хаскелях и не требует аннотаций в подавляющем большинстве случаев.
Если тип выведется неправильно, то с окружающим контекстом он будет несовместим, и рано или поздно вы получите ошибку компиляции. Типы (топ-левел-функций) лучше указывать потому, что типы — это документация, да и явное указание типов позволяет сделать более читабельные сообщения об ошибках типизации (вы накладываете больше констрейнтов, у тайпчекера меньше вариантов).
netch80
Я таки слишком неясно выразился. Имелось в виду, что программист ошибётся в какой-то мелочи, и результатом станет не тот тип, который ожидался. Явное называние ожидаемого типа в этом случае является дополнительной защитой со стороны программиста против собственных ошибок. (Ну и против ошибок компилятора, которые всегда есть, но вероятность в каждом конкретном случае ниже на несколько порядков...)
Да, это тот же самый принцип, что я описал абзацем выше. Но и типы промежуточных переменных — это такая же документация и такая же помощь тайпчекеру.
0xd34df00d
Так это либо будет ошибкой типизации где-то позже, либо не будет ошибкой типизации и семантически (на самом деле не всегда, но в подавляющем большинстве случаев так).
Ну, учитывая вышесказанное — не совсем.
Только она уже не столь важна. Типы топ-левел-байндингов полезны потому, что именно их вы будете дёргать в вызывающем коде, а остальное — это детали реализации.
0xd34df00d
Статические строгие системы типов выигрывают и с точки зрения скорости разработки, начиная с определённого размера кодовой базы и команды, потому что цена внесения изменений как функция от этих параметров растёт медленнее.
max1gu
Лехко. Какие-то странные люди, которые до этого делали Delphi, а затем c#, придумали TypeScript. Так вот сам попробовал: 90% глюков отлавливается на этапе компиляции остается только вопрос неправильнлого алгоритма.
Ещё одни странные люди, сменив версию Ангулара с 1 на 2, тоже перешли на TypeScript (жесткая типизация). Наверное, им надоело есть кактус.
0xd34df00d
Весь смысл статической строгой типизации в том, чтобы до исполнения отсекать некорректные программы.
franzose
Строгая типизация не означает, что в рантайме у вас внезапно что-нибудь не отвалится)
0xd34df00d
В C++ нет строгой типизации.
netch80
Строгость типизации — понятие достаточно относительное. По сравнению с JavaScript, где преобразование между числом и строкой — норма, C++ строго типизирован, а по сравнению с Go, где даже если int 32-битный, то преобразовывать между int и int32 можно только явно — нестрого типизирован.
Поэтому, при рассмотрении строгости типизации лучше вводить несколько канонических уровней и сравнивать с ними:
1. Вообще никаких неявных конверсий (Go, или близко к нему).
2. Конверсии по умолчанию — между числами, или только явно разрешённые функции, или только в особых контекстах (основной компилируемый майнстрим, C++, C#, Java и т.п.)
3. Размыта граница между строкой и тем, что она представляет (JavaScript, Perl...)
4. Вообще всё есть строка, оптимизации есть, но это принципиально не отменяют (sh, Tcl...)
0xd34df00d
Ну, я бы не стал ставить C++ в один ряд с C# и Java по строгости типизации при всей моей любви к нему, и конверсии между разными скалярными типами — не единственный аргумент. Больше непроверяемых кастов, операторы приведения и implicit-конструкторы, всякое такое счастье.
Строгость, конечно, понятие относительное, но множество имеющихся языков известно, поэтому можно рассматривать строгость типизации в контексте этого множества, где на вершине всякие там хаскели-идрисы (ну вот хочется почему-то строгость связать ещё и с выразительной силой системы типов, не знаю, почему), а C++ где-то в середине.
Antervis
не могут. С++ не является строго типизированным языком
panteleymonov
Есть случаи когда человек так и остается «новичком» используя только короткие шаблоны языка/тулзы/фрейморка.
yarric
Плох не хеллоу-ворлд, а языки, которые как-будто делают с целью упрощения написания хеллоу-ворлд-ов и написания красивых, но далёких от жизни туториалов.
Звучало бы странно, если бы не популяроность таких языков и непопулярность "сложных" языков, которые на самом деле упрощают жизнь при реальном программировании.
alexkunin
А можете привести несколько примеров языков, «которые как-будто делают с целью упрощения написания хеллоу-ворлд-ов и написания красивых, но далёких от жизни туториалов»?
yarric
Синтатиксис и семантика примера из статьи демонстрирует аналогичный код на многих современных распространённых языках программирования.
alexkunin
Это понятно из статьи, спасибо. Я прошу привести примеры «нового Того-самого-лучшего-языка», желательно несколько. А то пока обсуждаются отличия Ады от чего-то абстрактного.
yarric
Прошу прощения, не хочу показывать пальцем и создавать кому-либо антирекламу, пост не для этого написан.
alexkunin
Речь не о человеке, не о религии, и т.д. Мы говорим о языках программирования — продуманных системах, спроектированных и использующихся множеством людей. И разговор происходит на техническом ресурсе для профессионалов.
А пока что вы в статье сравниваете конкретный язык с придуманным негативным кейсом. Очень знаете ли похоже на «Ариэль вс. Обычный стиральный порошок».
yarric
Вы сможете привести улучшенную версию программы из моего примера на каком-нибудь из современных распространённых языков программирования вроде Java или С#?
alexkunin
Я не хочу ничего приводить, я ничего не утверждал о хорошести или плохости других языков. Я хочу увидеть от вас пример того, что, как вы утверждаете, имеет место быть.
yarric
Пример языка, в которых используется обработка ошибок на основе условий и исключений, как в примере? Java, C#, C++ и т. д.
alexkunin
Т.е. вы утверждаете, что Java, C#, C++ — это те, «которые как-будто делают с целью упрощения написания хеллоу-ворлд-ов и написания красивых, но далёких от жизни туториалов»?
Ясно, спасибо, ваша мысль ясна.
yarric
Java, C#, C++ — примеры языков, в которых используется обработка ошибок на основе условий и исключений, если с первого раза было не понятно.
alexkunin
А ада с помощью духа святого выходит из щекотливых ситуаций, стало быть?
yarric
Ada ещё добавляет проверки диапазонов на основе типов данных.
areht
А это как и зачем?
Запретить вводить людей младше года и старше 120 — это совершенно искусственный пример, хуже Hello World. А в реальности это зачем?
yarric
Лучше писать проверки диапазона руками в коде? Возможно, дело вкуса.
areht
Это из серии «предполагается, что новичку, заинтересовавшемуся языком, понравится очередной упрощенный вариант»?
Не знаю на счёт «лучше», но после «вынесите границы в настроечки!» вписывать эти проверки точно не хочется.
vintage
У вас в любом случае будут ограничения диапазонов. Заданные вами или компилятором или архитектурой процессора. Максимально компактные диапазоны позволяют оптимизировать вычисление и хранение данных, обнаруживать большее число ошибок. Разумеется число 120 неоправданно занижено. А вот диапазон {0… 256} выглядит вполне разумно на ближайшие 100 лет.
iig
Задавать возраст таким образом — готовый антипример. Особенно в 21 веке, когда проблема 2000 далеко позади, а проблема 2038 приближается.
vintage
И что вы имеете против такого задания возраста?
iig
Это приблизительно как придумать свою реализацию целочисленной арифметики, причём на bash и в столбик. И это в 21 веке. Возраст человека измеряют от дня рождения, так что детализацию возраста, если по уму, нужно делать до дня. А чтобы не наступать на грабли типа 29 февраля, лучше брать стандартное, с детализацией до секунды.
vintage
Вот именно, что замеряют. И результатом такого замера является значение типа "возраст" с соответствующими ограничениями. Если замеренное значение не вписывается в эти ограничения, то это повод бить тревогу.
iig
Вот зачем-то в программе делается сравнение возрастов. Алиса и Боб родились в один год. Алиса родилась 1 января. Боб родился 31 декабря. Кто из них старше? В модели, где все округляется до года, они одного возраста.
vintage
Почему формула сравнения именно такая — вопрос к тому, кто устанавливает правила. Возможно в какой-то специфической области сравнение должно быть именно такое. Но суть ведь не в том, как надо сравнивать возраста людей.
iig
Ок, пусть будет, тем более что это хелловорлд :). И какой профит в использовании такого типа-поддиапазона int? Компилятор сгенерирует код, который при каждом присваивании будет проверять диапазон и кидать exception? А для float так тоже можно? А диапазон может состоять из поддиапазонов? А если поддиапазонов maxint/2? А если присваивание в цикле?
КМК, лучше условия проверять явно и там где это необходимо.
oxidmod
Все зависит от того в скольких в местах вам проверить нужно. Смотрите на єто на как VO. Раз он создался, значит он 100% валидный.
vintage
Многие проверки можно выкинуть, когда точно знаешь какие диапазоны где приходят. Так что единственное место, где проверки останутся — на входе в приложения, но они там и так по любому будут. Но тут их хотябы компилятор сам сгенерирует, а не программист вручную.
iig
Я программист ненастоящий… И подобная компиляторная магия, как по мне, особенность, которой лучше избегать. Если нужна проверка входных данных — почему бы её не сделать как нужно? Например, если логика требует не exception, а специального кода возврата? Если сгорел датчик, например — перезагружаться?
vintage
try-catch позволит вам обработать нештатную ситуацию по своему.
iig
Теперь понятно, откуда берутся программы, внезапно кидающие невнятный exception, но продолжающие как бы работать.
vintage
У вас какое-то странное представление об обработке исключений.
0xd34df00d
Это вполне может быть не компиляторной магией, а магией системы типов, и лучше научиться ей пользоваться.
Umr001
ПОМОЕМУ МОРАЛЬ ВПОЛНЕ ВЫВЕДЕНА В КОНЦЕ СТАТЬИ АЛЕ