How to handle private state in functional programming?

Viewed 490

I have a feature in my application which has some private state information and some public state to share. How can I get rid of the mutable private state variable? How do I get the private state into the chain?

I just recently learned about functional programming and wanted to transform this feature to a more fp-like approach.

This is my approach so far as a simple example.

sealed class PublicState {
    data class Data(val a:Int, val b:Int):PublicState()
    object Pending : PublicState()
}

data class PrivateState(val a:Int, val b:Int, val x:Int)

sealed class Action {
    data class InputC(val c:Int):Action()
    data class InputD(val d:Int):Action()
}

sealed class Update {
    data class A(val a:Int):Update()
    data class B(val b:Int):Update()
    object Working : Update()
}

class Feature {

    val actions = PublishSubject.create<Action>()
    val state = BehaviorSubject.create<PublicState>()

    private var privateState = PrivateState(0,0,1)

    init {
        val startState = privateState.toPublicState()
        actions.flatMap {action ->
            when (action) {
                is Action.InputC -> handleC(action)
                is Action.InputD -> handleD(action)
            }
        }.scan(startState, ::reduce)
            .subscribe(state)
    }

    fun reduce(previousState:PublicState, update: Update):PublicState {

        // can't use previousState because Pending has not all information
        // I don't want to add the information to pending because state is undefined while pending
        return when (update) {

            is Update.A -> privateState.copy(a = update.a).toPublicState()
            is Update.B -> privateState.copy(b = update.b).toPublicState()
            Update.Working -> PublicState.Pending
        }
    }

    fun doAction(action: Action) {
        actions.onNext(action)
    }

    private fun handleC(action:Action.InputC):Observable<Update> {
        return Observable.fromCallable {
            // time consuming work which uses x
            val result = privateState.x + privateState.a + action.c
            Update.A(result) as Update
        }.startWith(Update.Working)
    }

    private fun handleD(action:Action.InputD):Observable<Update> {
        return Observable.fromCallable {
            // time consuming work which uses x
            val result = privateState.x + privateState.b + action.d
            Update.B(result) as Update
        }.startWith(Update.Working)
    }

}

private fun PrivateState.toPublicState(): PublicState {
    return PublicState.Data(a, b)
}

In reality there are a lot more state variables than a, b and x. But if I want them in the chain, I have a gigantic State variable and all of it gets exposed. It feels easier with the mutable variable. Do you have any suggestion how to solve this? I am also open for other patterns, if you think this is a wrong approach. My goal is to keep some private state and expose just the PublicState.

1 Answers

FP does not deal with private states. Why would you care about keeping something private? Because someone else, from an outer world, could intentionally or not mutate that one and bring entire object into disrepair, right? But there are no mutations in the FP. So you're safe.

Thus your quesiton reduces to the "how to handle state". Well, let me know if you want me to answer.

Related