Coding style - getters/setter functions

694 views
Skip to first unread message

Ziggy Jonsson

unread,
Aug 29, 2012, 10:34:52 PM8/29/12
to d3...@googlegroups.com
It seems to be the norm with most d3 extensions to define individual getter/setter functions for all "changable" local variables.  
This can be tedious in development as the same code is repeated over and over and I wonder if it would make sense to gravitate towards a more general template?

(a)  define all gettable/settable variables in an object like so:
_ = {orientation:"left",position:"bottom",........,x:2,y:4}
and use "_.orientation" in the code instead of orientation variable etc

(b) call a general getter/setter on the object in question before it's returned:
d3.getset.call(Obj,_);

(c)  with something like
d3.getset = function(_)  {
  var self = this
  Object.keys(_).forEach(function(key) {   
    this[key] = function(d) {
      if(!arguments) return _[key];
      _[key] = d;
      return self;
    }
  });
}

Obviously all local variables that remain "unsettable" remain as is (not included in _)

I'm curious to hear your thoughts on pros/cons between the two approaches

Best regards,
Z

Kai Chang

unread,
Aug 29, 2012, 11:45:52 PM8/29/12
to d3...@googlegroups.com
Pros:

* By exposing _, you can see all the public settings of the component
without calling getters individually.
* Less boilerplate at the end of components.

Cons:

* Extra "_."s all over the place. Extension code will look slightly
different than the examples and d3 code base.
* Some setters require other things to happen, such as a dependent
variable to be updated. These would still need to be written by hand.

Kai Chang

unread,
Aug 30, 2012, 3:55:09 AM8/30/12
to d3...@googlegroups.com
Pro:

_.x is easily found in a text search.

Kai Chang

unread,
Aug 30, 2012, 5:31:38 AM8/30/12
to d3...@googlegroups.com
I just watched this excellent talk on the history of matplotlib by the
late John Hunter:

http://www.youtube.com/watch?v=e3lTby5RI54

In the last 3 minutes he talks about client-side rendering and a "a
top-level figure object persisted as a very rich json structure". It
would make sense to use _ for that role.

Kai Chang

unread,
Aug 30, 2012, 5:40:05 AM8/30/12
to d3...@googlegroups.com
bug fix for d3.getset, this -> self

d3.getset = function(_) {
var self = this
Object.keys(_).forEach(function(key) {
self[key] = function(d) {
if(!arguments) return _[key];
_[key] = d;
return self;
}
});
}

Kai Chang

unread,
Aug 30, 2012, 5:47:15 AM8/30/12
to d3...@googlegroups.com
One more fix: !arguments.length. This should provide the correct
behavior for the getter. Please ignore the previous buggy bug fix.

d3.getset = function(_) {
var self = this
Object.keys(_).forEach(function(key) {
self[key] = function(x) {
if (!arguments.length) return _[key];
_[key] = x;

Kai Chang

unread,
Aug 30, 2012, 6:51:10 AM8/30/12
to d3...@googlegroups.com
I just implemented this for parallel coordinates and I like it. Here's
the commit (see parcoords.js).

https://github.com/syntagmatic/parallel-coordinates/commit/cbe4d8034406938d5e8ed9075b14fb8cb5e2c632

Saved me ~40 lines of code on a ~300 line component, not including the
definition of d3.getset().

I named the variable __, to avoid future collisions with underscore.js

A benefit of the syntax is that functions which manipulate the public
chart state look different than pure functions.

For private variables that depend on public variables, it's useful to
define those as functions instead of constants and call them when you
need them. This way the value is always up to date at the time it is
accessed.

Ziggy Jonsson

unread,
Aug 30, 2012, 7:37:44 AM8/30/12
to d3...@googlegroups.com, kai.s...@gmail.com
Thanks Kai, I went through similar thought process as you.   

Pros:
_ is not exposed to the "public" in my examples above and the variables should be as private/public as they are with individual getter/setter function
_ prefix is a reminder that this variable is a standard gettable/settable (i.e. semi-public) and doesn't look bad in actual code
If a variable doesn't have _ prefix, it's either private or has a more complex getter/setter than the standard one.

Cons:
When you have chained creators (i.e. factory functions with getters/setters that create objects with getters/setters) it's impossible to keep one symbol for settable local vars.
Conflicts picking one character variable (such as _) and longer name for the exposed collections is  more tedious.

Here is an (random) example of Mike's Hive gist updated to _.   (the function I coined was d3.expose which has a nice ring to it)
https://gist.github.com/3526579 

Best regards,
Z

Chris Viau

unread,
Aug 30, 2012, 10:52:24 AM8/30/12
to d3...@googlegroups.com, kai.s...@gmail.com
Here is another example: http://jsfiddle.net/christopheviau/YPAYz/
I like the way public variables are prepend with "opts", that makes them easily recognizable as such. Since the opts object is public, you can also bypass the generated getters and setters and play with it directly, i.e., for inspecting or for grabbing all the options at once for mixins. And BTW, I prefere not to use _ as a variable, as it could make it look like an underscore.js function and make it harder to read. Personal taste.

marc fawzi

unread,
Aug 30, 2012, 11:26:26 AM8/30/12
to d3...@googlegroups.com, kai.s...@gmail.com
"exports.opts opts;"

I always found it odd to treat a regular function as a hash/object. 

Could you do "var exports = new function() { ... ; this.opts = ... ; ... } 

I assume exports would then be an instance as new function looks to be an anonymous constructor

I claim no expertise when it comes to this discussion... just thinking out loud

Mike Bostock

unread,
Aug 30, 2012, 11:28:59 AM8/30/12
to d3...@googlegroups.com
There was an earlier thread on this topic:

https://groups.google.com/d/msg/d3-js/2WVlWrLOfhc/G8gCq0XijhkJ

Something like this would work:

function property(value) {
return function(_) {
if (!arguments.length) return value;
value = _;
return this;
};
}

As you can see from D3's code, though, I don't do this in practice. I
don't mind the tiny amount of boilerplate code, mainly because it
allows me to customize the behavior when getting or setting a
property. For example, you can compute derived data as a side-effect,
coerce types, validate input, or change the internal representation.
Also, it adds a tiny bit of overhead whenever you access a property,
which means that it's slower not just on external access (uncommon)
but internal access during render (common).

Mike

Ziggy Jonsson

unread,
Aug 30, 2012, 2:15:04 PM8/30/12
to d3...@googlegroups.com
Thanks everyone. I somehow missed the earlier discussion, but I think pros/cons are getting clearer to me.

Finally for those who are evil enough:
[....string array of local variables to be exposed....].forEach(function(fn){obj[fn]= function(_){if(!arguments){return eval(fn)};eval(fn+"=_");return obj}})

See https://gist.github.com/3535835 for an example.

I wonder if this eval would be considered safe as there is no obvious way to inject into the eval string.   
However for security reasons this message will self-destruct in 10 seconds.

Kai Chang

unread,
Aug 30, 2012, 3:33:08 PM8/30/12
to d3...@googlegroups.com
Too evil! It'd be interesting to do a visualization of recurring
discussions on the list.

I want to talk about pros for _ as an object more, so here goes.

I think reusable charts need a state object that's serializable as
JSON, which means it can't contain functions as values. I'm not sure
the best way to deal with it, because currently d3 components are
functions with an internal state.

This doesn't necessarily need to be the same as the _ variable we're
discussing. It might be a pair of methods: chart.serialize() and
chart.initialize() for instance. It could also be the argument passed
in when generating a component:

d3.svg.axis( axis_json )

At some point you have to draw a line in the sand and say, "I'm sorry
functions, but it's time to JSON.stringify and you're not real data".

I tried to find a spec or outline of what John Hunter's "rich json
structure" looks like, but I couldn't find anything.

It looks like some poor souls have resorted to putting in code as an
array of strings to make IPython charts:

https://github.com/dgleebits/PythonSystemAdminTools/blob/master/d3jsBarChart.ipynb

There is a slight performance hit when accessing an object, but I'm
sure there are ways around it for tight loops.

Kai Chang

unread,
Aug 30, 2012, 4:21:21 PM8/30/12
to d3...@googlegroups.com
I was looking at these slides and it appears that d3.svg.arc already
has the property of being able to take a json object:

http://bost.ocks.org/mike/d3/workshop/

Kai Chang

unread,
Sep 6, 2012, 4:22:35 AM9/6/12
to d3...@googlegroups.com
I was able to eliminate the boilerplate for setters with side effects
using d3.dispatch:

https://github.com/syntagmatic/parallel-coordinates/commit/097641740e24a313b15382ce32a8d18e930fcdfb

The parallel coordinates chart class has two dispatch objects. One
internal to trigger side effects, and one external for other
listeners.

By taking Ziggy's approach of storing settable variables in an object,
I also provide *both* configuration techniques proposed in reusable
charts (an initial config object and method chaining):

http://bost.ocks.org/mike/chart/

I simply override the defaults on __ with the config.

A cool feature is that if extra keys appear in the config object, a
getter/setter with public dispatch event is created for that key. This
seems a compelling way to add custom state/events to charts without
modifying their internals.

Setter events give you both old and new values, in case you need both
to stage a transition or keep an undo history.

johowie

unread,
Jul 14, 2013, 8:59:54 PM7/14/13
to d3...@googlegroups.com, kai.s...@gmail.com
Thanks to Kai for sharing your approach of using d3.dispatch for setters with side effects I am wondering if you or anyone has developed this further and perhaps made a template or a more in depth annotated version?

I note that the getter/setter events are added to both the `events` d3.dispatch object and the `set_events` d3.dispatch object (called `side_effects` in most recent version of you d3.parcoords.js) yet there are no listeners for those events on the `events` object. why is this ?

Kai Chang

unread,
Jul 14, 2013, 9:14:27 PM7/14/13
to d3...@googlegroups.com
Those are for external listeners. They're exposed here:

// expose events
d3.rebind(pc, events, "on");

For example, to listen to brush events.


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Curran

unread,
Feb 22, 2016, 1:32:27 PM2/22/16
to d3-js, kai.s...@gmail.com
Hi all,

I created a new library that abstracts the getter-setter pattern and adds the ability to react to changes in state.


Please let me know what you think. I may be re-inventing the wheel here for the thousandth time, but I just wanted to lay the foundation once and for all, industrial strength, so I and others can use this as a building block for larger things. Any feedback is welcome.

Related discussions:
Also I just want to thank the D3 community for being so awesome. There are so many great people on this list who have helped me immensely over the years.

Best regards,
Curran
Reply all
Reply to author
Forward
0 new messages