Примечание: ответ camccann лучше моего, но мой использует немного другой подход и дает пример того, как оценивать монаду состояния, поэтому я оставляю его здесь для справки.
Мы можем начать пытаться выяснить проблему, удалив сигнатуру типа для getAverage
и аргумент (c
), который не отображается в функции:
getAverage s=get >>= \s0 -> let (x,s1) =media s s0
in put s1 >> return x
Это все еще не компилируется, потому что мы пытаемся put
что-то, что не имеет правильного типа: s1
это Double
, а не MyState
. Это легко исправить:
getAverage s=get >>= \s0 -> let s1@(x,_) =media s s0
in put s1 >> return x
Мы также можем оставить шаблон let
без изменений и просто сказать put (x,s1)
: вместо этого я делаю это так, чтобы наш s1
имел тот же тип, что и s0
.
Это компилируется, так что теперь мы можем исправить сигнатуру типа. Если мы запрашиваем тип GHCi, он возвращает следующее:
getAverage :: (Fractional t, MonadState (t, t) m) => t -> m t
Double
- это экземпляр Fractional
, а State MyState
- это экземпляр MonadState (Double, Double)
, поэтому мы можем использовать что-то очень похожее на ваш исходный тип для getAverage
:
getAverage :: Double -> State MyState Double
Эта функция на самом деле не «получает» среднее значение: она обновляет его после добавления нового значения, поэтому давайте переименуем его соответствующим образом:
updateAverage :: Double -> State MyState Double
updateAverage s=get >>= \s0 -> let s1@(x,_) =media s s0
in put s1 >> return x
Теперь мы можем определить функцию getAverages
, которая принимает список Double
с, пропускает их через updateAverage
и возвращает список промежуточных средних на каждом шаге:
getAverages :: [Double] -> [Double]
getAverages ss = evalState (mapM updateAverage ss) (0, 0)
Это делает то, что мы ожидали:
*Main> getAverages [1..10]
[1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5]
Обратите внимание: чтобы сделать что-нибудь полезное с монадой State
, вам всегда нужно будет использовать evalState
(или тесно связанные runState
и execState
).