簡體   English   中英

如何實現這個monad變換器的延續?

[英]how can I implement this monad transformer with a continuation?

動機 我正在嘗試使用特殊指令f <||> g創建一個monad變換器,這意味着“重復整個包含f <||> g塊,一次用f ,下一次用g ”。 這可以用於DSL轉換,但您可以想象其他應用程序。

示例用法 computation monad表示不同的可能選擇(在這種情況下,是要打印的東西)。 printme函數說明如何處理每個不同的結果。 在這種情況下,我們在運行之前打印“開始計算”,之后打印“---”。

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"
---

問題 使用某種延續傳遞方式monad變換器有沒有一種干凈的方法來實現上述行為? 我看過Oleg等人的“Backtracking,Interleaving,Terminating Monad Transformers”論文,但似乎無法完全掌握它們的實現(一旦它們進入msplit實現並繼續)。

目前的實施 我目前的實現是傳遞一個分支決策列表。 monad將返回它實際選擇的分支列表,然后下次我們將切換最后一個可能的分支。 代碼如下(應該在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)

這是問題所在:這不是一個單子! 這種行為甚至沒有明確定義。 在這種情況下它應該做什么:

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

但是,它是Applicative 我們需要的類型是[IO a] ,它是2個applicative functor的組合,所以我們可以使用變換器包中的Data.Functor.Compose 這也為免費提供了帶有<|>Alternative實例。 我們將使用Rebindable Syntax為Applicative使用do-notation:

{-# 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

你到目前為止的建議是行不通的。 這是怎么回事:

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"

我還沒有找到一個好的解決方案。

如果您正在尋找基於延續的方法,那么您將不會比LogicT論文中SFKT成功/失敗延續實現簡單得多。

如果msplit太多(並且它是一個非常微妙的野獸),你可以忽略它為這個應用程序。 它的目的是允許公平的連接和分離,如果那些樣本輸出線要按順序打印,這不是您的規范的一部分。 只需關注5.1節中的MonadMonadPlus實現,您就可以了。

更新 :正如Sjoerd Visscher所指出的那樣,這是不對的,因為重新啟動只發生在mplus而不是整個計算。 這比第一次閱讀時出現的問題要棘手得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM