В моей области бизнеса - бэк-офиса IT для финансового учреждения - компоненту программного обеспечения очень свойственно нести глобальную конфигурацию вокруг, зарегистрировать ее прогресс, иметь некоторую обработку ошибок / короткое замыкание вычисления... Вещи, которые могут быть смоделированы приятно Читателем - Устройство записи - Возможно-монады и т.п. в Haskell и составлены вместе с преобразователями монады.
Но там кажусь некоторым недостаткам: понятие позади преобразователей монады довольно хитро и трудно понять, вывод преобразователей монады к подписям очень составного типа, и они причиняют некоторую потерю производительности.
Таким образом, я задаюсь вопросом: лучшая практика преобразователей Монады при контакте с теми упомянутыми выше общими задачами?
Сообщество Haskell расколото по этому вопросу.
Джон Хьюз сообщает, что ему легче обучать монадным преобразователям, чем монадам, и что его ученики лучше справляются с подходом «сначала преобразователи».
Разработчики GHC обычно избегают преобразователей монад, предпочитая сворачивать свои собственные монады, которые объединяют все необходимые им функции. (Мне только сегодня недвусмысленно сказали, что GHC не будет использовать преобразователь монад, который я определил три дня назад.)
Для меня преобразователи монад во многом похожи на программирование без точек (т. Е. программирование без именованных переменных), что имеет смысл; в конце концов, они как раз беспочвенны в программировании на уровне типов. Мне никогда не нравилось программирование без точек, потому что полезно иметь возможность вводить случайное имя.
На практике я наблюдаю
. Количество преобразователей монад, доступных в Hackage, очень велико, и большинство из них довольно просты. Это классический пример проблемы, когда сложнее изучить большую библиотеку, чем катать собственные экземпляры.
Монады, такие как Writer, State и Environment, настолько просты, что я не вижу особой пользы от преобразователей монад.
Преобразователи монад блистают в модульности и многократном использовании. Это свойство прекрасно продемонстрировали Лян, Худак и Джонс в их знаменательной статье «Преобразователи монад и модульные интерпретаторы» .
Являются ли преобразователи монад наилучшей практикой при решении этих общих задач, упомянутых выше?
Я бы сказал нет. Преобразователи монад лучше всего подходят для линейки продуктов связанных абстракций, которые можно создавать, составляя и повторно используя преобразователи монад различными способами. В таком случае вы, вероятно, разработаете ряд преобразователей монад, которые важны для вашей проблемной области (например, тот, который был отклонен для GHC), и вы (а) составите их несколькими способами; (б) добиться значительного повторного использования большинства трансформаторов; (c) инкапсулируют что-то нетривиальное в каждом преобразователе монад.
Мой преобразователь монад, который был отклонен для GHC, не соответствовал ни одному из критериев (a) / (b) / (c) выше.
Я думаю, что это заблуждение, только монада ввода-вывода не является чистой. монады, такие как Write / T / Reader / T / State / T / ST, все еще полностью функциональны.
Мне кажется, существует несколько понятий относительно термина «чистый / нечистый».Ваше определение «IO = нечистота, все остальное = чистое» звучит аналогично тому, о чем говорит Пейтон-Джонс в «Эффектах приручения» ( http://ulf.wiger.net/weblog/2008/02/29/peyton- jones-taming-effects-the-next-big-challenge / ). С другой стороны, Haskell реального мира (на последних страницах главы о преобразователе монад) противопоставляет чистые функции монадическим функциям в целом, утверждая, что вам нужны разные библиотеки для обоих миров. Кстати, можно утверждать, что ввод-вывод также является чистым, его побочные эффекты инкапсулируются в функцию состояния с типом RealWorld -> (a, RealWorld) . В конце концов, Haskell называет себя чисто функциональным языком (включая ввод-вывод, я полагаю: -).)
Мой вопрос не столько о том, что можно сделать теоретически, сколько о том, что оказалось полезным с точки зрения разработки программного обеспечения. зрения. Преобразователи монад допускают модульность эффектов (и абстракций в целом), но разве это направление программирования должно быть направлено к?
Концепция преобразователей монад довольно сложна и трудна для понимания, преобразователи монад приводят к очень сложным сигнатурам типов.
Думаю, это немного преувеличение:
Преобразователи монад - не единственные варианты, вы можете написать собственную монаду, используя монаду продолжения. У вас есть изменяемые ссылки / массивы в IO (глобальный), ST (локальный и управляемый, без действий ввода-вывода), MVar (синхронизация), TVar (транзакционный).
Я слышал, что потенциальные проблемы с эффективностью преобразователей Monad можно уменьшить, просто добавив прагмы INLINE для связывания / возврата в исходный код библиотеки mtl / transformers.
То есть что-то, что имеет тенденцию быть скорее глобальным, например, журнал или конфигурация, вы бы посоветовали добавить монаду ввода-вывода ? Глядя на (по общему признанию очень ограниченный набор) примеров, я прихожу к мысли, что код Haskell имеет тенденцию быть либо чистым (т. Е. Совсем не монадическим) {{ 1}} или в монаде ввода-вывода. Или это заблуждение?
Я думаю, что это заблуждение, только монада IO не чиста. монады наподобие Write / T / Reader / T / State / T / ST все еще остаются чисто функциональными. Вы можете написать чистую функцию, которая использует любую из этих монад внутренне, как этот совершенно бесполезный пример:
foo :: Int -> Int
foo seed = flip execState seed $ do
modify $ (+) 3
modify $ (+) 4
modify $ (-) 2
Все, что это делает, - это неявная потоковая передача / привязка состояния, то, что вы бы сделали вручную явно, здесь просто обозначение do дает вам немного синтаксического сахара, чтобы он выглядел императивным. Здесь вы не можете выполнять какие-либо действия ввода-вывода, вы не можете вызывать какие-либо внешние функции. Монада ST позволяет вам иметь реальные изменяемые ссылки в локальной области видимости, имея при этом чистый функциональный интерфейс, и вы не можете выполнять какие-либо действия ввода-вывода в нем, он все еще остается чисто функциональным.
Вы не можете избежать некоторых действий ввода-вывода, но вы не хотите прибегать к вводу-выводу для всего, потому что это то, куда может пойти все, ракеты могут быть запущены, у вас нет контроля.В Haskell есть абстракции для управления эффективными вычислениями с разной степенью безопасности / чистоты, монада ввода-вывода должна быть последним средством (но вы не можете полностью избежать этого).
В вашем примере, я думаю, вам следует придерживаться использования преобразователей монад или монад, сделанных на заказ, которые делают то же самое, что и их компоновка с помощью преобразователей. Я никогда не писал кастомную монаду (пока), но я довольно часто использовал преобразователи монад (мой собственный код, не на работе), не беспокойтесь о них так сильно, используйте их, и это не так плохо, как вы думаете .
Вы видели главу из Real World Haskell , в которой используются преобразователи монад ?
Когда я изучал монады, я создал приложение, использующее стек StateT ContT IO для создания библиотеки моделирования дискретных событий; продолжения использовались для хранения монадических потоков, причем StateT хранил очередь выполняемых потоков, а другие очереди использовались для приостановленных потоков, ожидающих различных событий. Это работало довольно хорошо. Я не мог понять, как написать экземпляр монады для обертки нового типа, поэтому я просто сделал его синонимом типа, и это работало довольно хорошо.
В наши дни я бы, наверное, создал свою собственную монаду с нуля. Однако всякий раз, когда я это делаю, я обращаюсь к "All About Monads" и источнику MTL, чтобы напомнить, как выглядят операции связывания, так что в некотором смысле я все еще думаю в терминах стека MTL, даже если результатом является собственная монада.