Functional Mixins In JavaScript

Clearly superior to classic inheritance.

(Photo credit: Shari’s Berries.)

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...

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
 @, 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
    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:

  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?