migmit: (Default)
[personal profile] migmit

Опять про ограниченные монады (см. предыдущий пост). Модуль "Suitable" остаётся тем же, что и был раньше, а вот основной тип данных меняется.

Что нам, фактически, нужно от значения типа "Restricted m a"? Нам нужно сбиндить его с последовательностью функций (первая - типа "a -> Restricted m b", вторая - "b -> Restricted m c" и т. д.), доведя до значения, для которого осмысленно запихивание в монаду "m", а затем сделать ему "unembed". Ну так мы сделаем это всё за один шаг, и этот самый шаг сделаем значением нашего нового типа.

Нам понадобятся existential types.


> {-# OPTIONS -fglasgow-exts #-}
> module RestrCont where
> import Control.Monad
> import qualified Data.Set as Set
> import Suitable
Основной тип данных:

> data RestrCont m a = RestrCont (forall b. Suitable m b => (a -> m b) -> m b)
В принципе, можно было бы написать "data RestrCont m a where RestrCont :: ...", используя GADT-ы вместо экзистеншиалсов. Преимущество такого подхода в том, что, если вместо класса "Suitable" использовать что-нибудь нормальное, типа класса "Ord", то полученная вещь заработает, например, в Hugs.

Объявляем наш тип монадой:


> instance Monad (RestrCont m) where
>     return x = RestrCont $ \h -> h x
>     RestrCont g >>= f = RestrCont $ \h -> g $ \x -> case f x of RestrCont g' -> g' h
В отличие от предыдущего примера, для того, чтобы сделать новый тип инстансом "MonadPlus" его не нужно менять; нужно лишь, чтобы само "m" поддерживало нужные операции.

> instance RestrPlus m => MonadPlus (RestrCont m) where
>     mzero = RestrCont $ const restrMZero
>     RestrCont g1 `mplus` RestrCont g2 = RestrCont $ \h -> g1 h `restrMPlus` g2 h
Операции "embed" и "unembed" тоже упрощаются. Первая из них использует только "restrBind"

> embed :: (RestrMonad m, Suitable m a) => m a -> RestrCont m a
> embed mx = RestrCont $ \h -> restrBind mx h
а вторая - только "restrReturn"

> unembed :: (RestrMonad m, Suitable m a) => RestrCont m a -> m a
> unembed (RestrCont g) = g restrReturn

Пример (ищите его в предыдущих постах) проходит без изменений.
Раньше, дабы соорудить некоторую алгебраическую операцию на "Restricted m" (из такой же алгебраической операции на m), нам нужно было изменить монаду "n" - так из "RestrPlus m" получалось "MonadPlus (Restricted m)". Сейчас это можно сделать, опять таки, проще:

> liftRestr :: Functor n => (forall a. Suitable m a => n (m a) -> m a) -> n (RestrCont m b) -> RestrCont m b
> liftRestr f nrmb = RestrCont $ \h -> f $ fmap (\(RestrCont g) -> g h) nrmb

Originally posted on migmit.vox.com

If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting