Partial Application With Ember Getter and Setter

There is a more functional approach available for the Ember get and set methods that you may have seen or even been using, get(this, ‘item’) instead of this.get(‘item’) and set(this, ‘item’, value) instead of this.set(‘item’, value). When I first saw this I too wanted to write code like that in my Ember projects, after all, no one is immune to the touch of JS FP fever right? So I destructured the Ember namespace, pulled get and set into the current scope and… the end. Hope you enjoyed this article and thanks for… What? Oh you expected more did’t you? If you don’t think this article should end here then good, neither do I. Let’s take this functional syntax for get and set and give them a little extra whompff. And by “whompff” I mean partial application.

When we’re done, we’ll create a decorator function that will allow us to decorate any namespace with our Ember.get and Ember.set partial application functions. So we will be able to bring these custom utilities into any namespace, even the global namespace. If that doesn’t appeal to your good natured programming sensibilities then the decorator function can just as easily be destructured to any namespace, after all it will simply return a POJO (plain old JavaScript Object). The POJO can destructure exactly the same way as you would the Ember object in the first example below. Yeaya! Something for everybody!

1
2
3
4
5
6
7
8
9
import Em from ‘ember’;
// This is ES2015 destructuring here:
const {get, set} = Em;
// which is analogous to this:
const get = Em.get,
set = Em.set;

// Which allows us to make calls like this:
let veryAble = get(this ‘inventorium’);

Before getting to how we’ll customize get and set, let’s discuss quickly the pros and cons of using ES2015 destructuring (above) to lift the default implementations of these functions out from within the Ember namespace. It’s a perfectly fine choice to make, just not one I’m fond of using because it requires boilerplate at the top of any modules which make use of the get and set functions and management of those statements might become tedious.

Besides that fact I don’t want to remember to drop in destructured assignments for get and set to each file and baking those statements into my files doesn’t feel good for me personally either. Lastly, JSHint will complain wherever the get or set functions are destructured but not used within a module, spamming ye ole’ “components/super-elem.js: line 4, col 8, ‘get’ was defined but never used” and then what? Add some ugly inline suppressor / jshint unused:vars / to silence the beast? No, not on something so ubiquitous.


Partial Application of Get

That being said, this article is about adding partial application to get and set. Destructuring get and set from the Ember namespace would only expose us to the default Ember.get and Ember.set functions which don’t provide partial application. So we’ll need to develop a proxy between those calls to Embers.get and Ember.set and in our proxies we can handle the partial application for cases where an incomplete set of arguments is passed. (If you need a primer on partial application or currying check out Currying vs Partial Application by: Dave Atchley).

Without further introduction. Let’s make our get solution. We can generate a file to hold the new get and set implementations. Run ember g util ember-getsetter or just create a javascript file somewhere in your project if you want to follow along. Open utils/ember-getsetter.js and start by add the following code:


1
2
3
4
5
6
7
8
import Ember from ‘ember’;

function partialEmberGetter (context, property) {
if (arguments.length === 1) {
return (prop) => Ember.get.call(context, context, prop);
}
return Ember.get.call(context, context, property);
}

This function we just wrote can be partially executed, meaning if we don’t call it with both arguments then it will return a function that can be called again. Let’s look at we’d use this more functional version of get inside a component if get worked like partialEmberGetter.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export default Ember.Component.extend({
weCanDoItThis() {
const days = get(this, ‘days’);
},

orWeCanDoItThat() {
const getThis = get(this);
let phrase = <span class="subst">${getThis('greeting')}</span> <span class="subst">${getThis('name')}</span>;
}

orWeCanDoItThis() {
// Unary calls
let hours = get(this)(‘hours’);
// are the same as
let hours = get(this, ‘hours’);
}
});

I don’t know about you but this feels good to me. Later we’ll assign the partialEmberGetter function to get. For now, Keep in mind with these partially applied functions the bulk of the work is held off until the final argument is passed in, so it’s not as if we’re doing twice the work by making twice as many calls because the first call returns quickly with another tiny function. We could probably actually optimize a little by refactoring our partialEmberGetter to look like this:


1
2
3
4
5
6
7
import Ember from ‘ember’;

function partialEmberGetter (context, property) {
return (arguments.length !== 1) ?
Ember.get.call(null, context, property) :
Ember.get.bind(null, context, prop);
}

In the new version of partialEmberGetter above we’re using the conditional operator ? : which then returns either the finished call to Ember.get or a bound function which can be called with the last argument. In either case, since we used the conditional operator both return values are in tail position which qualifies them for TCO (tail call optimization). Not that we have to or should even worry about TCO in situations like this.

Partial Application of Set

Moving on once more. We’ll want to do something similar for set. Below is a partialEmberSetter function which will eventually let us make partial calls to set. It’s a little more detailed because there are 3 arguments. Normally you would just use a curry or partial function from Ramda, Lodash or another function library. But we want our little module to stand on it’s own so we can use it in any Ember project.

1
2
3
4
5
6
7
8
9
10
11
// …
function partialEmberSetter (context, property, value) {
switch (arguments.length) {
case 1:
return partialEmberSetter.bind(null, context);
case 2:
return partialEmberSetter.bind(null, context, property);
default:
return Ember.set.call(null, context, property, value);
}
}

