
Implementing the Elm Architecture in Swift - rheeseyb
https://medium.com/design-x-code/elmification-of-swift-af14b7f92b30
======
jawngee
As someone who writes some pretty hardcore UIs in Cocoa, this reactive style
stuff seems applicable to a pretty narrow class of application.

The more you fight Cocoa, which I believe this stuff does, the bigger the mess
you'll create for yourself once you view hierarchy becomes large and filled
with custom views that don't really fit what 99% of these tutorials cover.

Furthermore, trying emulate "dom" diffing by replacing views, instead of
updating them, as they seem to be illustrating towards the end of the article
(could be reading this wrong though) is pretty inefficient way of doing things
in Cocoa.

~~~
cballard
This might be somewhat true for this particular article, but please do not
confuse that with functional-reactive-programming-like systems in general. I
don't know RxSwift so will write in terms of ReactiveCocoa.

1\. Apps are more than views.

2\. ReactiveCocoa and RxSwift are just asynchronous data over time,
represented as Just Another Data Structure ( _cough_ monad). Even if you just
use them instead of callback blocks, it's an improvement because they can be
composed, stored in data structures, and used in ways that methods taking a
callback function and returning Void cannot. Cancellation is then implicit on
deallocation, instead of needing to reference a "cancellation token" or other
icky state like that.

3\. Complex UIs are _exactly_ where you want this type of system, because it
provides type safety and compiler verification, instead of hoping that your
target/selectors and KVO work and never break when you edit something and the
compiler doesn't complain.

I think that a popular JavaScript UI library being named "React" has damaged
the perception of FRP-like systems. Generic FRP-like systems are not related
to UI, other than that they can be used for it. People are confusing "DOM
diffing" for having anything to do with signals/streams.

In UIs, FRP-like systems allow you to take some evil imperative state (a
slider was moved!), lift it into a happy pure-functional world, process the
inputs, and drop out of evil imperative state at the end ("update the color").
For example, an RGB slider (using a bit of shorthand in places):

    
    
        combineLatest(redSlider.value, greenSlider.value, blueSlider.value)
            .throttle(0.5, onScheduler: UIScheduler())
            .map({ r, g, b in UIColor(r, g, b) })
            .startWithNext({ color in colorView.color = color })
    

In four lines, we accept input from three different controls, wait for the
changes to "settle" (let's say updating colorView.color is expensive for some
reason), and update the view! It's very easy. Let's say we want to make a
change, and only update colorView when the user taps a button:

    
    
        combineLatest(redSlider.value, greenSlider.value, blueSlider.value)
            .sampleOn(button.tapped)
            .map({ r, g, b in UIColor(r, g, b) })
            .startWithNext({ color in colorView.color = color })
    

Only one change necessary. In plain-old-Cocoa, this would require another
instance method to be defined.

~~~
drewcrawford
So, this is the first time I actually _understand_ what the FRP people are
talking about. So, thanks for that. It has been greek to me for some time.

However, now that I do understand it, I agree with parent. I don't want to use
this for complex UIs. Point by point:

> Cancellation is then implicit on deallocation

In practice, you're going to have retain cycles, no? I mean I don't know where
these four lines "live", but if we write them in a closure and ship them off
to a sync/diff/runloop engine, unless we are quite careful, that sync engine
is going to hold a great many strong references. Unless you intend this to be
an IBAction definition, in which case...

> because it provides type safety and compiler verification, instead of hoping
> that your target/selectors and KVO work and never break when you edit
> something and the compiler doesn't complain.

As long as the underlying Cocoa uses target-action, true compiler verification
is impossible. Compiler verification checks _something_ (these four lines) but
it doesn't check that these lines actually run when the slider is moved in any
way.

> FRP-like systems allow you to take some evil imperative state (a slider was
> moved!), lift it into a happy pure-functional world,

It's not immediately clear to me how e.g. throttle is implemented, but it must
accumulate state inside it somehow in order to replay the event after the
timer.

> Complex UIs are exactly where you want this type of system, because it
> provides type safety and compiler verification, instead of hoping that your
> target/selectors and KVO work and never break when you edit something and
> the compiler doesn't complain.

Compiler verification is good; tests are better. And I do not understand how
you would even begin to write unit tests for this.

Now we get to:

1\. Stopping in the debugger and trying to reason about these signal chains is
complicated, because what we have here is a datastructure in memory, not lines
of code I can step through

2\. This example does not account for threading, and in any nontrivial example
you want to move between background and foreground a few times. It also does
not deal with "splitting/merging" (multiple observers, etc.) and I suspect the
intersection of those two features is a sharp edge.

3\. Finally, let's compare against a slightly more traditional syntax:

    
    
        @IBAction func valueChanged() {
            dispatch_throttle(0.5, onQueue: dispatch_get_main_queue()) {
                colorView.color = colorToValue(red: sliderA.value, green: sliderB.value, blue: sliderC.value)
            }
        }
    

