"JS-CLOS"
This is a brief introduction to JS-CLOS. JS-CLOS is a JavaScript library that features multiple inheritance, multiple dispatching and multiple constructors. Multiplicating everything, you get multiple happinesses ;)
JS-CLOS
What is CLOS in the first place?
Common Lisp Object System (CLOS) is a way to do OOP in Lisp. In CLOS, objects are basically just mutable hash-tables holding values. Methods are not included in these hash-tables. Instead, what's called methods in CLOS are implemented as generic functions. Generic functions are functions that work on multiple classes, a combination of them.
Heres a sample code in Tklos, a variant of CLOS.
;; a class representing instruments
(define-class <instrument> () ())
;; flute inherits from <instrument>
(define-class <flute> (<instrument>) ())
;; musicians have a name and the instrument they play
(define-class <musician> ()
((instrument :init-keyword :instrument)
(name :init-keyword :name)))
;; flute players inherits from <musician> and they play the flute
(define-class <flute-player> (<musician>)
((instrument :init-value (make <flute>))))
;; generic function can-play?
(define-generic can-play?)
;; a flute-player can play the flute
(define-method can-play? ((fp <flute-player>) (f <flute>))
#t)
;; a flute-player can not play other instruments
(define-method can-play? ((fp <flute-player>) (i <instrument>))
#f)
;; a musician can play an instrument when the musician has the instrument
(define-method can-play? ((m <musician>) (i <instrument>))
(is-a? (slot-ref m 'instrument) i))
Implementing it in JS
I found a repository named JS-CLOS on github that someone in Russia has abandoned a year ago unfinished. I forked and made some commits to it. I ended up rewriting almost every line and believe I've come up with a usable solution. Here's what's equivalent to the code above in JS-CLOS. The code is written in CoffeeScript.
## a class representing instruments
instrument = define_class()
## flute inherits from instrument
flute = define_class [instrument]
## musicians have a name and an instrument they play
musician = define_class [], (x) ->
(slot_exists x, 'instrument', instrument) && (slot_exists x, 'name', 'string')
## flute players inherits from musician and they play the flute
flute_player = define_class [musician], (x) ->
x.instrument = make flute
true
## generic function can_play
can_play = define_generic()
## a flute_player can play the flute
define_method can_play, [flute_player, flute], -> true
## a flute-player can not play other instruments
define_method can_play, [flute_player, instrument], -> false
## a musician can play an instrument when the musician has the instrument
define_method can_play, [musician, instrument], (m, i) ->
is_a m.instrument, i
Extension for Functional Programming
I extended JS-CLOS with some features to support functional style programming. One is for dispatching methods on equality of values. This allows us to do something like this:
fib = define_generic()
define_method fib, [0], -> 0
define_method fib, [1], -> 1
define_method fib, ['number'], (n) ->
fib (n - 1) + fib (n - 2)
Another such feature is the support for multiple constructors. With this, you can write ML style datatypes.
color = define_class()
Red = define_constructor color
Green = define_constructor color
Blue = define_constructor color
WebColor = define_constructor color, (str) ->
make(color, {webCol:str})
is_a Red(), color
#=>true
is_a (WebColor '#ff0033'), color
#=>true
More sophisticated examples will show up in my gist. Follow me if you are interested.