migmit: (Default)
[personal profile] migmit

В предыдущем решении один и тот же, по существу, код повторялся дважды. Это не есть хорошо. Вполне можно написать то же самое ОДИН раз, используя "Identity" в первом случае и "[]" во втором. Ну и, интуиция подсказывает, что нужно использовать тот факт, что и то, и другое является монадой.

Нам понадобится чуть больший список импортов:


> {-# LANGUAGE GADTs #-}
> module Rest2 where
> import Control.Monad
> import Control.Monad.Identity
> import Control.Monad.List
> import Control.Monad.Trans
> import qualified Data.Set as Set
> import OrdMonadSet
Тип данных мы определим так же, как и "OrdP" ранее, только вместо "[]" у нас будет произвольное "n".

> data OrdTData m n a where
>     OrdReturn :: a -> OrdTData m n a
>     OrdBind :: Ord a => m a -> (a -> OrdT m n b) -> OrdTData m n b
> newtype OrdT m n a = OrdT {fromOrdT :: n (OrdTData m n a)}
Снова так же определяется инстанс "Monad".

> instance Monad n => Monad (OrdT m n) where
>     return = OrdT . return . OrdReturn
>     OrdT nomnx >>= f = OrdT $ nomnx >>= handleOmnx
>       where handleOmnx (OrdReturn x) = fromOrdT $ f x
>             handleOmnx (OrdBind mu g) = return $ OrdBind mu $ \x -> g x >>= f
Теперь, "OrdT m n" является монадой, если "n" является монадой. То есть, "OrdT m" - это такой преобразователь монад. Трансформер, иначе говоря. Для полноты картины можно объявить и инстанс "MonadTrans".

> instance MonadTrans (OrdT m) where lift nx = OrdT $ liftM OrdReturn nx
Я не знаю, зачем это может понадобиться, но пусть уж будет, хотя бы для красоты.

Функция "embed" выглядит как и раньше, а вот "unembed" - не совсем. В предыдущем посте "unembed" и "unembedPlus" различались - в unembedPlus участвовала функция "ordMSum". Поскольку её аналог для произвольного "n" заранее неизвестен, придётся передавать её в "unembed" как параметр.


> embed mx = OrdT $ return $ OrdBind mx return
> unembed f = unembed'
>     where unembed' (OrdT nomnx) = f $ liftM handleOmnx nomnx
>           handleOmnx (OrdReturn x) = ordReturn x
>           handleOmnx (OrdBind mu g) = ordBind mu $ unembed' . g
Ну, теперь несложно воспроизвести старый "OrdM":

> type OrdM m a = OrdT m Identity a
> unembedId :: (OrdMonad m, Ord a) => OrdM m a -> m a
> unembedId = unembed $ runIdentity
Далее, если "n" - не просто монада, но инстанс "MonadPlus" (как, скажем, "[]"), то "OrdT m n" - тоже инстанс "MonadPlus"; кроме того, если "m" - инстанс "OrdMonadPlus", то можно воспроизвести старое "unembedPlus":

> instance MonadPlus n => MonadPlus (OrdT m n) where
>     mzero = OrdT mzero
>     OrdT nomnx1 `mplus` OrdT nomnx2 = OrdT $ nomnx1 `mplus` nomnx2
> unembedPlus :: (OrdMonadPlus m, Ord a) => OrdT m [] a -> m a
> unembedPlus = unembed $ foldr ordMPlus ordMZero
Наш пример теперь выглядит так:

> test = unembedPlus $ do x <- embed $ Set.fromList [6, 2, 3]
>                         (do y <- return x
>                             z <- embed $ Set.fromList [1..2]
>                             guard $ y < 5
>                             return $ y + z)
>                         `mplus` return 10

Originally posted on migmit.vox.com