Let’s say I have this OOP code ```data class MySta...
# functional
j
Let’s say I have this OOP code
Copy code
data class MyState(val a: String, val b: Int)
class MyStateHolder() {
    private val _state = MutableStateFlow(MyState(a = "", b = 0))
    val state: StateFlow<MyState> = _state.asStateFlow()

    fun updateState(a: String? = null, b: Int? = null) {
        _state.update {
            it.copy(a = a ?: it.a, b = b ?= it.b)
        }
    }
}
How would you convert this to functional programming style? Does one uses a top level variable like this?
Copy code
private val _myState = MutableStateFlow(MyState())
internal val myState = _myState.asStateFlow()

fun updateMyState(
    currentState: MyState,
    a: String? = null,
    b: Int? = null,
) = currentState.copy(
    a = a ?: currentState.a,
    b = b ?: currentState.b,
)

fun someFunction() {
    ...
    updateMyState(stateFlow.value, "new value", 1)
    ...
}
y
I would get rid of
MyStateHolder
altogether and just pass around a
MutableStateFlow<MyState>
, maybe wrapped in a context if you're feeling fancy. Then, I'd use arrow-optics and specifically its Compose compatibility module so that I can do:
Copy code
myState.updateCopy {
  a set foo
  b set bar
}
If I were to keep
MyStateHolder
, it'd be solely because I can pass it around as a context, and I'd just make it a dumb wrapper around the flow:
Copy code
class MyStateHolder {
  val myState = MutableStateFlow(MyState(a = "", b = 0))
}
context(msh: MyStateHolder)
val myState get() = msh.myState
and again use optics for updating.
u
@Youssef Shoaib [MOD], how would you approach getting rid of mutable state all together? Something that would be handled by a state monad in pure FP languages?
j
I would get rid of
MyStateHolder
altogether and just pass around a
MutableStateFlow<MyState>
Right, that’s what I’ve been trying to do yesterday. Thanks for the advice!
y
That's kinda what the MutableStateFlow is for. It's a sufficiently good way to handle state updates. Absolute purity is not the priority in Kotlin. We look for what's idiomatic, and within that strive for enough referential transparency and such so that we may reason about our code better. For these purposes, MutableStateFlow help us reason about code, and hence we use it.
j
I totally agree, I’m not looking for fp purity or being mathematically correct and whatnot. I’m trying to get into better practices/paradigm to avoid problems further down the road. I’m also considering a shift in the way I create state objects themselves. I’m woking with the some hardware connected to an app. The state is simply this :
Copy code
data class HardwareState(
    override val connectionState: ConnectionState,
    override val busy: Boolean,
)
Then I have an interface and it’s implementation defining what can be done with the hardware
Copy code
interface Hardware {
    val state: StateFlow<HardwareState>
    fun login()
    fun doWork()
}

class HardwareType1 : Hardware {
    private val _state = MutableStateFlow<HardwareState(...)>
    val state = _state.asStateFlow()

    override fun login() {
        // do some stuff
        state.update { it.copy { connectionState = CONNECTED } }
    }

    override fun doWork() {
        if (state.connectionState == CONNECTED) {
            // do the actual work
        }
    }
}
I’ve seen and heard for a while people saying the object used should represent the state itself. Would it make sense in my case to have
DisconnectedHardware
and
ConnectedHardware
instead. Then the login function can take a
DisconnectedHardware
and doWork a
ConnectedHardware
. Is that a good idea? Is there a name for this approach over the conventional oop style?
1
y
That's exactly what I'd do! #C0474L1Q8DR is hard, but it's an example of algebraic data types. It's also kind of in a similar pattern to making illegal states unrepresentable.
j
Thanks man! I feel my brain is bobbling up with all this stuff I’m finally trying out hehe