Combining State and StateT

Hello everyone,

I’m pretty new to functional languages, and I’m taking the time to learn PureScript. And what better way to learn a completely new language and paradigm than starting a new project you’re not even sure you can handle on a technology you’re comfortable with, right? :smiley:

Anyways, I have some code where I need to pass along some state information, but some of my functions are “purer”, while a couple of them are very effectful. So I share the state information between them using both State and StateT Effect. However, I can’t mix them or else I’ll obviously get a type mismatch error.

I tried looking up online and the Haskell community called it a “hoist” operation and suggested this:

state . runState

But I couldn’t find a “state” function in PureScript to make a new StateT.

So I ask:

  1. Does needing to do this indicate a problem in my design?
  2. If not, what’s the better way to “hoist” (whatever that is :smiley: ) from State to StateT in PureScript?

Does needing to do this indicate a problem in my design?

Not necessarily, but it’s a bit hard to tell without more details about what you’re trying to do.

If not, what’s the better way to “hoist” (whatever that is :smiley: ) from State to StateT in PureScript?

My understanding of the term “hoist” is that it is a function which allows you to swap out the underlying monad in a monad transformer. So in this case you’re going from StateT s Identity to StateT s Effect (note that State s is a synonym for StateT s Identity).

I think the state you are looking for is the one defined in the MonadState class: https://pursuit.purescript.org/packages/purescript-transformers/4.2.0/docs/Control.Monad.State.Class#t:MonadState

Another option, which might be simpler, is to write your functions in terms of class constraints rather than concrete monad transformer stacks. So for example, if you have a “purer” stateful operation whose type is, say,

op1 :: State MyState Unit

you could rewrite it as

op1 :: MonadState MyState m => m Unit

and then if you have an effectful one such as

op2 :: StateT MyState Effect Unit

then you could rewrite it as

op2 :: MonadState MyState m => MonadEffect m => m Unit

and then you should be able to combine op1 and op2 quite easily:

op3 :: MonadState MyState m => MonadEffect m => m Unit
op3 = do
  op1
  op2

since the compiler should be happy provided that you have all of the constraints from op1 and op2 present.

At some point you will have to pick a concrete monad to run those computations in, but as long as you pick one which can satisfy all of the requested constraints, then you’ll be in business. For example, you could choose State MyState in op1, and StateT MyState Effect for op2.

This technique is called “mtl style” and the Haskell community has written quite a lot about it, so that’s a good search query to use for more information.

4 Likes

Your answer is better than what I was even hoping for.
Thank you so much!

Welcome! Glad to hear it was helpful :slight_smile: