Как реализовать глобальный счетчик с помощью Monad?

#haskell #monads

#haskell #монады

Вопрос:

Мне нужен глобальный счетчик, начинающийся с 0, 1, 2, 3, ….. Я вроде понимаю, что этот «нечистый» код должен быть реализован отдельно… Я только начинаю понимать Monad, но понятия не имею, как реализовать этот глобальный счетчик с помощью Monad? Это может быть очень полезным примером для понимания, возможно ли это

Комментарии:

1. Как сказал Анкур, это в основном государственная монада. Реализация экземпляра Monad — return и bind (>>=) — была бы такой же, как State , однако вам потребовалась бы менее общая операция обновления, чем у State monad put ( put может изменять состояние произвольными способами). Я бы предположил, что хорошим интерфейсом был бы next который увеличивает количество и возвращает новое количество. Сохранение get операции, которая просто запрашивает счетчик без его обновления, тоже было бы неплохо.

2. Проверьте монаду поставки . Чтобы получить счетчик с его помощью, просто используйте [0..] в качестве источника.

3. @hammar Ваш комментарий должен быть ответом, и он должен быть принят.

Ответ №1:

State monad выдает вам состояние, но только внутри монады. Он не является постоянным при повторных вызовах функции.

Если вы хотите действительно глобальное, изменяемое состояние, вы можете захотеть сделать что-то вроде:

   import Data.IORef

  type Counter = Int -> IO Int

  makeCounter :: IO Counter
  makeCounter = do
      r <- newIORef 0
      return (i -> do modifyIORef r ( i)
                       readIORef r)

  testCounter :: Counter -> IO ()
  testCounter counter = do
      b <- counter 1
      c <- counter 1
      d <- counter 1
      print [b,c,d]

  main = do
      counter <- makeCounter
      testCounter counter
      testCounter counter
  

Здесь ‘makeCounter’ создает глобальную изменяемую переменную, которая сохраняет свое состояние при вызовах и разрушает чистоту. Например, в функции main два идентичных вызова ‘testCounter’ дают разные результаты.

 > main
[1,2,3]
[4,5,6]
  

Комментарии:

1.Почему вы сделали подпись Counter Int -> IO Int вместо просто IO Int ?

Ответ №2:

Вы можете реализовать это с помощью State монады, которая хранит текущее значение вашего счетчика в качестве состояния. Затем вы можете использовать get для получения текущего значения счетчика и modify ( 1) для его увеличения.

Одним из полезных вариантов этого является Supply monad, где вы можете использовать произвольную последовательность в качестве своего «счетчика», поэтому, чтобы иметь простой счетчик, начинающийся с нуля, просто используйте [0..] в качестве источника питания.

Ответ №3:

Что вы можете изучить, так это state monad . Это монада общего назначения, которая может использоваться для управления состоянием. В вашем случае счетчик — это просто состояние, которое вы хотите поддерживать.

http://www.haskell.org/haskellwiki/State_Monad

Ответ №4:

Пока состояние в порядке, вам не нужно проверять счетчик во время вычисления, а просто увеличивать его, поэтому монады записи должно быть достаточно. Смотрите Learn you a Haskell для (не слишком серьезного) введения.

Комментарии:

1. Я не уверен, что использования Writer было бы достаточно — если вы хотите узнать текущее количество во время вычисления, вы не можете получить его из Writer (состояние только для записи), это должна быть монада состояния.

2. @stephen tetley В вопросе не ясно, нужно ли вам значение счетчика во время вычисления. Однако, если вам нужен только конечный результат счетчика, Writer «безопаснее» и проще в использовании.