On Vox: А с континуэйшенами проще
Nov. 15th, 2008 07:18 pmОпять про ограниченные монады (см. предыдущий пост). Модуль "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 where RestrCont :: ...", используя GADT-ы вместо экзистеншиалсов. Преимущество такого подхода в том, что, если вместо класса "Suitable" использовать что-нибудь нормальное, типа класса "Ord", то полученная вещь заработает, например, в Hugs.
> data RestrCont m a = RestrCont (forall b. Suitable m b => (a -> m b) -> m b)
Объявляем наш тип монадой:
В отличие от предыдущего примера, для того, чтобы сделать новый тип инстансом "MonadPlus" его не нужно менять; нужно лишь, чтобы само "m" поддерживало нужные операции.
> 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
Операции "embed" и "unembed" тоже упрощаются. Первая из них использует только "restrBind"
> instance RestrPlus m => MonadPlus (RestrCont m) where
> mzero = RestrCont $ const restrMZero
> RestrCont g1 `mplus` RestrCont g2 = RestrCont $ \h -> g1 h `restrMPlus` g2 h
а вторая - только "restrReturn"
> embed :: (RestrMonad m, Suitable m a) => m a -> RestrCont m a
> embed mx = RestrCont $ \h -> restrBind mx h
> 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