Functional Mixins In JavaScript
Mixins add behavior to a type (or class) without relying on inheritance. JavaScript has nice prototype-based types and recently added classes, but lacks first-class mixins. Fortunately, it provides everything we need to implement them. Since we use mixins for Play, our library for creating native Web Components, I thought I’d share our approach here.
I first encountered mixins working with Flavors Lisp, back in 1988, which introduced the term to world of programmers. The author of Flavors, Harry Cannon, was inspired by an insanely popular local ice-cream shop, Steve’s. Every frozen yogurt shop now has them, as do many programming languages and libraries. Now, that’s the kind of cross-discipline impact I like to see!
Why Mixins?
Mixins are useful because types and behaviors are different things. A scrollbar adds behavior to a window, but isn’t meaningful as a type. And a given type may have many behaviors. A window may have menus, title bars, and so on. Does each possible combination represent a new type of window? That seems silly, but we still want to be able reuse those behaviors. And this separation of type and behavior was the original motivation for mixins.
As you might expect, lots of people have implemented mixins in JavaScript, although they haven’t always called it that. The most common approach is probably to just copy one object into another. But there are limits to this approach. For example, you can’t add an event listener that way.
Mixin Functions
A more flexible approach is to use functions. In a function, we’re not limited to copying properties. We can do whatever want because a mixin is just a function that takes a type, modifies it, and returns the modified version.
The Mozilla documentation for classes demonstrates an interesting variation on this idea, using class expressions to extend the prototype chain. This automatically addresses a number of complications associated with mixins, like enumeration. However, that also means we’re adding implicit types to the prototype chain for each mixin, which was the whole thing we were trying to avoid in the first place—not to mention, a potential debugging nightmare.
Fortunately, we don’t lose anything by simply defining mixins as functions that modify a type. (For the purists in the audience, you’re right, we do introduce side-effects because we’re modifying the type. But we could avoid this by simply cloning the type first, so the principle remains the same.) Sure, we need a few helper functions, but this turns out to be an advantage.
Generic Mixins
The advantage is that, the moment we took the leap into defining mixins as functions, anything we can do with functions applies to mixins. All the fun—ahem—games like currying and composition work with mixins, too. Our helpers just become part of a library of generic mixins.
Let’s start with a simple example—a mixin that defines a render
method that is meant to be used with an DOM element. We’ll assign the element’s innerHTML
property to the result of executing a template function on the element’s data. (As usual on this blog, the examples are in CoffeeScript because I prefer it. The language semantics are almost identical to JavaScript.)
render = (type) ->
type::render = -> @innerHTML = @template @dataset
We can use this to add such a method to any HTML element:
# add render method to our custom elementt
render MarkdownEditor
This works, but we can do better. To begin with, our render
function is enumerable, which we don’t want. Let’s define another generic mixin for defining arbitrary methods:
methods = (definitions) ->
(type) ->
for definition in definitions
Object.defineProperty type::, name,
value: f
configurable: true
writable: true
We can define our render
mixin using our new methods
mixin:
render = methods render: -> @innerHTML = @template @dataset
Composing Mixins
We can define similar helpers for all the things we may want to do with a type: properties, static (class-level) methods and properties, and so on. We can even compose mixins, just like we would do with ordinary functions. The only change we need to make is to ensure we return the type.
Of course, we can just do that in the code explicitly. But it’s easy to forget and a little harder to scan. A nice little higher-order function can help:
tee = (f) -> (first, rest...) ->
f first, rest...
first
We can use it like this:
methods = (definitions) ->
tee (type) ->
for definition in definitions
Object.defineProperty type::, name,
value: f
configurable: true
writable: true
We can build up sophisticated mixins this way. For example, here’s a generic mixin that defines a setter for a given property and fires a change
event whenever it’s updated (pipe
is defined in Fairmont Core and composes its arguments in reverse order, while property
is a generic mixin, similar to our methods
generic mixin, but for properties):
observe = (description, handler) ->
pipe do ->
for key, value of description
property [key]: do (value) ->
get: -> value
set: (x) ->
value = x
handler.call @, value
This composes mixins returned by the generic mixin property
. In Play, we use this to create a composable
mixin, which allows us to define a form of composition over Web Components:
composable = pipe [
observe value: "", -> @dispatch "change"
method pipe: (to) -> @on change: -> to.value = @value
]
Here’s another that adds diff-based rendering using VDOM (HTML
is a set of VDOM helpers and innerHTML
is the diff implementation, from diffHTML):
vdom = tee properties
html:
get: -> @shadow.innerHTML
set: do ({style, parse} = HTML) ->
(html) ->
vdom = if (isString html) then (parse html) else html
vdom.push (style @styles)
innerHTML @shadow, vdom
We can, in turn, use these to compose useful high-level combinations:
calypso = pipe [ vdom, styles, template ]
zen = pipe [ calypso, composable, autorender ]
I call these “presets,” following the recent trend of libraries like Babel, and because I am a musician (keyboards often have presets to simplify dealing with complex settings). The zen
preset gives you all the Play magic. The calypso
preset gives you the basics. Or you could define your own.
This is ultimately what allows us to write such simple component definitions:
gadget
tag: "x-greeting"
# zen preset gives us all the goodies :)
mixins: zen
template: ({value}) -> "<h1>#{value}, World!</h1>"
on: h1: click: ({target}) -> @value = "Goodbye"
Permeable Abstraction
The real magic here is that you can make your own magic. For example, what if you want to use a different VDOM implementation? Just define your a mixin for it. Play defines a few core features which are intrinsic to Web Components and everything else is a mixin. You can replace any non-core feature—like VDOM—without losing all the others.
This follows a deeper principle that I call permeable abstraction (not to be confused with leaky abstractions, which are merely incidentally permeable). Martin Fowler calls it an enabling attitude. Basically, we want to provide helpful abstractions and even infer intent (also known as “magic”) whenever possible, but not to the point where we’re inadvertently getting in the way of the developer. After all, we can’t anticipate everything.
In fact, in modern software design, it’s often impossible to imagine the contexts in which our software may be put to use. Permeable abstraction gives us the best of both worlds—we can be helpful without getting in the way. And mixins are a kind of permeable abstraction.
Mixin Events
Inheritance in JavaScript relies on super
to specialize methods. But what’s the equivalent for mixins? For example, suppose we want to ensure that all our mixins have a chance to do some initialization. If we were using inheritance, each mixin would simply call super
in it’s constructor. But since we’re not, we don’t have that option. Fortunately, we can do even better.
The trick is to define another mixin for listening for and dispatching events. The mixin equivalent of calling super
is defining a listener. This works even better than super
because we don’t need to remember to call anything and it’s more general. For example, we can expose events as part of our interface.
In Play, we created an evented
generic mixin that delegates events to the component’s Shadow Root. Any mixin added after that one can define an initialize
listener for itself without affecting other mixins. Our observe
mixin uses this same facility to dispatch a change
event, which, again, other mixins (or client code) can hook into.
I fell in love with this pattern when I first saw it thirty years ago. And I love that I can still use it today in JavaScript, in a such functional way.
Please feel free to study and borrow from the Play mixin code—and let us know what you think! Do you use similar patterns in your projects? Has your experience been good or bad?