как мога да внедря този монаден трансформатор с продължение?

мотивация. Опитвам се да създам монаден трансформатор със специална инструкция f <||> g, която означава "повторете целия този блок, съдържащ f <||> g, веднъж с f, следващия път с g". Това е предназначено да бъде за DSL трансформация, въпреки че можете да си представите други приложения.

примерна употреба. Монадата computation изразява различни възможни избори (в този случай на неща за отпечатване). Функцията printme казва какво да се прави с всеки различен резултат. В този случай ние отпечатваме "start computation" преди да се изпълни и "---" след това.

computation = do
    lift (print "start -- always")
    (lift (print "first choice") <||> lift (print "second choice"))
    lift (print "intermediate -- always")
    (lift (print "third choice") <||> lift (print "fourth choice"))
    lift (print "end -- always")

printme x = do
    putStrLn "=== start computation"
    xv <- x
    putStrLn "---\n"
    return xv

test = runIndep printme computation

изходът е следният,

=== start computation
"start -- always"
"first choice"
"intermediate -- always"
"third choice"
"end -- always"
---

=== start computation
"start -- always"
"first choice"
"intermediate -- always"
"fourth choice"
"end -- always"
---

=== start computation
"start -- always"
"second choice"
"intermediate -- always"
"third choice"
"end -- always"
---

=== start computation
"start -- always"
"second choice"
"intermediate -- always"
"fourth choice"
"end -- always"
---

въпрос. Има ли чист начин за постигане на горното поведение, като се използва някакъв вид трансформатор на монада в стил на продължаващо преминаване? Разгледах статията на Oleg et al. "Backtracking, Interleaving, and Terminating Monad Transformers", но изглежда не мога да разбера напълно тяхната реализация (след като стигнат до msplit реализация с продължения).

текуща реализация. Текущата ми реализация е да предам списък с решения за разклоняване, които трябва да бъдат взети. Монадата ще върне списъка с клоновете, които действително е избрала, и след това следващия път ще превключим последния възможен клон. Кодът е както следва (трябва да работи в 7.0.3),

import Control.Monad.Trans.Class

data IndepModelT ???? α = IndepModelT {
    unIndepModelT :: [Bool] -> ???? (α, [Bool]) }

instance Monad ???? => Monad (IndepModelT ????) where
    return x = IndepModelT $ \choices -> return (x, [])
    (IndepModelT x) >>= f = IndepModelT $ \choices -> do
        (xv, branches) <- x choices
        let choices' = drop (length branches) choices
        (fxv, branches') <- unIndepModelT (f xv) choices'
        return (fxv, branches ++ branches')

instance MonadTrans IndepModelT where
    lift x = IndepModelT $ \c -> liftWithChoice [] x
liftWithChoice cs mx = mx >>= \xv -> return (xv, cs)

(<||>)
  :: Monad ???? => IndepModelT ???? α -> IndepModelT ???? α -> IndepModelT ???? α
(IndepModelT f) <||> (IndepModelT g) = IndepModelT go where
    go (False:cs) = do
        (fv, branches) <- f cs
        return (fv, False : branches)
    go (True:cs) = do
        (fv, branches) <- g cs
        return (fv, True : branches)

run_inner next_choices k comp@(IndepModelT comp_inner) = do
    (xv, branches) <- k $ comp_inner next_choices
    case (get_next_choices branches) of
        Nothing -> return ()
        Just choices -> run_inner (choices ++ repeat False) k comp
    where
        get_next_choices [] = Nothing
        get_next_choices [True] = Nothing
        get_next_choices [False] = Just [True]
        get_next_choices (c:cs)
            | Just cs' <- get_next_choices cs = Just $ c:cs'
            | c Prelude.== False = Just [True]
            | otherwise = Nothing

runIndep :: Monad ???? =>
    (???? (α, [Bool]) -> ???? (β, [Bool]))
    -> IndepModelT ???? α
    -> ???? ()
runIndep = run_inner (repeat False)

runIndepFirst (IndepModelT comp) = comp (repeat False)

person gatoatigrado    schedule 04.12.2011    source източник
comment
Виждали ли сте haskell.org/haskellwiki/ListT_done_right и по-специално алтернативната реализация haskell.org/haskellwiki/ListT_done_right_alternative ?   -  person John L    schedule 05.12.2011
comment
Не мисля, че имаш нужда от продължения. Това, което изглежда имате, е вид дърво, където всяка операция ‹||› представлява клон. Но не мога да разбера правилния тип данни.   -  person Paul Johnson    schedule 05.12.2011


Отговори (3)


Ето го проблемът: това не е монада! Поведението дори не е добре дефинирано. F.e. какво трябва да направи в този случай:

do
  b <- ...randomly True or False...
  if b then ...some choices... else ...some other choices...

Въпреки това е Applicative. Типът, от който се нуждаем, е [IO a], който е съставът на 2 апликативни функтора, така че можем да използваме Data.Functor.Compose от пакета transformers. Това дава Alternative екземпляр с <|> също безплатно. Ще използваме Rebindable Syntax, за да използваме do-notation за Applicatives:

{-# LANGUAGE RebindableSyntax #-}
import Prelude hiding ((>>), (>>=))
import Control.Applicative
import Data.Functor.Compose

lift :: Applicative f => g a -> Compose f g a
lift = Compose . pure

(>>) :: Applicative f => f a -> f b -> f b
(>>) = (*>)

computation :: Alternative f => Compose f IO ()
computation = do
    lift (print "start -- always")
    lift (print "first choice") <|> lift (print "second choice")
    lift (print "intermediate -- always")
    lift (print "third choice") <|> lift (print "fourth choice")
    lift (print "end -- always")

printme x = do
    putStrLn "=== start computation"
    x
    putStrLn "---\n"

test = mapM printme $ getCompose computation
person Sjoerd Visscher    schedule 06.12.2011

Предложението, което сте получили досега, не работи. Ето как ще стане това:

f <||> g = ContT $ \k -> do
  xs <- runContT f k
  ys <- runContT g k
  return $ xs ++ ys

test = runContT computation (return . (:[]))

Но това не рестартира цялото изчисление за всеки избор, резултатът е следният:

"start -- always"
"first choice"
"intermediate -- always"
"third choice"
"end -- always"
"fourth choice"
"end -- always"
"second choice"
"intermediate -- always"
"third choice"
"end -- always"
"fourth choice"
"end -- always"

Все още не съм намерил добро решение.

person Sjoerd Visscher    schedule 05.12.2011
comment
Наистина, благодаря, че го посочихте. Предложението на @John L за ListT направения правилен трансформатор изглежда по-добре за повторно изпълнение на цялото изчисление, но трябва да пропусна тестова програма. - person acfoltzer; 05.12.2011

Ако търсите специално базиран на продължаване подход, няма да получите много по-просто от SFKT изпълнението на продължаване на успех/неуспех в LogicT хартия.

Ако msplit е твърде много (и е доста фин звяр), можете просто да го игнорирате за това приложение. Неговата цел е да позволи справедливо свързване и разделяне, което не е част от вашата спецификация, ако тези редове от примерен изход са предназначени да се отпечатват в ред. Просто се съсредоточете върху имплементациите Monad и MonadPlus в раздел 5.1 и ще сте готови.

Актуализация: Както посочва Sjoerd Visscher, това не е правилно, тъй като рестартирането се случва само от mplus, а не от цялото изчисление. Това е много по-сложен проблем, отколкото изглежда на пръв прочит.

person acfoltzer    schedule 05.12.2011