为什么我不能获得增加的计数器价值?

2 monads haskell

我想在Haskell中使用monads做一个全局计数器,我希望每次使用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 Code Online (Sandbox Code Playgroud)

当我尝试执行时run tickC,它总是返回1.我想要的是每次我run tickC,它返回增加的值,如1,,2,3,4 ....我知道那里肯定有一些愚蠢的问题,可以你们指出怎么样?

Die*_*Epp 6

这是正确的行为.每次调用时run tickC,run函数都会在计数器设置为零的情况下评估操作.基本上,每次打电话给run你时,都会得到一个初始化为零的计数器的不同"副本".

如果您希望每次都有计数器增量,则必须在同一次调用中执行所有操作run.例如,

tickMany = do
    x <- tickC
    y <- tickC
    z <- tickC
    return [x, y, z]

> run tickMany
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

所有monad都是如此,包括IO(忽略"不安全"操作).由于运行IO操作的唯一方法是通过main,IOmonad会遍历使用它的每个函数.

因此,如果您想要一个全局计数器,那么该计数器必须由全局使用的monad(即每个需要访问它的函数)来管理.您可以Counter全局使用monad或将计数器放入IOmonad中.将这样的状态放在你自己的monad中似乎是公认的设计实践,但当然,这取决于应用程序并且IO也很好.

你可能也希望看一下Control.Monad.State,这样你就可以用更少的打字来定义你的monad.就像是:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.State
newtype Counter a = Counter (State Int a) deriving (Monad)
...
Run Code Online (Sandbox Code Playgroud)