Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composing Stateful functions in Haskell

Tags:

haskell

state

What is the simplest Haskell library that allows composition of stateful functions?

We can use the State monad to compute a stock's exponentially-weighted moving average as follows:

import Control.Monad.State.Lazy
import Data.Functor.Identity

type StockPrice = Double
type EWMAState = Double
type EWMAResult = Double

computeEWMA :: Double -> StockPrice -> State EWMAState EWMAResult
computeEWMA α price = do oldEWMA <- get
                         let newEWMA = α * oldEWMA + (1.0 - α) * price
                         put newEWMA
                         return newEWMA

However, it's complicated to write a function that calls other stateful functions. For example, to find all data points where the stock's short-term average crosses its long-term average, we could write:

computeShortTermEWMA = computeEWMA 0.2
computeLongTermEWMA  = computeEWMA 0.8

type CrossingState = Bool
type GoldenCrossState = (CrossingState, EWMAState, EWMAState)
checkIfGoldenCross :: StockPrice -> State GoldenCrossState String
checkIfGoldenCross price = do (oldCrossingState, oldShortState, oldLongState) <- get
                              let (shortEWMA, newShortState) = runState (computeShortTermEWMA price) oldShortState
                              let (longEWMA, newLongState) = runState (computeLongTermEWMA price) oldLongState
                              let newCrossingState = (shortEWMA < longEWMA)
                              put (newCrossingState, newShortState, newLongState)
                              return (if newCrossingState == oldCrossingState then
                                    "no cross"
                                else
                                    "golden cross!")

Since checkIfGoldenCross calls computeShortTermEWMA and computeLongTermEWMA, we must manually wrap/unwrap their states.

Is there a more elegant way?

like image 602
tba Avatar asked Oct 22 '25 04:10

tba


1 Answers

If I understood your code correctly, you don't share state between the call to computeShortTermEWMA and computeLongTermEWMA. They're just two entirely independent functions which happen to use state internally themselves. In this case, the elegant thing to do would be to encapsulate runState in the definitions of computeShortTermEWMA and computeLongTermEWMA, since they're separate self-contained entities:

computeShortTermEWMA start price = runState (computeEWMA 0.2 price) start

All this does is make the call site a bit neater though; I just moved the runState into the definition. This marks the state a local implementation detail of computing the EWMA, which is what it really is. This is underscored by the way GoldenCrossState is a different type from EWMAState.

In other words, you're not really composing stateful functions; rather, you're composing functions that happen to use state inside. You can just hide that detail.

More generally, I don't really see what you're using the state for at all. I suppose you would use it to iterate through the stock price, maintaining the EWMA. However, I don't think this is necessarily the best way to do it. Instead, I would consider writing your EWMA function over a list of stock prices, using something like a scan. This should make your other analysis functions easier to implement, since they'll just be list functions as well. (In the future, if you need to deal with IO, you can always switch over to something like Pipes which presents an interface really similar to lists.)

like image 54
Tikhon Jelvis Avatar answered Oct 24 '25 02:10

Tikhon Jelvis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!