Kotlin lambdas can have a receiver in their scope....
# feed
e
Kotlin lambdas can have a receiver in their scope. They are great to make your code concise, but keeping code readable takes some practice. https://medium.com/@elizarov/with-the-receiver-in-scope-7b52bdcca6e9
👍 24
d
I guess it's a matter of style (and just a side-point in the article), but isn't this a bit clearer:
Copy code
applicationWindow.apply { // this: ApplicationWindow
    title = "Just an example"
    position = FramePosition.AUTO
    content = createContent()
}.show()
where the initialization and instantiation is seperate?
👍 1
4
e
It could be 👍
j
@dave08 I often use
something.apply {...}.also {...}
to separate initialization and actual usage (where necessary of course), so happy to see other people thinking the same
m
I'm curious as to your comment about "imperative ui frameworks" having passed their heydey. All UI is imperative, isn't it? It's about management of state. React type "re-render the component" designs aren't fundamentally any different to older UI toolkits, it's just a performance hack to work around the fact that HTML is being used more as a canvas than a UI toolkit
e
Modern UI frameworks are moving to be declarative. You don’t have separate steps in the code to “create frame”, then “set its title” and other attributes. Nowadays, people tend to create a UI object with all its attributes in one unseparable action (see React, SwiftUI, Compose, etc, etc). I do not see any new UI frameworks announced recently that do it in the old-school imperative style.
It used to be just “declarative DSL” atop of imperative API, but that goes away. In many modern frameworks even if you want to write an imperative code, you simply cannot obtain a reference to your UI object to imperatively modify its properties.
(There are no UI “objects” nowadays in the object-oriented sense as it used to be in the past)
m
I haven't used SwiftUI or Compose. That sounds like a bad design. Stuff like TornadoFX makes sense - a pseudo-declarative DSL for building UI is convenient. Not having access to the underlying mutable objects (cuz for sure in all those frameworks those objects exist) seems like a bizarre choice though.
e
It looks bizarre only initially. In fact, it is more efficient and performant than traditional approach, with more developer ergonomics at the same time. Take a look at an old-school UI framework + DSL combo, for example JavaFx + TornadoFx. Internally, JavaFx core has tons of overhead in each of its UI components just to keep track of potential changes you might do to its objects imperatively. Very old frameworks did not have this overhead, by they were cumbersome to use (lots of boilerplate). None of that tracking infrastructure is really needed if you build your UI via DSL. It is not going to change outside of framework’s control if the underlying UI objects are not exposed to the end users in API. Take a look at Compose for a Kotlin-centric variant. It is the future for all kinds of UIs in Kotlin. @Leland Richardson [G] has a good writeup: http://intelligiblebabble.com/compose-from-first-principles/
r
I like to think KVision is a modern framework 🙂 And it is very much imperative and very much object-oriented.
In KVision you can use DSL to build UI, but at the same time you can obtain a reference to every single component and modify it's properties at any time.
e
This approach is still popular, indeed, but its heyday has passed.
r
In my opinion this is just an overwhelming impact of the web programming and the whole JS ecosystem, which is still HTML-centric. But it's not the best solution for everything and everyone. I'm sure JB develops IntelliJ Idea with UI libraries, which are very much imperative and object-oriented in the classic way 😉
e
Legacy apps will be developed this way for a long time, but it is being gradually replaced by reactive solutions. Even in open source you can find a few non-JS projects by JetBrains that are pursuing those ideas outside of JS ecosystem and there are tons of them around the world.
It does not mean it will ever be completely replaced. It just means the world has seen a better way and there is no turning back on this road.
l
cuz for sure in all those frameworks those objects exist
Kind of. In Compose we end up targeting a mutable hierarchy of nodes we call “ComponentNodes”. These are not part of public API though, and are very primitive in scope. They have little more than a draw lambda, a layout lambda, and some children associated with them. When you render a “Button” in compose for instance, the declaration and configuration of that button is much higher level and what componentnodes it ends up creating are very much an implementation detail. Additionally, Compose’s runtime is built on top of a “Composer” object that manages a lot of what is happening. The composer is built in such a way that it can target both mutable tree types as well as immutable tree types (for instance a tree utilizing a shared persistent data structure). Of course, to be clear, Mutable/Immutable is not the same as Imperative/Declarative. And none of this is completely black and white, it’s a spectrum. Compose’s programming model pushes you in a declarative direction and part of that is when you want to change what UI is displayed on screen, you should think of it as data/state changing and then re-running a function that maps that data onto a UI. UI elements are configured by the inputs you pass into them, and not as a reference of some thing you pass around. This ends up having a lot of downstream effects in terms of reducing coupling and allows you to very easily refactor or delete parts of your UI. One might argue this feels like a hack if you are building on top of a UI framework that wasn’t meant for this (ie, DOM Elements on the web), but when the UI toolkit was built with this paradigm from the ground up, I would argue there is no longer this sort of “impedance mismatch”
e
It is hard to plug into modern OO language, but they adapt. JS is dynamic so it easily became the first playground. Statically typed languages follow. Swift had added tons of special new syntax and rules to support SwiftUI. Kotlin is coping a bit better so far (if I can say so) by the virtue of its initial DSL-friendliness, but it does not fly without compiler plugin either.
r
I would treat as an interesting observation the fact, that you think building reactive UI is better than imperative, but at the same time you design coroutines, which "kill" reactive programming paradigm by making it old-school imperative 🙂
e
That is not true. Coroutines don’t kill reactive programming. They enhance it, by making it easier and more concise to use in many cases.
Again, this is a good example that many modern improvements in developer ergonomics are hard to do only in the libraries without language support. RX in .Net was a cool, smart trick. With coroutines in the language you can do much better.
r
You created this monster and it is definitely widely regarded as an alternative to reactive programming 😉
e
Coroutines are like 50 years old concept. We definitely did not create it. It was there all along. What happened is that without coroutines in their languages developers were forced to abuse reactive libraries to create intricate solutions to the problems that are naturally solvable with coroutines. We are here just to equip people with proper tools for their problems, so that they don’t have to use a microscope to hit a nail, a task which is much easier done with a hammer, when you have one.
👍 3
m
I'm super interested in these topics so will definitely read the First Principles doc, thanks for the links and discussions.
I've been writing GUI apps a long time, since the DOS / TurboVision days, with an upgrade to Win 3.1 soon after. My initial reaction is that a Win32 style "mark dirty+rerender by reexecuting code on demand" approach was the first one used and got phased out in favour of mutable objects. So my starting point is that this sounds a bit odd but I'll check it out. The main issues I can see with React-style "rerender on change", which can certainly be convenient some times (e.g. if the UI structure needs to change dramatically based on data) is that it would end up needing a lot of hacks to avoid accidentally erasing state the user cares about like the contents of edit boxes, scroll positions, slider-pane positions and so on. GUIs are absolutely full of state that's being simultaneously controlled by the user, and also being controlled by the app. If you just re-create parts of the UI hierarchy from scratch when the data model changes, you'll need either a complicated diff/merge to avoid blatting user-controlled state, or you'll end up with a UI that feels flaky and glitchy to the user. It seems like it'd also be harder to do animations, because in animation you really want to be making the minimal changes to what's in memory but at high speed, and you need both old and new state to be kept track of simultaneously. Finally there's a lot of state you don't want to re-create in GUIs that isn't user controlled, but should be preserved because of efficiency, e.g. GPU textures, decompressed images ... functional programs are rather notorious for being less efficient than imperative in many cases due to unnecessary copying and work. Now, I can see a few cases where that may not be the case. One is - as said - if the UI is about to change dramatically anyway so the user wouldn't expect their half-entered state to stick around. Another is if you aren't really replacing the UI but doing a complicated diff/merge between the true mutable node tree and the one the developer sees. Another is if your GUI is hardly customisable to begin with, e.g. if the only state controlled by the user is the focus and contents of edit boxes. On mobile, I can see this working better, because such UIs have very little state that can be controlled by the user. Another is if you have a GUI state persistence framework so you can serialise/deserialise state. Android has one of those, or used to at least, because it would rebuild the entire GUI whenever the screen rotated ("big change"). Problem is most apps sucked at getting that right. Rebuilding the widget tree without losing important state was too hard. Many apps just disabled screen rotation entirely. So this is one reason it feels a bit of an odd approach. It may well be there are answers to all of these, and I have tremendous respect for Roman's opinions, so now I'll go read up on it.
(btw I have my own ideas about how to make reactive GUI work better ... so I'm not arguing JavaFX is perfect or anything)
OK, watched the vid and read the blog post. So indeed, Compose like React is doing a diff/merge on a mutable object tree. I'll think about it more, but I'm not yet convinced this is fundamentally much different in overhead to a JavaFX+TornadoFX type combo. I think if you have a DSL to build mutable objects, then on top you can get a long way with dependency tracking of reads of mutable state and re-execution of lambdas when a dependency of that lambda changes, which is the approach I've been playing with a bit on the desktop. At any rate, I feel I understand what you mean more now. Behind the scenes there's indeed still mutable objects, and there's a kind of patchup process mapping the transient object graph built by re-execution of functions to the underlying "real" graph that handles expensive state.
e
I’ve been building UIs since the DOS days, too, even wrote my own framework in TurboVision style, but I’m no looking at its reemrgence. It’s going out for good.
It’s not just the change in how the UI libraries are designed. It is a paradigm change in the programming languages that is happening right now. The Simula/Smalltalk/C++/Java object-oriented style had reacted its peak and is in decline. The famous Smalltalk vision of UI “objects” that you manipulate had been extremely fruitful and has lead to tons of great stuff, but the world has learned a better way to write UI code, a way without those objects in API, that is easier for developers at the same time. There’s no unlearning it. Hereby I predict that in 5-15 years even the legacy languages (like C++ and Java) will have to either somehow support this new paradigm in the language or will face oblivion in UI space. Junior developers, entering the market in 10 years, will be familiar with React/Compose/SwiftUI and will simply find it baroque and unnaturally cruel to write UI code in JQuery/Swing/JavaFx/Qt/GTK/etc.
m
Well, most UI toolkits can have this kind of thing layered on. Qt Quick is a similar DSL for Qt that's been around quite a long time. I've been hearing predictions of FP paradigm abolishing OOP for as long as I've been programming and it never happened, OOP is still going strong for various reasons. So we'll see 🙂
e
OOP will not be abolished, that’s for sure. Every UI framework still has reference-based mutable data structures (~objects) inside of it. It is just we are seeing them less and less exposed to developers in API surface. Exposing mutable objects, in general, is becoming less popular even outside of UIs.
o
guess for most cases it’s not about OOP vs FP, but about hiding the state/UI management guts from the UI programmer, focusing on intentions. HTML is pretty old technology, and shown to be powerful enough to handle very complex user interfaces, while not giving direct access to the underlying rendering primitive
r
And I would still argue with the statement, that this new UI paradigm is easier for the developers. What are the arguments for this ease? Perhaps it's easier only for simple use cases, but in general it's not. The APIs are still complex if we want to build complex GUIs. The limitations require developers to use hacks for many simple tasks. I treat the overwhelming number of JavaScript UI frameworks as a proof, that it just can't be done right this way 🙂
l
A lot of the points that were brought up earlier by @mikehearn are handled in a way that might be easy to miss if you just think of compose as react on top of android. And as far as complexity, the fact that we built an entire UI toolkit on top of this paradigm (instead of just bolting a declarative DSL on top of it) i think is proof enough that you can build something “complex” with it, and the overwhelming feedback that we’ve gotten from people that have used it so far is that it makes a lot of the things that were very difficult before very easy. Most of this is subjective though so feel free to form your own opinions. If you want to learn more about how Compose actually handles this diffing and state mgmt, etc, a good talk to watch might be this talk I gave at Android Dev summit. The latter half of it goes into a lot of detail about the way the runtime and compiler work together.

https://www.youtube.com/watch?v=Q9MtlmmN4Q0

m
Thanks Leland!
@Leland Richardson [G] Can you maybe discuss the intriguing "Compose for desktop" work that seems to be going on? Is Compose going to become a general purpose portable UI toolkit usable outside of Android?