А там как раз хороший пример про систему сборки. И это, на мой взгляд, применимо и к какому-нибудь специфичному непроцедурному языку, и к другим системам.
Например, мне довелось работать над data-flow языком, который выражается диаграммами:
Он был достаточно бедный, чтобы любой пользователь мог легко накидать процесс.
Но даже в таком бедном языке возникают проблемы, свойственные конкурентному программированию, например дождаться всех, или большинства сигналов, что делать в случае таймаутов, и как выбирать приоритет между одновременными сигналами.
Если мы выносим решения этих вопросов на уровень языка, диаграммы быстро становятся нечитаемыми. Если мы перемещаем их внутрь нод, нам теперь требуется поддержка скриптов для нод.
Нужно ли поддерживать условия? Скорее всего да, но это новый тип ноды.
Нужно ли поддерживать циклы? Если да, то это усложняет диаграммы, кроме того появляется проблема с мутабельным состоянием цикла. Если нет — язык будет бедным.
О том, что теория без практики мертва, вещают тысячи практиков со всех сторон.
А вот этот пример с Promise — это отличный пример, когда практика без теории слепа.
Очень странно слышать, что семантически монады IO, Async и Cont — это одно и тоже.
Мой отсыл к фруктам был призван продемонстрировать логическую ошибку: отношение между монадами и, например, call/cc, — это отношение между общим и частным, и конечно же частное не заменяет общее.
Если бы вы сказали, "монады — это абстракция над вычислениями в контексте, независимая от вычислителя", а потом добавили, что мол "на практике сама абстракция не нужна, а пользу приносит в-основном конкретная монада async/await и отлично работает", и с этим можно спорить (и я постарался бы этот тезис оспорить :-)), а в исходном виде фраза просто некорректна.
Со случаем римских чисел согласиться не могу, в данном случае часть сложности, которая ушла от перехода от римских чисел к позиционным арабским переместилась в систему образования, когда школьники до 4-го класса учатся делать простые арифметические операции с ними (в то время как в детском саду легко складывают/вычитают на палочках) при этом правильно учитывая переносы в разрядах. То есть сложность частично переместилась на уровень документации и обучения.
Но есть в каком-то смысле экстремальный случай — это, например, обфускация. И здесь я соглашусь, что это похоже на создание большой сложности из воздуха, которую мы затем можем с определёнными усилиями постепенно уменьшать, распутывая шаг за шагом этот обфусцированный код. Но бесконечно упрощать мы всё равно не сможем, это просто противоречит теории информации.
Я представляю этот процесс примерно как у нас есть система определённой сложности, обладающей некоторой энтропией. С помощью рефакторинга мы можем спуститься в локальный минимум энтропии. Но чтобы опускаться ещё ниже, нам нужно переделать и, например, перестроить код на других абстракциях (скажем, перейти от коллбэков к асинкам). При этом, как говорит автор статьи, часть сложности перейдёт на уровень документации и обучения, но код мы можем разгрузить довольно сильно, рефакторингами снова скатившись в локальный минимум энтропии.
Но всегда есть нижняя граница сложности, ниже которой мы не опустимся. Поэтому её (сложность) нужно будет куда-то растолкать. Как-то так.
Семантические ошибки тоже можно отлавливать с помощью типов, оборачивать разные семантические типы в newtype, явно формулировать ограничения и эффекты.
И какая-то практика тоже нужна, чтобы заранее избегать ловушек вроде let Just x = ....
Опять же, кто вам запрещает писать тесты?
Я правильно понимаю, что вы считаете, что римские числа обладали некоей сложностью, а при переходе к арабским позиционным числам часть сложности просто исчезла?
Вся сложность разработки микросервисов уехала из отдельных сервисов на уровень "заставить всё это работать вместе".
Существенно усложнилось тестирование (теперь оно практически всё интеграционное), усложнились протоколы обмена, стало трудно получить целостность данных (см. raft/paxos и прочее веселье), усложнилось развёртывание. Что отлично подтверждает тезис о том, что сложность никуда не девается, а она просто перераспределяется
В-общем согласен, только замечу, что непреднамеренная сложность (accidental complexity) является обычной необходимой сложностью, которую вовремя не обнаружили и не разложили по необходимым абстракциям (или не создали необходимые) и не задокументировали.
Люди не стараются себе специально усложнить жизнь и привнести побольше сложностей, обычно то, что мы называем непреднамеренной сложностью появляется из-за того, что они слишком упростили штуки в других местах, нет?
Плюс (раз уж мы о Хаскеле) у нас есть всегда значения типа IO a. Соответственно типы [IO a] и IO [a] довольно сильно различаются. Как будет выглядеть тип таких значений с checked exceptions, но без типа IO мне совершенно непонятно.
Checked exceptions такие как в джаве не изоморфны алгебраическим типам. Почему?
Попробуйте ответить на вопрос: какая сигнатура будет у функций map и filter в случае checked exceptions?
Я упомянул обфускацию.
16:00 Московского времени? Или GMT?
Вы думаете, стоит открывать этот ящик Пандоры?
А там как раз хороший пример про систему сборки. И это, на мой взгляд, применимо и к какому-нибудь специфичному непроцедурному языку, и к другим системам.
Например, мне довелось работать над data-flow языком, который выражается диаграммами:

