你是如何从一个属于产品monad的状态monad中获得当前状态的?

时间:2013-08-11 15:51:07

标签: haskell monads state-monad

我正在从Control.Monad.Product包构建一些产品monad。作为参考,产品monad类型是:

newtype Product g h a = Product { runProduct :: (g a, h a) } 

它的monad实例是:

instance (Monad g, Monad h) => Monad (Product g h) where
    return a = Product (return a, return a)
    Product (g, h) >>= k = Product (g >>= fst . runProduct . k, h >>= snd . runProduct . k)
    Product (ga, ha) >> Product (gb, hb) = Product (ga >> gb, ha >> hb)

来源:http://hackage.haskell.org/packages/archive/monad-products/3.0.1/doc/html/src/Control-Monad-Product.html

问题我

我构建了一个简单的monad,它是两个State Int Monads的产品,然而,当我尝试访问下一个基础状态时:

ss :: Product (State Int) (State Int) Int
ss = do 
  let (a,b) = unp $ P.Product (get,get) :: (State Int Int,State Int Int)  
  return 404

您看到get只创建了另一个State Int Int,我不确定如何实际获取基础状态的值,我该怎么做?请注意,我可能会runState ab获取基础值,但此解决方案似乎不太有用,因为这两个状态的初始值必须先验地修复。

问题II。

我真的希望能够在不同类型的状态下创建产品monad,即:

ss2 :: Product (State Int) (State String) ()
ss2 = do 
  let (a,b) = unp $ P.Product (get,get) :: (State Int Int,State Int String)  
  return ()

但我得到这种类型的错误:

 Couldn't match expected type `String' with actual type `Int'
    Expected type: (State Int Int, State String String)
      Actual type: (StateT Int Identity Int,
                    StateT String Identity Int)

因为我认为两个get必须返回相同的类型,这是一个不幸的限制。有关如何解决这个问题的想法吗?

2 个答案:

答案 0 :(得分:4)

解决方案是使用状态为monad的州monad:

m :: State (Int, String) ()

然后,您可以使用zoom库中的_1_2 / lens运行与产品的两个字段之一进行交互的操作,如下所示:

m = do
    n <- zoom _1 get
    zoom _2 $ put (show n)

要了解有关此技术的更多信息,您可以阅读更详细的blog post on lenses

答案 1 :(得分:4)

不能按照你想要的方式完成。假设有一种方法可以将当前状态从左侧monad中取出。然后你有一个类型

的功能
getLeft :: Product (State a) (State b) a

(State a a, State b a)同构。

现在我们可以选择丢弃左侧部分并仅运行右侧部分:

evalState (snd (runProduct getLeft)) () :: a

所以我们得到一个任意类型a的居民。

换句话说,Product内的两个monad是完全独立的。它们不会相互影响,可以单独运行。因此,我们不能从一个值中取出一个值,而在另一个值(或两者中)中使用它。