Supposing I’ve got a `Ref<ForIO, MyClass>` a...
# arrow
k
Supposing I’ve got a
Ref<ForIO, MyClass>
and a
StateT<ForIO, MyClass, Unit>
, what is the best way to make these two work together (using the
StateT
to update the value wrapped in
Ref
?) I have to unpack the
Ref
, pass it into the
StateT
, and use the result to update the
Ref
. I’m not sure how to do that without
Copy code
myRef.update { oldState ->
  val (newState, _) = myStateT.runM(IO.monad(), oldState).unsafeRunSync()
  newState
}
but I know the
unsafeRunSync
is the stinkiest code smell there ever was.
StateT can’t provide a safe concurrent instance, it can only go up to Async, just a heads up that is full of limitations
k
Darn. I was asking questions about this a month ago and got pointed to using Ref with State to ensure thread safety (it wasn’t an Arrow dev who suggested it, though; it was a name I didn’t recognize). This is an Android app and there are many user interactions that trigger a state change. I can do all this using LiveData, but I was trying to write to minimize using Android stuff for testing purposes.
s
What are you using
State
for?
Using
Ref
will protect you from concurrent reading and writing like
AtomicReference
does. It’s however not safe to perform effects while
updating
a
Ref
which is why you require
unsafeRunSync
there
Can you share a larger snippet I could take a look at?
k
I have a ViewModel that holds a view state that originally was exposed via LiveData that my view observes and reacts to. User interactions on the view call a function in ViewModel that updates state (synchronously for some things, IO stuff for things like CRUD) As an excuse to learn more about Arrow, I was trying to swap in Arrow things. I could be trying to use State as an antipattern by mistake. So I’m using State for updating my ViewModel’s viewstate. With the new experiments using FP/Arrow stuff, I haven’t fully thought through the best way to expose the state changes to the view. But setting that aside for the time being (which also hopefully explains why everything is all over the map with StateT, ReaderT, Ref as I think through things):
Copy code
data class CarDetailViewState(val car: Option<CarEntity> = None)

class CarDetailViewModel(private val carDao: CarDao): ViewModel() {
    private val viewStateRef = Ref(IO.monadDefer(), CarDetailViewState()).fix()
    val carDetailFetcher = getCarDetail(carDao)

    fun setCar(id: Int) = IO.fx {
        val car = carDetailFetcher.run(IO.monad()).bind()
        val (newState, _) = setCar(car).runM(IO.monad(), state).bind()
        viewStateRef.bind().updateAndGet {
            newState
        }

        set(car).runM(IO.monad()).flatMap {

        }
    }

    fun setId(id: Int): IO<CarDetailViewState> {
        return getCarDetail(carDao).run(id).fix().flatMap { entity ->
            viewStateRef.flatMap { ref ->
                ref.updateAndGet { oldState ->
                    oldState.copy(car= Some(entity))
                }
            }
        }
    }
}

fun setCar(car: CarEntity) = State<CarDetailViewState, Unit> {
    it.copy(car=Some(car)) toT Unit
}

fun set(car: CarEntity) = StateT<ForIO, Ref<ForIO, CarDetailViewState>, Unit>(IO.monad()) { ref ->
    IO.fx {
        ref.getAndUpdate {
            it.copy(car=Some(car))
        } toT Unit
    }
}


fun getCarDetail(carDao: CarDao) = ReaderT<ForIO, Int, CarEntity> { carId ->
    IO.effect { Option.fromNullable(carDao.get(carId)) }
}
Couple drafts of same approach still in there, trying to work through concepts.
set(car: CarEntity)
has type inference failures right now because it’s incomplete.
CarDetailViewModel::setCar
currently is referencing non-existent
state
variable, second half is totally unfinished and based off at a recommendation Paco provided here so far
So it’s a total mess and I wouldn’t blame you not bothering.
s
I think there is some misunderstanding about
Ref
and
State
here. @Jorge Castillo has a solution for Android using ViewModels, and we’re going to add them to the Arrow documentation very soon. We’re currently in the process of releasing.
k
Oh, most definitely! There’s also some backstory missing. My initial attempt weeks ago didn’t use
Ref
at all. But I ran into concurrency issues because userclicks, typing, etc. trigger state changes and I can’t serialize them. Someone here (but not an Arrow maintainer or name I recognize) was very enthusiastic about me using
Ref
for this reason. This is my first attempt at using Ref, so I’m 100% sure I don’t grok it 🙂
And I know that State is used so you can enforce mutability with state, so the way ViewModel and Android works, you can’t really do that. So It’s definitely me shoving a square peg into a round hole.
s
Ref
is like
AtomicRef
, so it doesn’t allow you to run
IO
inside updates.
update
and
modify
can potentially run multiple times in case the `update`/`modify` wasn’t valid. (It uses
compareAndSet
in a loop until it returns
true
). So you’re risking running the effect multiple times.
It sounds like you need something like
Queue
(or
MVar
(functional mutable var, or single value queue). That would allow you to send multiple events and receive multiple events.
All operators are non-blocking, and it offers several back pressure strategies as well.
I’ll try to make some time later to take a better look at your snippet!
k
OK. I actually thought
Ref
was an Arrow name for the concept of
MVar
, which I’d seen around on Haskell or Scalaz/Cats forums. If you don’t have time to look at the snippet, don’t worry about it. It’s not mission critical; it’s me trying to learn, and I hate to have people spend time helping me with stuff that doesn’t even matter in the grand scheme of things, especially with everything going haywire in the world at the moment!