О программировании, из комментов у
vit_r
Nov. 30th, 2012 07:55 am![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
Наши дедушки писали спецификации программ, а потом их реализации (в машинных кодах). Мы дожили до времён, когда можно писать одну только спецификацию и её запускать. Уже когда написали “спецификацию”, запустили и убедились, что она корректна — вот после этого можно уже посмотреть: если что-то работает слишком медленно, не параллелится или памяти много жрёт, можно отдельные места низкоуровнево оптимизировать. И не надо думать, что низкоуровневая оптимизация это обязательно на ассемблере/Си и прочих close to machine языках. Когда вещь, которая естественным образом выражается через цикл, насильно запихивают в рамки ФП чтобы она лучше параллелилась — это низкоуровневая оптимизация.
Спецификация это такое описание процедуры или модуля, из которого очевидно, что именно они делают (такое описание не единственно, и выбор варианта дело вкуса). Ну вот например одна из спецификаций процедуры sort:
“Процедура sort берёт набор элементов и возвращает список, состоящий из минимального элемента этого набора, потом минимального элемента оставшегося набора и так далее.”
sort(items: Сollection) = min(items) cons sort(items without min(items))
sort(Collection.Empty) = List.Empty
sort(items: Сollection) = min(items) cons sort(items without min(items))
sort(Collection.Empty) = List.Empty
Чем хорошо такое определение? Тем что оно функционально? — хрена с два; то, что оно функциональное это мелкая частность и стечение обстоятельств. По-настоящему оно хорошо тем, что соответствует понятному непрограммисту описанию на естественном языке и при этом максимально просто, вероятность допустить в таком определении ошибку минимальна. А если ещё делать перекрёстное ревью кода и написать пару юнит-тестов, то вообще исчезающе мала.
А вот если, скажем, процедуру sort определять через алгоритм, который хитрый и ещё надо доказывать, что он вообще работает, возникает вопрос что значит "доказывать что Х работает верно". Это значит, что нужно доказать что Х работает эквивалентно своей спецификации. А где эта спецификация? Её так или иначе надо написать, и доказательство так или иначе провести, потому что иначе вероятность ошибки колоссальна: я знаю как ошибки в нетривиальной алгоритмике выявлялись спустя годы использования софта в продакшине, то есть никакое закидывание юнит-тестами бы их не вскрыло. Вот потому я и адепт того, чтобы писать программы их конструктивными спецификациями, а оптимизации использовать максимально редко.
ФП я люблю ровно постольку поскольку функциональный стиль часто идеален для написания конструктивных спецификаций функций. Однако для написания конструктивных спецификаций модулей и сущностей доменной модели приложения, которое я делаю, функциональщины обычно нехватает. Поэтому в ещё большей степени я адепт мультипарадигменных языков, где можно за день накатать DSL отвечающий доменной модели и дальше писать код в таком вот спецификационном стиле, избегая частностей.
Сейчас таких языка, кстати, два: Perl и Scala. Оба люто прекрасны, однако имеют и общий недостаток: в руках неопытного программиста они как боевая граната в руках у двухлетнего карапуза. Перл лучше для rapid prototyping (потомучто без статической типизации), Скала для всего осталього (потомучто со статической типизацией).