Как монады могут облегчить мою работу?Покажите мне какой-нибудь классный кусок кода - PullRequest
5 голосов
/ 07 октября 2010

Мне нравится читать фрагменты кода о понятиях, которые я не понимаю.Есть ли фрагменты, которые демонстрируют монады во всей их красе?Что еще более важно, как я могу применить монады, чтобы облегчить мою работу.

Я интенсивно использую jQuery.Это одно классное приложение монад, о которых я знаю.

Ответы [ 5 ]

3 голосов
/ 07 октября 2010

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

В любом случае, вот пример использования монады List из теста-running скрипт, который я написал:

runAll :: IO ()
runAll = do
  curdir <- getCurrentDirectory
  sequence $ runTest <$> srcSets <*> optExeFlags <*> optLibFlags
  setCurrentDirectory curdir

Технически я использую интерфейс Applicative, но вы можете просто изменить <*> на ap из Control.Monad, если вас это беспокоит.

Крутая вещь в этом состоит в том, что он вызывает runTest для каждой комбинации аргументов из списков "srcSets", "optExeFlags" и "optLibFlags" для генерации данных профилирования для каждого из этих наборов.Я думаю, что это намного лучше, чем то, что я сделал бы в C (3 вложенных цикла).

2 голосов
/ 07 октября 2010

По сути, монады являются «императивными миниязыками».Следовательно, они позволяют вам использовать любую императивную конструкцию, такую ​​как исключения (Maybe), ведение журнала (Writer), ввод / вывод (IO), состояние (State), недетерминизм (списки [a]), парсеры (Parsec, ReadP) или их комбинации.

Для более сложных примеров посмотрите код примера для моего операционного пакета .В частности,

  • WebSessionState.lhs реализует веб-сессии, которые запрограммированы так, как если бы сервер был постоянным процессом, в то время как они фактически доставляются асинхронно.
  • TicTacToe.hs показывает игровой движок, в котором игроки и ИИ пишутся так, как если бы они работали в параллельных процессах.
2 голосов
/ 07 октября 2010

Ваш вопрос действительно расплывчатый - это все равно, что задавать вопрос «показать пример кода, который использует переменные». Это настолько присуще программированию, что любой код будет примером. Итак, я просто дам вам самую последнюю из посещаемых функций Haskell, которая все еще открыта в моем редакторе, и объясню, почему я использовал монадический поток управления.

Это фрагмент кода из моего файла конфигурации xmonad. Это часть реализации макета, который ведет себя определенным образом, когда нужно управлять одним окном, а другим - для нескольких окон. Эта функция принимает сообщение и генерирует новый макет. Если мы решим, что никаких изменений не будет, мы ничего не вернем:

handleMessage' :: AlmostFull a -> SomeMessage -> Int -> Maybe (AlmostFull a)
handleMessage' l@(AlmostFull ratio delta t) m winCount =
  case winCount of
    -- keep existing Tall layout, maybe update ratio
    0 -> finalize (maybeUpdateRatio $ fromMessage m) (Just t)
    1 -> finalize (maybeUpdateRatio $ fromMessage m) (Just t)

    -- keep existing ratio, maybe update Tall layout
    _ -> finalize (Just ratio) (pureMessage t m)
  where
    finalize :: Maybe Rational -> Maybe (Tall a) -> Maybe (AlmostFull a)
    finalize ratio t = ratio >>= \ratio -> t >>= \t ->
      return $ AlmostFull ratio delta t

    maybeUpdateRatio :: Message -> Maybe Rational
    maybeUpdateRatio (Just Shrink) = Just (max 0 $ ratio-delta)
    maybeUpdateRatio (Just Expand) = Just (min 1 $ ratio+delta)
    maybeUpdateRatio _             = Nothing

Мы решаем, что возвращать, основываясь на текущем состоянии оконного менеджера (которое определяется вычислением в монаде X, чей результат мы передаем этой функции, чтобы сохранить чистую действительную логику) - если имеется 0 или 1 окно , мы передаем сообщение в макет NearFull и позволяем ему решить, что делать. Это функция f. Возвращает Just новое соотношение, если сообщение изменяет соотношение, в противном случае ничего не возвращается. Другая половина похожа; оно передает сообщение в обработчик Tall, если имеется 2 или более окон. Это возвращает Just новый Tall макет, если это то, о чем просил пользователь, в противном случае возвращается Nothing.

Функция finalize является интересной частью; он извлекает ratio (желаемое новое соотношение) и t (желаемое новое Tall расположение) из своей оболочки Maybe. Это означает, что оба должны быть не Nothing, в противном случае мы автоматически возвращаем Nothing из нашей функции.

Причина, по которой мы использовали здесь монаду Maybe, заключалась в том, что мы могли написать функцию, зависящую от всех доступных результатов, без необходимости писать какой-либо код для обработки случаев, когда появился Nothing.

1 голос
/ 07 октября 2010

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

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

Идея состоит в том, чтобы создать собственную монаду, комбинируя монадные трансформаторы, и тогда мы можем легко создать несколько комбинаторов для поиска. Наша монада будет ReaderT Dictionary Возможно. И мы определяем функции find, которые ищут данный ключ, both, который будет возвращать список элементов, найденных в обоих запросах, и oneOf, который выполняет два поиска и пробует первый, и если это не так не удается, он пытается второй. Вот пример такого поиска:

import Control.Monad
import Control.Monad.Reader

find a = ReaderT (lookup a)
both a b = liftM2 (++) a b
oneOf = mplus

search = both (find 1) ((find 2) `oneOf` (find 3)) 
         `oneOf` both (find 4) (find 5)

И работает:

(runReaderT search) [(1,"a"),(3,"c"),(4,"d"),(5,"g")] --> Just "ac"

(runReaderT search) [(6,"a")] --> Nothing

Большое преимущество, которое мы получаем, будучи монадой, заключается в том, что мы можем связывать поиски вместе и поднимать другие функции в эту абстракцию. Скажем, например, у меня есть два поиска search_a и search_b, и я хочу сделать их, а затем вернуть их объединенными:

 do a <- search_a
    b <- search_b
    return (merge a b)

или альтернативно liftM2 merge search_a search_b.

1 голос
/ 07 октября 2010

Я изучал безопасность на Haskell и в информационном потоке.Эта статья довольно интересная, она использует Monads для обеспечения конфиденциальности в программах на Haskell.

http://www.cse.chalmers.se/~russo/seclib.htm

...