#haskell #monads
#haskell #монады
Вопрос:
Мне нужен глобальный счетчик, начинающийся с 0, 1, 2, 3, ….. Я вроде понимаю, что этот «нечистый» код должен быть реализован отдельно… Я только начинаю понимать Monad, но понятия не имею, как реализовать этот глобальный счетчик с помощью Monad? Это может быть очень полезным примером для понимания, возможно ли это
Комментарии:
1. Как сказал Анкур, это в основном государственная монада. Реализация экземпляра Monad —
return
и bind(>>=)
— была бы такой же, как State , однако вам потребовалась бы менее общая операция обновления, чем у State monadput
(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 . Это монада общего назначения, которая может использоваться для управления состоянием. В вашем случае счетчик — это просто состояние, которое вы хотите поддерживать.
Ответ №4:
Пока состояние в порядке, вам не нужно проверять счетчик во время вычисления, а просто увеличивать его, поэтому монады записи должно быть достаточно. Смотрите Learn you a Haskell для (не слишком серьезного) введения.
Комментарии:
1. Я не уверен, что использования Writer было бы достаточно — если вы хотите узнать текущее количество во время вычисления, вы не можете получить его из Writer (состояние только для записи), это должна быть монада состояния.
2. @stephen tetley В вопросе не ясно, нужно ли вам значение счетчика во время вычисления. Однако, если вам нужен только конечный результат счетчика,
Writer
«безопаснее» и проще в использовании.