-
MonadComprehensions -
- Since:
-
7.2.1
Включить синтаксис понимания списков для произвольных монад.
Понимания монады обобщают запись понимания списков, включая параллельные понимания (Параллельные понимания списков) и понимания преобразований (Обобщенные (похожие на SQL) понимания списков), чтобы они работали для любой монады.
Понимания монады поддерживают:
-
Связывания:
[ x + y | x <- Just 1, y <- Just 2 ]
Связывания переводятся с помощью функций
(>>=)иreturnв обычную запись do-нотации:do x <- Just 1 y <- Just 2 return (x+y)
-
Условий:
[ x | x <- [1..10], x <= 5 ]
Условиям сопоставляется функция
guard, которая требует экземплярAlternative:do x <- [1..10] guard (x <= 5) return x
-
Выражения преобразования (как и с
TransformListComp):[ x+y | x <- [1..10], y <- [1..x], then take 2 ]
Это переводится в:
do (x,y) <- take 2 (do x <- [1..10] y <- [1..x] return (x,y)) return (x+y) -
Выражения группировки (как и с
TransformListComp):[ x | x <- [1,1,2,2,3], then group by x using GHC.Exts.groupWith ] [ x | x <- [1,1,2,2,3], then group using myGroup ]
-
Параллельные выражения (как и с
ParallelListComp):[ (x+y) | x <- [1..10] | y <- [11..20] ]Параллельные выражения переводятся с помощью функции
mzip, которая требует экземплярMonadZip, определённый в Control.Monad.Zip:do (x,y) <- mzip (do x <- [1..10] return x) (do y <- [11..20] return y) return (x+y)
Все эти возможности включены по умолчанию, если включен расширение MonadComprehensions. Типы и более подробные примеры использования пониманий объясняются в предыдущих главах Обобщенные (похожие на SQL) понимания списков и Параллельные понимания списков. В общем случае вам нужно просто заменить тип [a] на тип Monad m => m a для пониманий монады.
Примечание
Несмотря на то, что большинство этих примеров используют монаду списка, понимания монады работают для любой монады. Пакет base предлагает все необходимые экземпляры для списков, что делает MonadComprehensions обратной совместимой с встроенными, преобразовательными и параллельными пониманиями списков.
Более формально, отжатие выглядит следующим образом. Мы пишем D[ e | Q] для обозначения отжатия понимания монады [ e | Q]:
Expressions: e
Declarations: d
Lists of qualifiers: Q,R,S
-- Basic forms
D[ e | ] = return e
D[ e | p <- e, Q ] = e >>= \p -> D[ e | Q ]
D[ e | e, Q ] = guard e >> \p -> D[ e | Q ]
D[ e | let d, Q ] = let d in D[ e | Q ]
-- Parallel comprehensions (iterate for multiple parallel branches)
D[ e | (Q | R), S ] = mzip D[ Qv | Q ] D[ Rv | R ] >>= \(Qv,Rv) -> D[ e | S ]
-- Transform comprehensions
D[ e | Q then f, R ] = f D[ Qv | Q ] >>= \Qv -> D[ e | R ]
D[ e | Q then f by b, R ] = f (\Qv -> b) D[ Qv | Q ] >>= \Qv -> D[ e | R ]
D[ e | Q then group using f, R ] = f D[ Qv | Q ] >>= \ys ->
case (fmap selQv1 ys, ..., fmap selQvn ys) of
Qv -> D[ e | R ]
D[ e | Q then group by b using f, R ] = f (\Qv -> b) D[ Qv | Q ] >>= \ys ->
case (fmap selQv1 ys, ..., fmap selQvn ys) of
Qv -> D[ e | R ]
where Qv is the tuple of variables bound by Q (and used subsequently)
selQvi is a selector mapping Qv to the ith component of Qv
Operator Standard binding Expected type
--------------------------------------------------------------------
return GHC.Base t1 -> m t2
(>>=) GHC.Base m1 t1 -> (t2 -> m2 t3) -> m3 t3
(>>) GHC.Base m1 t1 -> m2 t2 -> m3 t3
guard Control.Monad t1 -> m t2
fmap GHC.Base forall a b. (a->b) -> n a -> n b
mzip Control.Monad.Zip forall a b. m a -> m b -> m (a,b)
Понимание должно пройти проверку типов, когда его отжатие пройдёт проверку типов, за исключением того, что (как обсуждалось в Обобщенные (похожие на SQL) понимания списков) в пунктах «тогда f» и «тогда сгруппировать с использованием f», когда квалификатор «по b» опущен, аргумент f должен иметь полиморфный тип. В частности, «тогда Data.List.sort» и «тогда сгруппировать с использованием Data.List.group» недостаточно полиморфны.
Понимания монады поддерживают переназначимый синтаксис (Переназначимый синтаксис и неявный импорт Prelude). Без переназначимого синтаксиса операторы из модуля «стандартного связывания» используются; с переназначимым синтаксисом операторы ищутся в текущей лексической области видимости. Например, параллельные понимания будут проверены по типам и отжаты с использованием того, что «mzip» находится в области видимости.
Переназначимые операторы должны иметь «Ожидаемый тип», указанный в таблице выше. Эти типы довольно общие. Например, вы можете использовать оператор связи с типом
(>>=) :: T x y a -> (a -> T y z b) -> T x z b
В случае пониманий преобразования обратите внимание, что группы параметризованы по некоторому произвольному типу n (при условии, что у него есть fmap, а также понимание по произвольной монаде.