This syntax is also four lines of code, including the context of where the
lines live. This example resolves all the problems I listed with the FRP
example. In addition, it also collates which slider goes with which color
component in a single line, rather than breaking that relationship apart
across a (potentially long) signal path.

To evolve from your first example to your second example we would just change

    
    
        slider.addTarget(self, action: "valueChanged", forControlEvents: .ValueChanged)
    

to

    
    
        button.addTarget(self, action: "valueChanged", forControlEvents: .TouchUpInside)
    

While I readily concede this aspect is not quite as elegant as your example,
to me having a slightly more complicated 1-line diff is a very low price to
pay for all the other benefits I listed.

~~~
jdreaver
I just wanted to respond to this:

> It's not immediately clear to me how e.g. throttle is implemented, but it
> must accumulate state inside it somehow in order to replay the event after
> the timer.

Certainly, a lot of FRP (and functional programming in general!) concepts
involve state. I would never argue that there is no state in functional
programming, just that the state is more explicit, localized, and easier to
reason about.

In fact, in pure FRP, there are two fundamental building blocks: Behavior and
Event. A Behavior is simply a value over time, and an Event is a value at an
instant in time. Therefore, Behaviors are where you store state. However, what
makes them "easier to reason about" is that oftentimes the API forces you to
construct them in a way that makes all of the possible ways to change the
Behavior are clear and present at once.

In summary, all inherently stateful concepts require stateful code, at lease
somewhere under the hood. I would say that FRP and functional programming just
make the state explicit.

------
shoover
Interesting times for experimental Swift UI libraries coming out, with tcp-
json-swift [1] mentioned last week and now this.

This is an excellent synthesis and transfer of related technology. Nice work!
Some of the Swift- and NS-isms are tricky to follow without knowing Mac dev,
and I'm still wrapping my head around how it all wires together, but
especially the update loop is very clear and small and I think a unique use of
Rx to blend into React/virtual-dom/Elm ideas.

My perspective is I am trying to bring the Elm Architecture to F# but started
out porting virtual-dom.js on the bottom. It's early and ugly and just at the
point of having to deal with patching children nodes, so parts of this are
quite relevant. One avenue I intend to check out is performing set operations
on a flat node list and using something like protocol methods on parent views
to add and remove children, so that part of the code is definitely going to
trigger some thoughts.

The dictionary of observables indexed by node threw me at first. AFAICT the
virtual views, action types, and updates all compose, and then once it's time
to do anything with real views (the patch operation in diff/patch terms) just
that part is handled independently per node? (EDIT: Ah, I see the context
dispatch is composed as in Elm, and only pushing down model updates is
separate.) It's dead easy compared to the work virtual-dom.js has to do to
index patches, index DOM nodes, and walk the DOM applying updates. This seems
to handle all that with much less code, with a couple caveats:

1) The ability to reorder children is missing, right?

2) Any plan to support laziness to curb rerendering sections of the virtual
tree? It's probably not needed for many apps, but for some reason the feature
gets air time in discussions around React and Elm. (Looks like all observables
get called unconditionally once per update, too?)

[1] - [https://github.com/chriseidhof/tcp-json-
swift](https://github.com/chriseidhof/tcp-json-swift)

~~~
rheeseyb
Thanks for the feedback! To answer your questions: 1) Currently, yes, though
that is something that will definitely be implemented as we work on this
further 2) Laziness there is something that we're going to look into. We need
to first assess how much of an impact it has on performance - if that's big
enough to warrant taking further (which it likely will be for more complex
apps) then we need to figure out the best way to perform a deeper diff of the
model Good luck with the F# port - I'd be very interested to hear more!

~~~
shoover
Great, I will keep an eye on your repo and look forward to seeing how you
solve those problems, if it comes to it.

There's not much to say on the F# front yet except that the goal is to get a
taste of functional rendering from an app state atom for Windows GUIs and F#
and the Elm Architecture seem like the most approachable way to get there for
me. So far the work consists of porting the node, thunk, and widget designs
from virtual-dom.js to F# types and functions and using .NET reflection to
create and patch live views. Work continues on the patch function, but seeing
your code is definitely creating an itch to move on to the architecture.

------
rheeseyb
Open sourced here: [https://github.com/momentumworks/swift-
elm](https://github.com/momentumworks/swift-elm)

------
phatbyte
As someone who's currently learning Swift and Cocoa and have no idea what Elm
is. Is this something actually useful that helps building apps?

~~~
brudgers
Elm is a language that compiles to JavaScript that is designed to be somewhat
accessible to people already using JavaScript...one could call it a flavor of
JavaScript that's influenced by languages like Haskell and not get an argument
from me.

On the other hand, this project, though interesting, isn't something that the
process of learning Swift and Cocoa would really benefit from. If it seems
kind of interesting, starting with Elm as a separate project is probably a
better way to get context for evaluating using the Elm architecture in Swift.

