Вот кое-что, что я сделал недавно, и может показать некоторые возможности монад. Фактический код здесь не показан для защиты невинных, это всего лишь набросок.
Допустим, вы хотите выполнить поиск по какому-либо словарю и, в зависимости от того, что вы найдете, вы хотите выполнить какой-либо другой поиск. Поиски могут вернуть ничего (элемент, который вы ищете, не существует), в этом случае вы можете попробовать другой поиск, а если все поиски не пройдут, вы вернете ничего.
Идея состоит в том, чтобы создать собственную монаду, комбинируя монадные трансформаторы, и тогда мы можем легко создать несколько комбинаторов для поиска. Наша монада будет 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
.