Hacker News new | past | comments | ask | show | jobs | submit login
The Siren Call of KVO and Cocoa Bindings (metaobject.com)
41 points by mpweiher on Mar 15, 2014 | hide | past | favorite | 19 comments

You missed VPRI KScript implemented in squeak smalltalk.

Massive performance issues and unresolved Glitch issues.

KScript and KSWorld: A Time-Aware and Mostly Declarative Language and Interactive GUI Framework http://www.vpri.org/pdf/tr2013002_KSonward.pdf

Toward the Future of Personal Computing System Construction http://www.vpri.org/pdf/M2013004_agere.pdf

And the works of Sean McDermid Roly Perera and dm barbour (AWELON Blue)

There are tons of systems I did not mention, the field is quite large. Just start with the references I gave and continue down the rabbit hole :-)

I am quite aware of KScript and KSWorld, in fact one of the authors shares an office with me, though we are both not in that often. These are highly experimental systems, so they didn't quite fit into my narrative.

while i will go off and read a few of the linked papers, this post doesn't do a great job, in my opinion, of showing a meaningful relationship between generalized constraint solvers and one-way dataflow constraints. i use one-way dataflow constraints because they are simple/natural/expressive. generalized constraint solvers are vastly more general, and i can't see how they would apply in a non-geometric setting. isn't cassowary only really interesting when there are inequalities? when there are only equalities, isn't the system either over-constrained (bad) or not (fine)? (to be fair, i haven't read the cassowary paper.) would i ever want code that expresses an object in terms of an inequality with another object? will i ever care about cyclic graphs of constraints between objects that actually require a solver to determine if they are over-constrained, and will any solver be able to do the right thing without knowing some properties of the constraints (e.g. linearity)? (cycles are the one thing i'm careful to avoid when i write one-way dataflow constraints in e.g. MVC code.)

If you are already using one-way dataflow constraints, you weren't exactly the target audience for my writeup, which was trying to introduce this body of work to devs who may only be familiar with the pale imitations. I am a bit surprised that you are careful to avoid cycles, even the one-way solvers I mention handle those.

I also think that the one-way solvers are the most immediately useful. On the other hand, the linguistic support needed for a pluggable one-way solver seems very similar to that needed for other solvers, and yes, they can be quite useful in more general circumstances. After all there is a whole field and several companies that do this sort of thing commercially.

fair enough that maybe i'm not in the target audience. and perhaps i'm too stuck in my own head, writing iOS apps, where i can't imagine general constraint solvers being used to solve a software-design problem (as opposed to an algorithmic problem, where it certainly does make sense). i am genuinely curious - are general constraint solvers useful as a software-design pattern?

are one-way dataflow constraints really any different from the observer pattern? i avoid cycles exactly because i implement it myself, and don't even have a solver, per se. even KVO seems not to handle cycles. and when e.g. i am using auto-layout in IB, even then i am careful to avoid over-constrainedness, even though the solver can settle on a "solution."

True .. and while I revisit the linked references some of which I'm familiar with (fran and ilk, for ex), I'd like to point out that having the list of references at the bottom of the post of something would be much more handy than scanning the text for normal words with weird colours. The traditional paper format is awesome for readability when dealing with material that references a lot.

The Garnet and KR work you cited from CMU is what got me into constraints/declarative OO/FRP. The term I best like for this style is "Dataflow". Cells, which is/was a dataflow extension for CLOS (object oriented Lisp) used this term, and uses both lazy and eager dataflow.

It was a huge amount of fun to work in, because it made GUI and server programming amazingly productive for a small team. You could get things working super fast, and with very little bugs because so much of the bookkeeping was done for you.

And one of the secrets compared to other systems in this vein was exactly the eager default, with a lazy option. By choosing per-formula which model you wanted, you could use the system that handled the most bookkeeping for you.

The Lisp code is still here:


The old examples and so on are in the Internet Archive:


No mention of SQL? Relational algebra is a very prominent example of one-way constrain system, if you look at individual nodes in the query execution tree. Also, remember to consider index maintenance and materialized/indexed views.

