On Vox: Ограниченное - часть 2
Nov. 9th, 2008 09:43 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
В предыдущем решении один и тот же, по существу, код повторялся дважды. Это не есть хорошо. Вполне можно написать то же самое ОДИН раз, используя "Identity" в первом случае и "[]" во втором. Ну и, интуиция подсказывает, что нужно использовать тот факт, что и то, и другое является монадой.
Нам понадобится чуть больший список импортов:
Тип данных мы определим так же, как и "OrdP" ранее, только вместо "[]" у нас будет произвольное "n".
> {-# 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
Снова так же определяется инстанс "Monad".
> 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)}
Теперь, "OrdT m n" является монадой, если "n" является монадой. То есть, "OrdT m" - это такой преобразователь монад. Трансформер, иначе говоря. Для полноты картины можно объявить и инстанс "MonadTrans".
> 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
Я не знаю, зачем это может понадобиться, но пусть уж будет, хотя бы для красоты.
> instance MonadTrans (OrdT m) where lift nx = OrdT $ liftM OrdReturn nx
Функция "embed" выглядит как и раньше, а вот "unembed" - не совсем. В предыдущем посте "unembed" и "unembedPlus" различались - в unembedPlus участвовала функция "ordMSum". Поскольку её аналог для произвольного "n" заранее неизвестен, придётся передавать её в "unembed" как параметр.
Ну, теперь несложно воспроизвести старый "OrdM":
> 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
Далее, если "n" - не просто монада, но инстанс "MonadPlus" (как, скажем, "[]"), то "OrdT m n" - тоже инстанс "MonadPlus"; кроме того, если "m" - инстанс "OrdMonadPlus", то можно воспроизвести старое "unembedPlus":
> type OrdM m a = OrdT m Identity a
> unembedId :: (OrdMonad m, Ord a) => OrdM m a -> m a
> unembedId = unembed $ runIdentity
Наш пример теперь выглядит так:
> 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