Ни один из этих двух пунктов не воплощен в Haskell очень хорошо. Я думаю, что Haskell по-прежнему приводит к несколько незавершенному коду, но подходит к проблеме в целом с другой философией и другими инструментами.
Очереди
Грубо говоря, Хикки хочет указать, что если вы пишете метод для объекта, который вызывает другой объект
class Foo
def bar(baz)
baz.quux
end
end
тогда мы только что жестко закодировали представление о том, что все, что передается в Foo#bar
, должно иметь метод quux
. С его точки зрения, это завершение, потому что это означает, что реализация Foo
неразрывно связана с реализацией того, как реализуется объект, переданный Foo#bar
.
Это меньше проблема в Ruby, где вызов метода гораздо больше похож на динамически отправляемое сообщение, отправляемое между объектами. Это просто означает, что объект, переданный Foo#bar
, должен каким-то образом ответственно реагировать на сообщение quux
, не более того.
Но это подразумевает последовательность в обработке сообщений. Если вы вместо этого отправляете сообщение в очередь, чтобы в конечном итоге быть доставленным к результирующему объекту, вы можете легко разместить посредника в этом шве — возможно, вы захотите запустить bar
и quux
одновременно.
В Эрланге эта идея доведена до логической крайности больше, чем в Хаскеле, и я настоятельно рекомендую узнать, как Эрланг решает подобные проблемы.
spawn(fun() -> Baz ! quux)
Правила
Хикки неоднократно подчеркивает, что конкретные, жестко закодированные методы выполнения ветвления создают комплекс. К слову, ему не нравится оператор case или сопоставление с образцом. Вместо этого он предлагает Правила, под которыми, как я полагаю, он подразумевает системы «производственных правил». Они обеспечивают выбор и разветвление, позволяя программисту установить набор правил для того, когда определенные действия «запускаются», а затем ждать, пока входящие события не удовлетворят достаточным правилам, чтобы вызвать срабатывание действий. Наиболее известной реализацией этих идей является Пролог.
В Haskell сопоставление с образцом встроено глубоко в его душу, поэтому трудно утверждать, что Haskell сразу же выполняет декомплементацию таким образом... но в Haskell есть действительно хороший пример системы правил — разрешение классов типов.
Вероятно, наиболее известным понятием этого являются классы типов в стиле mtl
, где вы в конечном итоге пишете функции с сигнатурами, такими как
foo :: (MonadReader r m, MonadState s m, MonadIO m, MonadCatch m)
=> a -> m b
где foo
полностью полиморфен в типе m
, если он соответствует определенным ограничениям — он должен иметь постоянный контекст r
, изменяемый контекст s
, возможность выполнения IO
и возможность генерировать и перехватывать исключения.
Фактическое разрешение того, какие типы реализуют все эти ограничения, решается системой правил, которую часто (любезно или иначе) называют «прологом класса типов». Действительно, это достаточно мощная система, чтобы кодировать целые программы внутри системы типов.
Это на самом деле очень приятно и дает Haskell своего рода естественный стиль внедрения зависимостей, как описано в примере mtl
выше.
Тем не менее, я думаю, что после длительного использования такой системы большинство хаскеллеров приходят к пониманию того, что, хотя системы правил иногда бывают умными... они также могут легко выйти из-под контроля. В Haskell есть множество осторожных ограничений мощности пролога класса типов, которые гарантируют, что программисту будет легко предсказать, как он разрешится.
И это основная проблема систем правил в целом: вы теряете явный контроль над тем, какие действия в конечном итоге будут запущены... так что становится сложнее корректировать ваши правила для достижения ожидаемого результата. На самом деле я не уверен, что согласен с Ричем в том, что системы правил, таким образом, ведут к декомплементации. Возможно, вы не разветвляете явно информацию, связанную с другими объектами, но вы устанавливаете множество нечетких долгосрочных зависимостей между вещами.
person
J. Abrahamson
schedule
29.01.2014