I like that article is well-researched with references to various papers, there is definetly some good reading material there.

KVO is akin to manual memory management - a lot of individual pieces to tweak and remember, and the outcome is very brittle and error-prone.

I prefe the approach where dependant values are expressed as functions, and then a caching layer is added where needs for performance.

How is it "brittle" and "error-prone"? You create your model object and KVO observe its properties from the controller. How much simpler and more decoupled could you get? If you're using MVC correctly, it seems to me that the pieces just naturally fall into place. You also get the benefit of being able to change your model from elsewhere in your program, or even swap it out completely, and have it automatically reflect in your view.

(On the other hand, if your app is not model-based, I can see how it could get unwieldily.)

The promise of simplicity does not match the reality. It's explained well here, this was linked in the article too: http://khanlou.com/2013/12/kvo-considered-harmful/

There are three ways in which it gets brittle:

1. Suppose we have a model object with 10 properties, which you want drawn on screen. Following KVO pattern you would set values for 10 labels during [viewWillAppear], then you would signup for KVO 10 times, then, in handling KVO notification, you would update one of the 10 labels (10 different update code paths), and then you would unsubscribe from KVO in [viewWillDisappear] and/or in [dealloc] also 10 times. Adding a new property, you have to add code in all of these place, and failing even one could have disastrous consequences - inconsistent data display or a crash.

That was a simple example. Now consider a more complex example:

2. Suppose we have a class Person with firstName and lastName. Now if you signup for KVO for both of these, and I perform a legal name change for that person from "Bill Smith" to "Jane Doe", you will receive two notifications. If you're sending out a welcome email, you will end up sending two emails, one of them for nonexistent person "Jane Smith". KVO notifications expose inconsistent data. You could rectify that problem by creating a method for changing both first and last name at the same time and managing notifications carefully, but then all the elegance went out of the window - you're doing your notifications manually.

Now for a really screwed up situation:

3. Suppose we have the Account class with a list of bank transactions. Separately, we have a Person where you display the person's total wealth as result of those transactions. Normally, I would start by computing the account balance each time on demand, but let's say that computing is expensive and we want to persist/cache the result for speed. To keep the Person.wealth up to date we sign up for KVO notifications for Account.transactions and recompute the balance. Now your UI component is hooked to KVO-observe both the list of transactions and the Person.wealth. I post new transaction, and you get two KVO notifications - one for Account.transactions, and one for Person.wealth. But you get them in random order! You might get the Person.wealth notification before transaction notification, or you may get them in the opposite order - exposing inconsistent data (transactions would show the transaction, but wealth will show the old balance because it didn't receive the notification yet). And unlike the example #2, there is nothing we can do to fix this, because the conflicting data is in different classes.

Caching has the reverse problem - needing to figure out when to invalidated the cached data. If only there was a way to ahem observe when the values of the keys the cache depends on change...

Doesn't caching has an equivalent set of pitfalls though?

KVO and Cocoa Bindings are not the same thing. Just because Bindings use KVO does not mean both technologies are alike. KVC/KVO has many uses outside Cocoa Bindings or UI updates. Further, obviously KVC/KVO has a performance penalty and that is exactly why you would not use it for high performance tasks. However for just about everything else it is a great tool and will minimize the amount of code you write greatly, resulting in improved maintainability.

"KVO and Cocoa Bindings are not the same thing"

Yes. From the Fine Article:

"[..] bindings are one-way dataflow constraints, specifically with the equation limited to y = x1. More complex equations can be obtained by using NSValueTransformers. KVO is more of an implicit invocation mechanism that is used primarily to build ad-hoc dataflow constraints."

Bindings: simple one way dataflow constraints.

KVO: implicit invocation.

Not the same thing.

Ive begun learning Reactive Cocoa and I feel that it does most of what you mentioned your post except that the syntax quite unwieldy. Maybe some higher language that compiles down to it just might do the trick.

I am probably going to be downvoted by have you had a chance to look at https://github.com/seivan/SHRACComparison and maybe https://github.com/facebook/KVOController - you really don't need RAC to use signals.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact