Кто-нибудь когда-нибудь сталкивался с монадным трансформатором в дикой природе? - PullRequest
52 голосов
/ 03 мая 2010

В моей сфере деятельности - бэк-офисе ИТ для финансового учреждения - очень часто программный компонент носит глобальную конфигурацию, регистрирует ее прогресс, имеет какое-то короткое замыкание при обработке ошибок / вычислениях. Вещи, которые могут быть смоделированы с помощью Reader-, Writer-, Maybe-monads и т. П. В Haskell и скомпонованы вместе с монадными трансформаторами.

Но есть некоторые недостатки: концепция монадных трансформаторов довольно хитрая и трудная для понимания, монадные трансформаторы приводят к сигнатурам очень сложного типа и приводят к некоторому снижению производительности.

Итак, я задаюсь вопросом: лучше ли использовать монадные трансформаторы при решении этих общих задач, упомянутых выше?

Ответы [ 6 ]

43 голосов
/ 03 мая 2010

Сообщество Haskell разделено по этому вопросу.

  • Джон Хьюз сообщает, что ему легче учить трансформаторы монад, чем учить монады, и что егоучащиеся лучше справляются с подходом «сначала трансформеры».

  • Разработчики GHC обычно избегают монадных трансформаторов, предпочитая свернуть свои собственные монады, которые объединяют все необходимые им функции.(Мне только что сегодня безоговорочно сказали, что GHC будет не использовать монадный трансформатор, который я определил три дня назад.)

Для меня монадные трансформаторы - этоочень похоже на бессмысленное программирование (т. е. программирование без именованных переменных), что имеет смысл;в конце концов, они точно программируют бессмысленно на уровне типов.Мне никогда не нравилось бессмысленное программирование, потому что полезно вводить случайное имя.

На практике я наблюдаю

  • Количество монадных преобразователейДоступно на Hackage очень здорово, и большинство из них довольно просты.Это классический пример проблемы, когда труднее выучить большую библиотеку, чем бросать свои собственные экземпляры.

  • Монады типа Writer, State и Environment настолько просты, что я неНе вижу большой пользы для монадных трансформаторов.

  • Там, где монадные трансформаторы сияют, это модульность и повторное использование.Это свойство прекрасно продемонстрировано Ляном, Худаком и Джонсом в их знаковой статье «Монадные трансформаторы и модульные переводчики» .

Являются ли наилучшими практиками монадных трансформаторовпри работе с этими общими задачами, упомянутыми выше?

Я бы сказал нет. Где монадные трансформаторы являются Лучшая практика - это когда у вас есть продукт строка связанных абстракций, которые вы можете создать, составляя и повторно используя монадные преобразователи различными способами.В таком случае вы, вероятно, разрабатываете ряд монадных преобразователей, которые важны для вашей проблемной области (например, тот, который был отклонен для GHC), и вы (а) составляете их несколькими способами;(б) достичь значительного количества повторного использования для большинства трансформаторов;(c) инкапсулируют что-то нетривиальное в каждом монадном трансформаторе.

Мой монадный трансформатор, который был отклонен для GHC, не соответствовал ни одному из критериев (a) / (b) / (c) выше.

8 голосов
/ 03 мая 2010

Концепция монадных трансформаторов довольно сложно и трудно понять, монадные трансформаторы приводят к подписи очень сложного типа

Думаю, это немного преувеличено:

  • Использовать конкретный стек монад в трансформаторе не сложнее, чем в простой монаде. Просто подумайте о слоях \ стопках, и все будет в порядке. Вам почти всегда не нужно поднимать чистую функцию (или конкретное действие ввода-вывода) более одного раза.
  • Как уже упоминалось, вы уже скрываете свой стек Monad в новом типе, используйте обобщенный вывод и скрываете конструктор данных в модуле.
  • Старайтесь не использовать конкретный стек Monad в сигнатуре типа функции, пишите общий код с классами типов Monad, такими как MonadIO, MonadReader и MonadState (используйте гибкое расширение контекстов, которое стандартизировано в Haskell 2010).
  • Использование таких библиотек, как fclabels, для сокращения стандартных действий, которые обращаются к частям записи в монаде.

Трансформаторы монад - не единственные варианты, вы можете написать собственную монаду, использовать продолжение монады. У вас есть изменяемые ссылки / массивы в IO (глобальный), ST (локальный и управляемый, без операций IO), MVar (синхронизация), TVar (транзакционный).

Я слышал, что потенциальные проблемы эффективности с преобразователями Monad можно уменьшить, просто добавив прагмы INLINE для привязки / возврата в исходном коде библиотеки mtl / transformers.

3 голосов
/ 07 декабря 2010

Я недавно "упал" на композицию монад в контексте F #. Я написал DSL с сильной зависимостью от монады состояния: все компоненты основаны на монаде состояния: синтаксический анализатор (монада анализатора, основанная на монаде состояния), таблицы сопоставления переменных (более одной для внутренних типов), таблицы поиска идентификаторов. И поскольку все эти компоненты работают вместе, они полагаются на одну и ту же государственную монаду. Поэтому существует понятие состава состояний, которое объединяет различные локальные состояния, и понятие средств доступа к состояниям, которые дают каждому алгоритму видимость своего состояния.

