про систему записи
Mar. 4th, 2016 01:35 am![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Читал в последнее время научные и околонаучные статьи. Обратил внимание, что от математической записи я во-первых отвык, во-вторых меня с нее воротит.
Суть математической (алгебраической) записи - в том, чтобы записать покороче, и в результате во-первых по листку таскать будет удобнее, во-вторых можно таскать по листку с помошью механического применения правил преобразования, не задумываясь о смысле. Проблема однако в том, что смысл делается неясен, чтобы его понять из математической записи, требуется расшифровка. Но если преобразований делается много, а расшифровка - только один раз в конце (плюс зашифровка в начале), то получается профит.
Но у нас в программировании все по-другому. Мы не делаем механические преобразования вручную, у нас для них есть компьютер. И для зашифровки у нас тоже есть компьютер. Поэтому не надо маяться шифровкой вручную, надо писать так, чтобы было сразу понятно, о чем идет речь. В этом смысле интересно посмотреть на прогресс автоматизации. Фортран когда-то был создан по образу и подобию математической записи. Но по мере развития компиляторов стало менее важно внутримашинное представление и более важна человеческая читабельность - появилось структурное программирование, и так далее.
Дополнительный момент заключается в том, что записанная человеком математическая запись обычно неточная, она сопровождается примечаниями типа "тут играйте, тут не играйте, тут рыбу заворачивали". А вот программа - штука точная, с фиксированным синтаксисом и семантикой.
Так вот, меня очень бесит, когда в статьях люди используют математическую запись чтобы записать некую формулировку, а потом не делают с ней никаких перобразований. Поэтому процесс чтения сводится к бесмыссленной расшифровке и угадыванию, где они там рыбу заворачивали. Вместо того чтобы записать или популярно словами или в виде точной и читаемой программы. Зачем так делают - непонятно, видимо для пущей наукообразности. Причем заметна очень четкая корреляция: чем более ни о чем статья, тем больше наукообразности пытаются изобразить авторы.
Та же самая претензия применима ко всяким хаскелам и прочим APLам: математико-образный крипто-язык без малейшего смысла в этом шифровании. Смысл во всяком хаскельном мета-мета-программировании возможно и есть, но для него надо придумать менее зашифрованную запись.
Суть математической (алгебраической) записи - в том, чтобы записать покороче, и в результате во-первых по листку таскать будет удобнее, во-вторых можно таскать по листку с помошью механического применения правил преобразования, не задумываясь о смысле. Проблема однако в том, что смысл делается неясен, чтобы его понять из математической записи, требуется расшифровка. Но если преобразований делается много, а расшифровка - только один раз в конце (плюс зашифровка в начале), то получается профит.
Но у нас в программировании все по-другому. Мы не делаем механические преобразования вручную, у нас для них есть компьютер. И для зашифровки у нас тоже есть компьютер. Поэтому не надо маяться шифровкой вручную, надо писать так, чтобы было сразу понятно, о чем идет речь. В этом смысле интересно посмотреть на прогресс автоматизации. Фортран когда-то был создан по образу и подобию математической записи. Но по мере развития компиляторов стало менее важно внутримашинное представление и более важна человеческая читабельность - появилось структурное программирование, и так далее.
Дополнительный момент заключается в том, что записанная человеком математическая запись обычно неточная, она сопровождается примечаниями типа "тут играйте, тут не играйте, тут рыбу заворачивали". А вот программа - штука точная, с фиксированным синтаксисом и семантикой.
Так вот, меня очень бесит, когда в статьях люди используют математическую запись чтобы записать некую формулировку, а потом не делают с ней никаких перобразований. Поэтому процесс чтения сводится к бесмыссленной расшифровке и угадыванию, где они там рыбу заворачивали. Вместо того чтобы записать или популярно словами или в виде точной и читаемой программы. Зачем так делают - непонятно, видимо для пущей наукообразности. Причем заметна очень четкая корреляция: чем более ни о чем статья, тем больше наукообразности пытаются изобразить авторы.
Та же самая претензия применима ко всяким хаскелам и прочим APLам: математико-образный крипто-язык без малейшего смысла в этом шифровании. Смысл во всяком хаскельном мета-мета-программировании возможно и есть, но для него надо придумать менее зашифрованную запись.
no subject
Date: 2016-03-04 10:29 am (UTC)Все монадки и прочие семигрупоиды, на самом деле, только об этом.
Другое дело, что богатство выразительных средств, позволяющее лучше объяснить компьютеру (а на самом деле другому человеку) что происходит в коде (не через имена переменных, а через сложную композицию из типов и функций) - не всегда продуктивно, потому что основная задача программы - работать, а не быть понятной и выразительной для читателя.
(что не отменяет требований по удобству сопровождения и т.д.)
no subject
Date: 2016-03-05 03:03 am (UTC)no subject
Date: 2016-03-06 04:21 pm (UTC)Основной идеей вокруг монадок (с точки зрения software engineering) является такая формулировка типов, которая позволяет описывать реальный мир с высоким формализмом (т.е. без "подхачили бизнес-логику в самом сердце DSL'я, чтобы у клиента 1213 не списывались деньги раньше срока").
Ярким примером является энкапсуляция ошибок и обязательности возможности наличия данных - монадки Must и Maybe. Типы данных требуют для Maybe, чтобы все обработчики этих данных умели обработать случай None (т.е. реальных данных нет), а Must - не даст сделать код, который "иногда, очень-очень редко" вернёт None вместо файлового дескриптора.
Ещё интереснее обработка ошибок. Если мы описываем монадку, которая состоит из Data|None|Error, то если мы в каком-то месте забудем сказать, что делать если там ошибка (например, на пятом уровне по стеку вверх), то программа не скомпилируется.
Отсюда поговорка "компилируется, значит работает" (что неправада в контексте правильности алгоритма, но более-менее правда в вопросах "всё предусмотрели и нет неопределённых поведений").
Принудительное разделение на "чистый" код и код в монадке IO позволяет легко рефакторить этот самый чистый код, потому что его элементарно покрыть тестами и там нет ничего, чтобы могло "сломать что-то другое".
Другими словами, с точки зрения инженерной практики, это набор машинно-enforced правил, которые позволяют избежать дурацких ошибок и "плохо продумал", но при этом не слишком тормозят процесс бюрократизацией.
Ах, да, ещё pattern matching - это когда у компилятора достаточно информации о типах, чтобы сказать, что мы "забыли" какой-то из case'ов в switch'е (если на сишном языке говорить).
От массового расползания по индустрии его останавливает только взрывание мозга у обучающихся. А кусочки проползают в новые языки (тот же Rust - на монадках, только слова такого не произносят).
no subject
Date: 2016-03-07 08:37 pm (UTC)> Ярким примером является энкапсуляция ошибок и обязательности возможности наличия данных - монадки Must и Maybe. Типы данных требуют для Maybe, чтобы все обработчики этих данных умели обработать случай None (т.е. реальных данных нет), а Must - не даст сделать код, который "иногда, очень-очень редко" вернёт None вместо файлового дескриптора.
> Ещё интереснее обработка ошибок. Если мы описываем монадку, которая состоит из Data|None|Error, то если мы в каком-то месте забудем сказать, что делать если там ошибка (например, на пятом уровне по стеку вверх), то программа не скомпилируется.
Это все происходит от специфической убогости их языка. У них нету даже циклов, не говоря уже про break/continue. Вместо того все вытягивается в конвейеры, в которых последующим стадиям приходится уметь обратабывать все, что производится предудыщими стадиями.
Но и для конвейеров есть более просто решение - пропускать насквозь все, что не знаешь как обработать. Это решение очень краеугольно во всем современном программировании, в особенности в ООП, и приводит к большому повышению эффективности работы программистов. Но в их убогом языке его применять невозможно по той простой причине, что и присваивания у них тоже нет. Поэтому приходится все обрабатывать вручную, и при любом мелком изменении в первых стадиях конвейера их карточный домик начинает рассыпаться.
> Принудительное разделение на "чистый" код и код в монадке IO позволяет легко рефакторить этот самый чистый код, потому что его элементарно покрыть тестами и там нет ничего, чтобы могло "сломать что-то другое".
Это очень-очень неправильная идея, из очень многих соображений. Начать можно с простого соображения: фокус в том, что "чистый код" ничего не делает и никому не нужен. А если его пытаться сделать нужным, то он у них делается просто жутким. После этого можно на остальны соображения даже не смотреть.
> Ах, да, ещё pattern matching - это когда у компилятора достаточно информации о типах, чтобы сказать, что мы "забыли" какой-то из case'ов в switch'е (если на сишном языке говорить).
Нынешние сишные компиляторы умеют выдавать такие предупреждения.
no subject
Date: 2016-03-08 08:13 am (UTC)Нет. Если функция может вернуть три состояния: None, value и Error, то если мы пишем
foo = function(), то компилятор никак не может проверить, что мы действительно сказали, что делать, если foo будет None или Error. Эксепшены чуть лучше, чем -1 из функции, т.к. хотя бы роняют программу, а не кормят этот -1 дальше по коду, но это runtime checking. Добротный язык должен проверить, что у меня в языке написано что делать с None, что делать с Error и что делать с value. А если value - перечисляемый тип, то ещё и проверить, что все значения value покрыты.
Монадки - как раз и есть такие "конвееры", позволяющие завернуть значение в "коробочку" с доп.обработкой. Так, чтобы функция, обрабатывающая реальное значение, не парилась (и никогда в жизни не столкнулась) с прилагающимися к обработке None и Error.
А вот про break/continue не надо, пожалуйста. Я не видел ни одного компилятора сишнопаскального вида, который бы мог сказать "чувак, твои break/continue не покрывают все случаи возвращаемых значений". Есть миллиард статических чекеров, которые это дело расширяют, но сам язык не подразумевает достаточно выразительной семантики, чтобы объяснить, какие там могут быть значения.
Чистый код - нужен и важен. Если у тебя функция считает sha512 от блока, зачем ей внешний мир? Входные данные, выходные данные. Если она при этом пользуется пятком глобальных переменных и её результат зависит от getdatetime, то это не очень хорошая функция для вычисления sha512, правда?
no subject
Date: 2016-03-08 09:23 pm (UTC)Исключения гораздо хуже в том смысле, что с ними сильно возрастает вероятность того, что состояние функции не будет очищено перед возвратом. Поэтому по-хорошему использовать их можно только в коде, который гарантирует, что такая очистка будет корректно произведена - то есть, в коде, который ничего не очищает вручную, а вся очистка задается при создании элементов состояния и выполняется автоматически при выходе из блока.
> А вот про break/continue не надо, пожалуйста. Я не видел ни одного компилятора сишнопаскального вида, который бы мог сказать "чувак, твои break/continue не покрывают все случаи возвращаемых значений".
Мне несколько непонятно, что имеется в виду под "покрывать". У break/continue нет потребности возвращать какие-то значения, которые будут обрабатываться ("покрываться") где-то далеко. Они обрабатывают особые ситуации сразу на месте. Поэтому "не покрыть" с ними что-то - невозможно.
> Чистый код - нужен и важен. Если у тебя функция считает sha512 от блока, зачем ей внешний мир?
Это тривиальная функция, не представляющая большого интереса с точки зрения разработки. Всех делов - записать последовательность арифметических действий по шпаргалке.
no subject
Date: 2016-03-09 10:44 am (UTC)Из БД может быть прочитано value|none|error. Функция умеет делать сложную математику, но мы хотим, чтобы она возвращала что-то осмысленное даже если там error или none. Мы можем написать пачку if'ов, чтобы на всех уровнях стека каждый раз писать if status==error ... if value == none..., а можем завернуть разумные функции (которые работают только с данными) в монадки, которые добавят адекватную обработку error/none случаев, и если мы в финале (на верхнем уровне) не обработаем все варианты (допустим, в стеке в какой-то момент у нас получилось уже error|invalid|outdatated|none|value), то программа просто не скомпилируется.
А вот сишный код, который if'ами утыкан, просто молча "сделает не то", обнаружив на месте value invalid|outdated, о котором в одном месте программы подумали, а во втором забыли.
no subject
Date: 2016-03-09 08:43 pm (UTC)no subject
Date: 2016-03-09 09:51 pm (UTC)Либо, напишем (псевдокодом):
compound_reply = compose (important_data, SPECIAL_CODE_TO_SAY_NO_DATA_HERE, important_data).
return compound_reply
И в compose мы эту обработку сделаем. И в ответе по стеку выше (куды compound_reply уйдёт) тоже напишем наш if'ик, так?
no subject
Date: 2016-03-10 08:40 pm (UTC)Нужно только если одна и та же функция занимается и чтением и обработкой: прочитали один кусочек, обработали, прочитали еще кусочек, обработали. И вот тут break/continue/return позволяют при ошибке легко плюнуть на все оставшиеся кусочки.
no subject
Date: 2016-03-04 11:33 am (UTC)