И так далее. Всё в точности, как в статье.
Отсутствие хорошей системы типов, конечно!
О том, что теория без практики мертва, вещают тысячи практиков со всех сторон.
А вот этот пример с
Promise
— это отличный пример, когда практика без теории слепа.Неправильно, удар палкой, сделанный мастером в нужный момент, приносит просветление.
Очень странно слышать, что семантически монады
IO
,Async
иCont
— это одно и тоже.Мой отсыл к фруктам был призван продемонстрировать логическую ошибку: отношение между монадами и, например, call/cc, — это отношение между общим и частным, и конечно же частное не заменяет общее.
Если бы вы сказали, "монады — это абстракция над вычислениями в контексте, независимая от вычислителя", а потом добавили, что мол "на практике сама абстракция не нужна, а пользу приносит в-основном конкретная монада async/await и отлично работает", и с этим можно спорить (и я постарался бы этот тезис оспорить :-)), а в исходном виде фраза просто некорректна.
Со случаем римских чисел согласиться не могу, в данном случае часть сложности, которая ушла от перехода от римских чисел к позиционным арабским переместилась в систему образования, когда школьники до 4-го класса учатся делать простые арифметические операции с ними (в то время как в детском саду легко складывают/вычитают на палочках) при этом правильно учитывая переносы в разрядах. То есть сложность частично переместилась на уровень документации и обучения.
Но есть в каком-то смысле экстремальный случай — это, например, обфускация. И здесь я соглашусь, что это похоже на создание большой сложности из воздуха, которую мы затем можем с определёнными усилиями постепенно уменьшать, распутывая шаг за шагом этот обфусцированный код. Но бесконечно упрощать мы всё равно не сможем, это просто противоречит теории информации.
Я представляю этот процесс примерно как у нас есть система определённой сложности, обладающей некоторой энтропией. С помощью рефакторинга мы можем спуститься в локальный минимум энтропии. Но чтобы опускаться ещё ниже, нам нужно переделать и, например, перестроить код на других абстракциях (скажем, перейти от коллбэков к асинкам). При этом, как говорит автор статьи, часть сложности перейдёт на уровень документации и обучения, но код мы можем разгрузить довольно сильно, рефакторингами снова скатившись в локальный минимум энтропии.
Но всегда есть нижняя граница сложности, ниже которой мы не опустимся. Поэтому её (сложность) нужно будет куда-то растолкать. Как-то так.
Понял, принял.
Фрукты можно заменить много чем. Например сухофруктами или яблоками. Или апельсином красным или апельсиновыми дольками.
Семантические ошибки тоже можно отлавливать с помощью типов, оборачивать разные семантические типы в newtype, явно формулировать ограничения и эффекты.
И какая-то практика тоже нужна, чтобы заранее избегать ловушек вроде
let Just x = ...
.Опять же, кто вам запрещает писать тесты?
М?

(Там первая строка является ссылкой)
Эээ, так это метафора была? :-)
Я правильно понимаю, что вы считаете, что римские числа обладали некоей сложностью, а при переходе к арабским позиционным числам часть сложности просто исчезла?
Вся сложность разработки микросервисов уехала из отдельных сервисов на уровень "заставить всё это работать вместе".
Существенно усложнилось тестирование (теперь оно практически всё интеграционное), усложнились протоколы обмена, стало трудно получить целостность данных (см. raft/paxos и прочее веселье), усложнилось развёртывание. Что отлично подтверждает тезис о том, что сложность никуда не девается, а она просто перераспределяется
Зато в дереве сам элемент стал сложнее — ему уже нужно иметь два потомка (в случае BST).
Так что не видно понижения одновременно требуемой сложности. Она снова перераспределилась!
В-общем согласен, только замечу, что непреднамеренная сложность (accidental complexity) является обычной необходимой сложностью, которую вовремя не обнаружили и не разложили по необходимым абстракциям (или не создали необходимые) и не задокументировали.
Люди не стараются себе специально усложнить жизнь и привнести побольше сложностей, обычно то, что мы называем непреднамеренной сложностью появляется из-за того, что они слишком упростили штуки в других местах, нет?
Вы-таки отказываете функции
map
в параллелизме?Плюс (раз уж мы о Хаскеле) у нас есть всегда значения типа
IO a
. Соответственно типы[IO a]
иIO [a]
довольно сильно различаются. Как будет выглядеть тип таких значений с checked exceptions, но без типаIO
мне совершенно непонятно.Checked exceptions такие как в джаве не изоморфны алгебраическим типам. Почему?
Попробуйте ответить на вопрос: какая сигнатура будет у функций
map
иfilter
в случае checked exceptions?