Skip to content
This repository has been archived by the owner on Feb 19, 2018. It is now read-only.

CS2 Discussion: Features: mixin #81

Closed
mikeyhew opened this issue Apr 21, 2017 · 6 comments
Closed

CS2 Discussion: Features: mixin #81

mikeyhew opened this issue Apr 21, 2017 · 6 comments

Comments

@mikeyhew
Copy link

The new ECMAScript classes lend themselves to a neat pattern for mixins. The idea is described pretty well here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Mix-ins.

A mixin is a function that takes a base class as a parameter, and returns a new class that extends it. Here is an example:

let MyMixin = Base => class MyMixin extends Base {
  constructor() {
    super()
    // do some initialization
  }

  foo() {
  }
}

You can include MyMixin in your classes like so:

class MyBase {}
class MyClass extends MyMixin(MyBase) {
  // overrides MyMixin.foo
  foo() {
    super.foo()
    // do some extra stuff
  }
}

This can be done in CoffeeScript too. What would be better, though, is if CoffeeScript supported it natively:

mixin MyMixin
  constructor: ->
    # do some initialization

class Foo extends MyBase includes MyMixin
  foo: ->
    super()
    # do some extra stuff

This could be extended to allow mixins to include each other:

mixin MyOtherMixin includes MyMixin
  ...

would compile to

let MyOtherMixin = Base => class extends MyMixin(Base) {
   ...
}

Unfortunately, this breaks backward compatibility by introducing two new keywords. includes could be replaced with with, which is already a reserved word, but that still leaves mixin, which is probably a commonly used word. I'm not sure if there's any good alternatives, and I like mixin because it's very clear about what it means.

@GeoffreyBooth
Copy link
Collaborator

I like the syntax you mentioned in the other thread:

class Bar extends SomeBaseClass with Foo, Baz

Why do we need a mixin keyword to declare a mixin? Couldn’t we declare it with class like in the MDN example you link to?

@mrmowgli
Copy link

mrmowgli commented Apr 22, 2017 via email

@mikeyhew
Copy link
Author

mikeyhew commented Apr 22, 2017

Couldn't we declare it with class like in the MDN example you linked to?

Yes. mixin Foo is just syntactic sugar for

Foo = (Base) -> class Foo extends Base

Which you can already do in CoffeeScript.

Why is mixin Foo better? Because it looks cleaner, and it promotes using this pattern, making it look just declaring a class but with a different keyword.

The benefit of mixin would be similar to the way class improved things when there was no class in vanilla JavaScript. There was a recipe for doing object-oriented programming in JavaScript, but it was complicated and error-prone, and CoffeeScript abstracted away the details and let you just write class Foo extends Bar.

Of course, you could argue that Foo = (Base) -> class Foo extends Base is less complicated and less error-prone, and you'd be right; but I think its benefits would still outweigh the cost of a new keyword - especially because any existing code that uses the word mixin would probably benefit from this addition to the language. Others may disagree.

You make a good point that class Foo extends Bar with Baz1, Baz2 could be added to the language without breaking backward compatibility 👍

@objectkit
Copy link

objectkit commented Apr 22, 2017

Mixins would be a great addition IMO... and I really like the use of the word 'with'. Once upon a time, because of collision with the ECMAScript keyword with I had thought that mimics would have been a better idea, but as with is not used and is a reserved word in CofffeeScript... well, apart from making so much (hypothetical) semantic sense...

class HttpRequest with EventDispatcher

or

class HttpService extends WebService with EventHandler, Component

a new use of the word 'with' in CoffeeScript just seems natural to my eyes. I have to admit, I'd really, really like it.

I could only assume that the mixin would be applied to the class prototype first, which would permit overrides, but it raises the question of what rules apply to overriding a mixin method?

class HttpService extends WebService with EventHandler

    # @override of the EventHandler method by HttpService here
    handleEvent: (event) ->
        if not event instanceof Thing
              # how would one delegate the mixin implementation? 
              super.handleEvent(event)
              # what about execution context?
              EventHandler::handleEvent.call @ event 
              # this magic is messy, but could be achieved 
              super.EventHandler.handleEvent event

Or would the rule be that mixin methods always override class methods?

Or would static methods be ignored or added too?

Or would another rule be that mixin methods are added in the order they are declared? That is, if two or more mixins had the same method, that each would override the others until the end of the mixin chain was reached (i.e. the last method implementation wins and is added to class prototype?

What if instantiation was required by a mixin target and used its own variables?

class X extends Y with Z

    constructor: () ->
        #invoke Y constructor
        super()
        # what about Z?
        Z.call @
        # more messy magic
        super.Z()

Don't get me wrong, I think the path proposed before this comment is a good idea and I'd like to see it happen. I think the caveat would have to be that care would have to be taken by choice and use of mixin implementations. That is, its not really a problem of the CoffeeScript language, but a language feature that can be utilised by developers with a little forethought.

@GeoffreyBooth
Copy link
Collaborator

Closing as there doesn’t appear to be much interest.

Also, classes seem to be one of the most in-flux features in ECMAScript right now. I think we should avoid introducing new sugars on top of classes for the time being, because ECMA might introduce their own versions. Some future version of ECMAScript could very well introduce an equivalent of class mixins, and if we’ve already released our own sugar for it, and the two are incompatible? Then we’d have a repeat of the mess we went through with classes and CoffeeScript 2, when we had to rework CoffeeScript 1’s classes to be output using the class keyword, and minor breaking changes resulted. We want to avoid reruns of that.

@coffeescriptbot
Copy link
Collaborator

Migrated to jashkenas/coffeescript#4974

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants