问题描述
|
我正在尝试在Haskell中使用monad进行全局计数,我想每次使用monad计数器时都获得递增的值,但是每次都获得相同的值实在令人困惑!
代码列表如下:
module CounterMonad where
data Counter a = C (Int -> (Int,a))
--reset the counter
new :: Counter ()
new = C $ \\_ -> (0,())
-- increment the counter:
--inc :: Counter Int
--inc = C $ \\n -> (n+1,n)
inc = get >>= \\s -> (put (s+1))
-- returning the current value of the counter
get :: Counter Int
get = C $ \\n -> (n,n)
--
put x = C $ \\n -> (x,x)
--return is nop,>>= is sequential exectuion
instance Monad Counter where
return r = C $ \\n -> (n,r)
(>>=) (C f) g = C $ \\n0 -> let (n1,r1) = f n0
C g\' = g r1
in g\' n1
run :: Counter a -> a
run (C f) = snd (f 0)
tickC = do
inc
c <- get
return c
当我尝试以run tickC
执行时,它总是返回1。
我想要的是每次我run tickC
,它都会返回递增的值,例如1,2、3、4...。
我知道那里肯定有一些愚蠢的问题,你们能指出如何吗?
解决方法
这是正确的行为。每次调用
run tickC
时,run
函数都会将计数器设置为零来评估操作。本质上,每次调用“ 4”时,您将获得一个不同的\“副本\”计数器,该计数器初始化为零。
如果希望每次使计数器递增,则必须在对run
的同一调用中执行所有操作。例如,
tickMany = do
x <- tickC
y <- tickC
z <- tickC
return [x,y,z]
> run tickMany
[1,2,3]
这对所有单子都是如此,包括ѭ8igno(忽略“ unsafe”操作)。因为执行IO
操作的唯一方法是通过main
,所以IO
monad会穿过使用它的每个函数。
因此,如果您要使用全局计数器,则该计数器必须由全局使用的monad管理(即,每个需要访问它的功能)。您可以全局使用Counter
monad,也可以将计数器放在the8ѭmonad中。将这样的状态放在自己的monad中似乎是可接受的设计实践,但是当然,这取决于应用程序,IO
也可以。
您可能还希望查看Control.Monad.State
,它使您可以使用更少的键入量来定义单子。就像是:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.State
newtype Counter a = Counter (State Int a) deriving (Monad)
...