3

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

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 IntMonad 的乘积,但是,当我接下来尝试访问底层状态时:

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价值,但这个解决方案似乎不是很有用,因为这两个状态的初始值必须先验地固定。

问题二。

我真的很希望能够在不同类型的状态下创建一个产品单子,即:

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必须返回相同的类型,这是一个不幸的限制。关于如何解决这个问题的任何想法?

4

2 回答 2

4

它不能按照你想要的方式完成。假设有一种方法可以从左单子中获取当前状态。然后你会有一个类型的功能

getLeft :: Product (State a) (State b) a

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

现在我们可以选择扔掉左边的部分,只运行右边的部分:

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

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

也就是说,里面的两个monadProduct是完全独立的。它们不会相互影响,可以单独运行。因此,我们不能从一个值中取出一个值并在另一个(或两者)中使用它。

于 2013-08-11T19:18:01.387 回答
4

解决方案是将状态单子与您的状态产品一起使用:

m :: State (Int, String) ()

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

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

要了解有关此技术的更多信息,您可以阅读我关于镜头的博客文章,其中包含更多详细信息。

于 2013-08-11T16:38:04.037 回答