You could refactor the above function a little bit if you wanted to make that function smaller. The switch statement is helpful for illustration though and historically they are fast. I think this function makes for a good switch “sitch” (sitch: n - slang term for “situation”). When partialEmbersetter is called with 1 argument, it returns a call to itself bound with that first argument. If that function is called with another argument, (or if the original call was made with 2 arguments to begin with), partialEmberSetter returns a bound function with 2 arguments already set. Originally I thought to use the ES2015 fat arrow functions as the call wrappers, (val) => Ember.set.call(null, context, property, val) instead of Function.prototype.bind, but after some thinking I concluded that bind is designed to do this and it’s already supported in every browser except IE <= 8 so why complicate things just because I think fat arrows are sexy.


Let’s explore a world where set = partialEmberSetter. It’s an easier concept to explain with code.


1
2
3
4
5
6
7
8
// we can do this
set(this, ‘guitar’, 425); // sets ‘this.guitar’ to 425
set(this, ‘piano’, 2400); // sets ‘this.piano’ to 2400

// … or
set(this)(‘guitar’, 425); // this.set(‘guitar’, 425)
set(this, ‘piano’)(2400); // this.set(‘piano’, 2400)
set(this)(‘drums’)(799); // this.set(‘drums’, 799)

The top 2 calls in the examples above are practical, while the last 3 are demonstrative, you could always make unary calls like set(this)(‘item’)(value) but I don’t think you should. Below are some more likely ways in which you might find yourself using this implementation.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Setup reusable object setter functions
const setPriceFor = set( get(this, ‘priceList’) );
setPriceFor(‘guitar’, 425); // this.priceList.guitar = 425
setPriceFor(‘piano’, 2400); // this.priceList.paiano = 2400

// … or specific property setter functions
const setStrings = set(this, ‘guitarStrings’);
setStrings(6);
// ..
const breakString = ( ) => setStrings( get(this, ‘guitarStrings’) - 1);

// … or if your awaiting a Promise instead of
somePromiseForPrices()
.then((price) => this.set(‘storePrices’, price));

// we could do:
somePromiseForPrices()
.then(set(this, ‘storePrices’));

My original implementation hadn’t allowed for the set(obj)(prop)(val) syntax to be used. I added it in for the sake of completeness and for this article, though I haven’t yet found myself needing to use it. Abstraction is nice, but too much abstraction can become confusing. Take the example below, I personally find the bottom easier to read


1
2
3
4
5
6
7
8
// Example 1: Too much abstraction for me.
const setThis = set(this);
const setName = setThis(‘name’);
const setPrice = setThis(‘price’);

// Example 2: Less is more.
const setName = set(this, ‘name’);
const setPrice = set(this, ‘price’);

Now that we have code to implement our new get and set, let’s actually set it up in a usable way. Somewhere in our ember-getsetter.js file add this export statement.

1
2
3
4
5
export default function getSetDecorator(namespace = {}) {
namespace.get = partialEmberGetter;
namespace.set = partialEmberSetter;
return namespace;
}


The above function will directly add both of our new functions on any object we pass into it as methods namespace.get and namespace.set. Since this function returns an object, we can call it and then destructure the result exactly as we would destructure Ember to get the basic implementation of get and set.


1
2
3
// Bring our get & set into the current module namespace
import getSetDecorator from ‘app-name/utils/ember-getsetter’;
const {get, set} = getSetDecorator();

Or we can apply this all over our application with one fell swoop if we are so inclined!


1
2
import getSetDecorator from ‘app-name/utils/ember-getsetter’;
getSetDecorator( window );

The Final Code (for your enjoyment)

In any case, here we are and good to go! As some extra knowledge, Ember Objects import the functions they use to implement their this.get and this.set methods from the ember-metal get and set functions. Normally when we call this.set in an object, we’re not directly calling Ember.set, but in a roundabout manner that is exactly what we are doing. We’re calling a method, which calls a function, which calls the get or set function imported from Ember metal, and Ember.set/get come from the same place. So what we are doing here isn’t risky business, it’s just an abstraction. If the underlying Ember.get or Ember.set changes then our implementations change with them. Separate paths to the same place. Also, this is lightweight and by no means an all or nothing solution. You can switch between the default calls to this.set and this.get as well as get(this, ‘item’), it’s all up to you!

We can also use these getters and setters to get and set on POJOs “plain old Java(Script) objects“ and get the “unknown property” benefit that Ember.get supplies us. Basically anything you can do with Ember.get or Ember.set.


Here’s the final code, if your looking to add to this you could start with getWithDefault as the next logical addition.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import Ember from ‘ember’;

/*
Decorate an object/namespace with our more functional
version of Ember.{set,get} allowing partial execution.

@param namespace
@returns Object
*/

export default function getSetDecorator(namespace = {}) {
namespace.get = partialEmberGetter;
namespace.set = partialEmberSetter;
return namespace;
}

function partialEmberGetter (context, property) {
return (arguments.length !== 1) ?
Ember.get.call(null, context, property) :
(prop) => Ember.get.call(null, context, prop);
}

function partialEmberSetter (context, property, value) {
switch (arguments.length) {
case 1:
return partialEmberSetter.bind(null, context);
case 2:
return partialEmberSetter.bind(null, context, property);
default:
return Ember.set.call(null, context, property, value);
}
}



Some extra reading if your interested in Partial Application