Почему я не могу получить увеличенное значение счетчика? - PullRequest
2 голосов
/ 13 июня 2011

Я пытаюсь создать глобальный счетчик, используя монады в Haskell, я хочу получать увеличенное значение каждый раз, когда использую счетчик монад, но я немного застреваю, получая одно и то же значение каждый раз! Список кодов выглядит следующим образом:

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 .... Я знаю, что там должна быть какая-то глупая проблема, вы, ребята, можете указать, как?

1 Ответ

6 голосов
/ 13 июня 2011

Это правильное поведение.Каждый раз, когда вы вызываете 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)
...
...