Изначально дизайн был действительно "просто одной большой государственной монадой". Но затем я начал нуждаться в состояниях с локальными временами жизни, и все же в контексте «постоянного» состояния (и опять же, все эти состояния управляются монадами). Для этого мне нужно было ввести преобразователи монад состояния, которые увеличивают состояние и адаптируют монады состояния вместе. Я также добавил трансформатор, чтобы свободно перемещаться между монадой состояния и монадой состояния продолжения, но я не потрудился его использовать.

Поэтому для ответа на вопрос: да, монадные трансформеры существуют в «диком» виде. Тем не менее, я бы категорически против использования их «из коробки». Напишите ваше приложение с простыми строительными блоками, используя небольшие мосты между вашими модулями, если вы в конечном итоге используете что-то вроде монадного преобразователя, это здорово; Не начинай оттуда.

И о типовых сигнатурах: я начал думать об этом типе программирования как о чем-то очень похожем на игру в шахматы с завязанными глазами (а я не шахматист): ваш уровень навыков должен быть таким, какой вы видите «Ваши функции и типы соответствуют друг другу. Сигнатуры типов в основном оказываются отвлекающими, если только вы явно не хотите добавлять ограничения типов по соображениям безопасности (или потому, что компилятор заставляет вас их давать, например, с записями F #).

2 голосов
/ 05 мая 2010

Когда я изучал монады, я создал приложение, используя стек StateT ContT IO для создания библиотеки имитации дискретных событий; продолжения использовались для хранения монадических потоков, причем StateT содержал очередь выполняемых потоков, а другие очереди использовались для приостановленных потоков, ожидающих различные события. Это работало довольно хорошо. Я не мог понять, как написать экземпляр Monad для оболочки нового типа, поэтому я просто сделал его синонимом типа, и это сработало довольно хорошо.

В эти дни я, вероятно, катил бы свою собственную монаду с нуля. Однако всякий раз, когда я делаю это, я смотрю на «All About Monads» и источник MTL, чтобы напомнить мне, как выглядят операции связывания, так что в некотором смысле я все еще думаю о терминах стека MTL, даже если результат пользовательская монада.

2 голосов
/ 04 мая 2010

Я думаю, что это заблуждение, только монада IO не чиста. монады как Запись / T / Reader / T / State / T / ST монады чисто функциональный до сих пор.

Мне кажется, что существует более одного понятия о термине чистый / не чистый. Ваше определение «IO = unpure, все остальное = pure» звучит похоже на то, о чем говорит Пейтон-Джонс в «Эффектах укрощения» (http://ulf.wiger.net/weblog/2008/02/29/peyton-jones-taming-effects-the-next-big-challenge/). С другой стороны, в Real World Haskell (на последних страницах Монады). Глава Transformer) противопоставляет чистые функции монадической функции в целом - утверждая, что вам нужны разные библиотеки для обоих миров. Кстати, можно утверждать, что IO также чист, его побочные эффекты заключаются в функцию State с типом RealWorld - > (a, RealWorld) . В конце концов, Haskell называет себя чисто функциональным языком (включая IO, я полагаю: -).)

Мой вопрос не столько в том, что можно сделать теоретически, а в том, что оказалось полезным с точки зрения разработки программного обеспечения. Монадные преобразователи допускают модульность эффектов (и абстракций в целом), но разве направление программирования должно быть направлено к?

2 голосов
/ 04 мая 2010

Так что то, что имеет тенденцию быть довольно глобальный как журнал или конфигурация, вы бы предложили поставить в IO монада? Смотря на (по общему признанию очень ограниченный набор) примеров, я прихожу думать, что код на Haskell имеет тенденцию быть либо чистый (т.е. совсем не монадический) или в монаде IO. Или это заблуждение?

Я думаю, что это заблуждение, только монада IO не чиста. такие монады, как Write / T / Reader / T / State / T / ST, все еще чисто функциональны. Вы можете написать чистую функцию, которая использует любую из этих монад внутри, как этот абсолютно бесполезный пример:

foo :: Int -> Int
foo seed = flip execState seed $ do
    modify $ (+) 3
    modify $ (+) 4
    modify $ (-) 2

Все, что это делает, - это неявное выполнение потоков / верстка состояния, то, что вы бы делали сами вручную, нотация здесь просто дает вам хороший синтаксический сахар, чтобы он выглядел обязательным. Вы не можете выполнять какие-либо действия ввода-вывода здесь, вы не можете вызывать какие-либо посторонние функции. Монада ST позволяет вам иметь реальные изменяемые ссылки в локальной области, имея чистый интерфейс функций, и вы не можете выполнять какие-либо действия ввода-вывода, там все еще чисто функционально.

Вы не можете избежать некоторых действий ввода-вывода, но вы не хотите возвращаться к вводу-выводу для всего, потому что это то, куда все может пойти, ракеты могут быть запущены, у вас нет контроля. У Haskell есть абстракции для управления эффективными вычислениями с различной степенью безопасности / чистоты, монада ввода-вывода должна быть последним средством (но вы не можете избежать этого полностью).

В вашем примере я думаю, что вы должны придерживаться монадных преобразователей или монады, изготовленной на заказ, которая делает то же самое, что и их составление с помощью преобразователей. Я никогда не писал собственную монаду (пока), но я довольно часто использовал монадные преобразователи (мой собственный код, а не на работе), не волнуйтесь о них так много, используйте их, и это не так плохо, как вы думаете .

Вы видели главу из Real World Haskell , в которой используются монадные трансформаторы ?

...