If I have a `StateT<ForIO, X, Y>` used to he...
# arrow
k
If I have a
StateT<ForIO, X, Y>
used to help manage state in my app, what is the benefit over a reader? when you
.runM(IO.monad(), currentState)
it yields `(newState, someVal)`wrapped in
IO
. I’ve been managing state in my Android app with a
private var
data class in my ViewModel representing a state object. For
Id
operations, I can just use
State<MyState, Unit>
to get back
(newState, _)
and then
myState = newState
. No problem. But if I’m working with
StateT<ForIO, MyState, Unit>
, it seems there could be race conditions doing it this way if two different state transformations are running in parallel since they’re both
IO
. So I can’t just
.unsafeRunAsync { … state = newState }
Instead, I can have these be
StateT<ForIO, MyState, SomePartialStateChange>
and then take the partial state change and use that to update the viewmodel’s state outside the state transformation rather than reassigning the viewmodel’s state to the transformation’s returned
MyState
. But at that point, why not just use
ReaderT<ForIO, MyState, SomePartialStateChange>
? What’s the use case for
StateT
if not for this example of
IO
-wrapped state modification?
Or, maybe a more immediate question: why use
StateT<ForIO,Any,Any>
at all unless you know you’ll never be evaluating these in parallel?
p
I'm okay with IO-wrapped state modification, the mtl helpers are too cumbersome before 1.0
reader doesn't allow you to modify the environment, state does
b
TBH when I saw state & concurrency I immediately went to
Ref
💯 1
Then when I saw "view state" I thought "I really need to read the comonadic UI papers I have accumulated" 😄
p
ye better than io
hahaha
k
well then imagine
data class MyState(val id: Int, loaded: Boolean)
if you have
Copy code
fun setId(id: Int) = StateT<ForIO, MyState, Unit> { ... iState.copy(id=id) toT Unit }

fun setLoaded(t: Boolean)= StateT<ForIO, MyState, Unit> { ... iState.copy(loaded=t) toT Unit}
and then when you
.unsafeRunAsync
to get the data out of
IO
and
Copy code
{ (newState, _) ->
  myCanonicalState = newState
}
then you have a race condition if
setId
and
setLoaded
can be run concurrently because one will overwrite the other’s changes.
And as for
Reader
not letting you modify the context, can’t you have
Copy code
data class Context(var id: Int)
and then inside your reader,
Copy code
{ context ->  context.id=5 }
and then anyone making reference to that context will have the updated context? I’ve seen this recommended over StateT on the FP Complete blog (guy responsible for Haskell’s Yesod)
(haven’t tried it with Arrow yet)
p
with var for sure
it's thread-unsafe tho
what State does is apply those modify operations with flatMap, so sequence is assured
IO + Atomic should get you 90% there
👍 1
k
The issue I’m running into is that my StateT functions can be triggered by user interactions, so there is no single point where I’m always flatmapping the operations. Instead, the onclick handler calls one, the callback from a DB operation calls another, etc.
b
Ref<ForIO, State<MyState, Unit>>
?
is specifically for atomic access to concurrent resources for that use case
👍 1
k
I will have to look into Ref and Atomic. I’m not familiar with either. I see Ref is a class in Arrow. @pakoito, is Atomic something separate or are you using it generically to refer to atomic operations like Ref appears to ensure?
p
AtomicReference in general
k
OK thank you
p
Ref is actually a wrapper over it, so very little gains with the IO approach
k
Cool beans. I just figured out how to use monad transformers with
fx
to avoid a bunch of nested stuff. Soon will learn about Ref/AtomicReference. Thanks