Это правильное поведение.Каждый раз, когда вы вызываете run tickC
, функция run
оценивает работу со счетчиком, установленным на ноль.По сути, каждый раз, когда вы вызываете run
, вы получаете различную «копию» счетчика, инициализируемую нулем.
Если вы хотите, чтобы счетчик увеличивался каждый раз, вам придется выполнять все операции в одном вызоведо run
.Например,
tickMany = do
x <- tickC
y <- tickC
z <- tickC
return [x, y, z]
> run tickMany
[1, 2, 3]
Это верно для всех монад, включая IO
(игнорируя «небезопасные» операции).Поскольку единственный способ запустить операцию IO
- через main
, монада IO
пронизывается через каждую функцию, которая ее использует.
Поэтому, если вам нужен глобальный счетчик, этот счетчик должен бытьуправляется монадой, используемой глобально (т. е. каждой функцией, которой необходим доступ к ней).Вы можете использовать свою монаду Counter
глобально или поместить счетчик в монаду IO
.Кажется, что принятая практика проектирования - помещать подобное состояние в вашу собственную монаду, но, конечно, это зависит от приложения, и IO
тоже подойдет.
Вы также можете захотеть взглянуть на Control.Monad.State
, что позволит вам определить вашу монаду с гораздо меньшим количеством набрав.Что-то вроде:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.State
newtype Counter a = Counter (State Int a) deriving (Monad)
...