我正在从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)
问题我
我构建了一个简单的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
a
和b
获取基础值,但此解决方案似乎不太有用,因为这两个状态的初始值必须先验地修复。
问题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
必须返回相同的类型,这是一个不幸的限制。有关如何解决这个问题的想法吗?
答案 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是完全独立的。它们不会相互影响,可以单独运行。因此,我们不能从一个值中取出一个值,而在另一个值(或两者中)中使用它。