:alphabet-white-question: API philosophy / design ...
# compose
m
alphabet white question API philosophy / design question. Compose isn't an OOP toolkit, so composable functions by convention don't return anything. To change things you trigger re-composition and pass in new parameters. This is fine most of the time but there are places where it gets awkward, and an OOP API would be a lot simpler. The key one I'm thinking of here is focus, but there are probably other cases because I'm not a Compose full timer by any means. In an OOP toolkit to instantiate a form and then focus the first text edit is quite straightforward. You instantiate a UI from code or XML, pick out the control you want and call its focus() method. In Compose controls don't have any identity, so you can't do this. Instead you have to create a "focus requester", then pass it in to the widget you want to focus, then launch an effect, and finally you can use the focus requester to request the focus. It's a lot more code, more concepts, doesn't really generalize well and in general is a bit awkward. Although you can make a wrapper composable that does this specifically for a textfield, it's hard to see how to make it generic. An obvious way to fix this would be if
TextField
and any other focusable composable returned an object (interface) instead of unit that exposes methods for inherently imperative actions like
focus()
. If everything you want to do can be done by changing params, you ignore the result. Otherwise you can just assign it to a value and then use it from within a side effect. There'd be less code required and it'd be way clearer from auto-complete what you can do. To compose more functionality with it, you'd use Kotlin delegation to wrap the returned control object and add things to it. Is there some important reason this wouldn't work well?
k
Trying to graft an imperative approach onto a functional toolkit is probably an indicator of not “fully buying” into the functional world. Having a composable function return something that allows you to control its subsequent state is very much against the whole mental model of how things work.
m
UI is frequently imperative and stateful, no way around that. Hence why things like FocusRequester exist, there's not much functional about that API.
Likewise for
remember {}
k
Right, except that composables should not be owning their state. That leads to fracturing and duplicating pieces of state all over the codebase, and having to reconcile it across all those fractures.
m
A focus requestor isn't state though, so it's just an attempt to jam a square peg into a round hole (and there are other cases where the FP model doesn't really fit). Focus is really state that belongs to the window or screen, but Compose doesn't model it that way because it'd require drilling focus through every single composable that could accept it. Tedious.
c
I think it’s helpful to approach this from the perspective of whatever real problem you are trying to solve. What brought you to ask this question in the first place? What are you working on?
m
This came up in a UI with a bunch of forms. The code required was awkward compared to what it'd have been with an OOP toolkit. There is perhaps a way to combine the strengths of both.
c
The code required was awkward
I take this to mean there’s more boilerplate to a basic form? “awkward” may imply a few different things
I do think there’s a tradeoff there when adopting a declarative UI framework. Some things that were trivial in an imperative context (like focus) become awkward, yes.
m
More boilerplate, more things to learn, more steps to get wrong etc.
d
More boilerplate
Boilerplate is what creates flexibility. This can decrease as you build out the core components of your systems UI over time.
more things to learn
More things to learn is a natural by product of doing things in a new fashion. Eventually that too will feel less cumbersome as you have established a firm foundation of knowledge and understanding. New things will be but a small portion to add to your already vast amount of knowledge over time and will come with ease.
more steps to get wrong etc.
This is really unavoidable. Learning any new/non-trivial skill is this way. I think you are just going through the growing pains of “new” and if you persist with diligence they will subside.
Also, it’s too late in the day to want an OOP Compose system. That is called Dart + Flutter, and it’s not really that great compared to Kotlin + Compose.