简体   繁体   English

在 haskell 中构建一个不确定的 monad 转换器

[英]Building a nondeterministic monad transformer in haskell

I would like to build a nondeterministic monad transformer in haskell that, I believe, behaves differently from ListT and from the alternative ListT proposed at http://www.haskell.org/haskellwiki/ListT_done_right .我想在 haskell 中构建一个不确定的 monad 转换器,我相信它的行为不同于 ListT 和http://www.haskell.org/haskellwiki/ListT_done_right上提出的替代 ListT。 The first of these associates a monad with a list of items;其中第一个将 monad 与项目列表相关联; the second associates a monad with individual items but has the property that monadic actions in given element influence monadic elements in subsequent slots of the list.第二个将 monad 与单个项目相关联,但具有给定元素中的 monadic 操作影响列表后续槽中的 monadic 元素的属性。 The goal is to build a monad transformer of the form目标是建立一个形式的 monad 转换器

data Amb m a = Cons (m a) (Amb m a) | Empty

so that every element of the list has its own monad associated with it and that successive elements have independent monads.这样列表中的每个元素都有自己的 monad 与之关联,并且连续的元素具有独立的 monad。 At the end of this post I have a little demonstration of the kind of behavior this monad should give.在这篇文章的结尾,我对这个 monad 应该提供的行为进行了一些演示。 If you know how to get some variant of ListT to give this behavior, that would be helpful too.如果您知道如何获得 ListT 的某些变体来提供这种行为,那也会有所帮助。

Below is my attempt.以下是我的尝试。 It is incomplete because the unpack function is undefined.它是不完整的,因为unpack函数未定义。 How can I define it?我该如何定义它? Here's one incomplete attempt at defining it, but it doesn't take care of the case when the monad m contains an Empty Amb list:这是一个不完整的定义它的尝试,但它没有处理 monad m 包含Empty Amb 列表的情况:

unpack :: (Monad m) => m (Amb m a) -> Amb m a                                                                                                                 
unpack m = let first = join $ do (Cons x ys) <- m                                                                                                             
                                 return x                                                                                                                     
               rest =  do (Cons x ys) <- m                                                                                                                    
                          return ys                                                                                                                           
           in Cons first (unpack rest)   

Full (incomplete) code:完整(不完整)代码:

import Prelude hiding  (map, concat)                                                                                                                          
import Control.Monad                                                                                                                                          
import Control.Monad.Trans       

data Amb m a = Cons (m a) (Amb m a) | Empty                                                                                                                   

infixr 4 <:>                                                                                                                                                  
(<:>) = Cons                                                                                                                                                  

map :: Monad m => (a -> b) -> Amb m a -> Amb m b                                                                                                              
map f (Cons m xs) = Cons y (map f xs)                                                                                                                         
    where y = do a <- m                                                                                                                                       
                 return $ f a                                                                                                                                 
map f Empty = Empty                                                                                                                                           

unpack :: m (Amb m a) -> Amb m a                                                                                                                              
unpack m = undefined                                                                                                                                          


concat :: (Monad m) => Amb m (Amb m a) -> Amb m a                                                                                                             
concat (Cons m xs)  = (unpack m) `mplus` (concat xs)                                                                                                          
concat  Empty = Empty                                                                                                                                         

instance Monad m => Monad (Amb m) where                                                                                                                       
    return x = Cons (return x) Empty                                                                                                                          
    xs >>= f = let yss = map f xs                                                                                                                             
               in concat yss                                                                                                                                  

instance Monad m => MonadPlus (Amb m) where                                                                                                                   
    mzero = Empty                                                                                                                                             
    (Cons m xs) `mplus` ys = Cons m (xs `mplus` ys)                                                                                                           
    Empty `mplus` ys = ys                                                                                                                                     

instance MonadTrans Amb where                                                                                                                                 
    lift m = Cons m Empty        

Examples of desired behavior期望行为的例子

Here, the base monad is State Int在这里,基础 monad 是State Int

instance Show a => Show (Amb (State Int) a) where                                                                                                             
    show m = (show .  toList) m                                                                                                                               


toList :: Amb (State Int) a -> [a]                                                                                                                            
toList Empty = []                                                                                                                                             
toList (n `Cons` xs) = (runState n 0 : toList xs)                                                                                                             


x = (list $ incr) >> (incr <:> incr <:> Empty)                                                                                                                
y = (list $ incr) >> (incr <:> (incr >> incr) <:> Empty)                                                                                                      

main = do                                                                                                                                                     
  putStr $ show x -- | should be [2, 2]                                                                                                                       
  putStr $ show y -- | should be [2, 3]   

Thanks.谢谢。

Update: An example of why LogicT doesn't do what I want.更新:为什么 LogicT 不做我想做的事的一个例子。

Here's what LogicT does on the simple example above:以下是 LogicT 在上面的简单示例中所做的事情:

import Control.Monad                                                                                                                                          
import Control.Monad.Logic                                                                                                                                    
import Control.Monad.State                                                                                                                                    

type LogicState = LogicT (State Int)                                                                                                                          


incr :: State Int Int                                                                                                                                         
incr = do i <- get                                                                                                                                            
          put (i + 1)                                                                                                                                         
          i' <- get                                                                                                                                           
          return i'                                                                                                                                           

incr' = lift incr                                                                                                                                             
y =  incr' >> (incr' `mplus` incr')                                                                                                                           

main = do                                                                                                                                                     
  putStrLn $ show (fst $ runState (observeAllT y) 0)   -- | returns [2,3], not [2,2]                                                                                                       

I believe you can just use StateT .我相信你可以只使用StateT For example:例如:

import Control.Monad.State

incr = modify (+1)
sample1 = incr `mplus` incr
sample2 = incr `mplus` (incr >> incr)

monomorphicExecStateT :: StateT Int [] a -> Int -> [Int]
monomorphicExecStateT = execStateT

main = do
    print (monomorphicExecStateT sample1 0) -- [1, 1]
    print (monomorphicExecStateT sample2 0) -- [1, 2]

I don't think this is possible in the general case (and a monad transformer should be able to transform any monad).我认为在一般情况下这是不可能的(并且 monad 转换器应该能够转换任何 monad)。 The unpack option you mention is not possible for monads in general - it corresponds to the operation:您提到的 unpack 选项通常对于 monads 是不可能的 - 它对应于操作:

extract :: (Comonad w) => w a -> a

Which is an operation on a comonad (the mathematical dual of a monad).这是对 comonad 的操作(monad 的数学对偶)。

There are things you can do to 'unpack' it by taking the (m (Amb ma)) and mapping it several times to produce a single (ma) in each case, but this requires you to know in advance (or rather, from outside the monad) how many choices are being created, which you cannot know without some form of extract operation.您可以通过获取 (m (Amb ma)) 并将其多次映射以在每种情况下生成单个 (ma) 来“解压缩”它,但这需要您提前知道(或者更确切地说,从在 monad 之外)正在创建多少选择,如果没有某种形式的提取操作,您无法知道这些选择。

The reason that in the second ListT the tail of the list depends on a monadic action is because we need to perform a monadic action in some cases in order to find out how many choices are being produced (and thus how long the list is).在第二个 ListT 中,列表的尾部取决于单子操作的原因是因为在某些情况下我们需要执行单子操作,以便找出正在生成的选项数量(以及列表的长度)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM