A deep dive into the inner workings of ClojureScript's analysis,
88 compilation, and runtime phases. The talk focused specifically on
89 the way that traditional JavaScript abstractions thwart code-size
90 and efficiency and how ClojureScript avoids such pitfalls. Finally,
91 I told a couple "war stories" about some of the specific challenges
92 met by the Clojure/core team during the development of the compiler
93 and runtime.
8. Primacy
of
Semantics
I also regard syntactical problems as essentially irrelevant to programming languages at
their present stage ... the urgent task in programming languages is to explore the field
of semantic possibilities.
— Christopher Strachey - “Fundamental Concepts in Programming Languages”
9. JavaScript has much in common with
Scheme.
It is a dynamic language.
It has a flexible datatype (arrays) that can
easily simulate s-expressions.
And most importantly, functions are
lambdas.
http://javascript.crockford.com/little.html
53. Primacy
of
Syntax
one could say that all semantics is being represented as syntax
... semantics has vanished entirely to be replaced with pure syntax.
— John Shutt - “Primacy of Syntax”
68. Functional Inheritance
Pattern
• Private data
• Access to super methods
• Missing new safe
69. Functional Inheritance
Pattern
var person = function(spec) {
var that = {};
that.getName = function() { var me = person({'name' : 'Fogus'});
return spec.name; me.getName()
}; //=> 'Fogus'
return that;
};
var man = function(spec) {
var that = person(spec);
var super_getName = that.superior('getName');
var me = man({'name' : 'Fogus'});
that.getName = function() { me.getName()
return "Mr. " + super_getName(); //=> 'Mr. Fogus'
};
return that;
};
70. Functional Inheritance
Pattern Dangers
• Harder to minify
• Larger memory footprint
• Cannot update instances
• Eliminates instanceof
• Mucks with Function.prototype and
Object.prototype in an unsafe way
71. Pseudo-classical
Inheritance Pattern
Person = function(name) {
this._name = name;
};
var me = new Person('Fogus');
Person.prototype.getName = function() { me.getName()
return this._name; //=> 'Fogus'
};
Man = function(name) {
Person.call(this, name);
};
inherits(Man, Person); var me = new Man('Fogus');
me.getName()
Man.prototype.getName = function() { //=> 'Mr. Fogus'
return "Mr. "
+ Man._super.getName.call(this);
};
72. Pseudo-classical
Inheritance Pattern
• Harder Easier to minify
• Larger Smaller memory footprint
• Cannot Can update instances
• Eliminates instanceof
• Mucks with Clean
Function.prototype and
Object.prototype
77. Functional Inheritance
Inlining
var person = function(spec) { var myName = me.getName();
var that = {}; var sig = signature({‘name’ : myName});
sig.toString();
that.getName = function() { //=> ‘Sent from teh Fogus iPhone’
return spec.name;
};
return that;
};
78. Functional Inheritance
Inlining
var person = function(spec) { var myName = me.getName();
var that = {}; var sig = signature({‘name’ : myName});
sig.toString();
that.getName = function() { //=> ‘Sent from teh Fogus iPhone’
return spec.name;
};
return that;
};
?
79. Functional Inheritance
Inlining
var person = function(spec) { var myName = me.getName();
var that = {}; var sig = signature({‘name’ : myName});
sig.toString();
that.getName = function() { //=> ‘Sent from teh Fogus iPhone’
return spec.name;
};
return that;
};
var sig = signature({‘name’ : me.getName()});
sig.toString();
//=> ‘Sent from teh Fogus iPhone’
81. Pseudo-classical Inlining
Person = function(name) {
this._name = name;
var myName = me.getName();
};
var sig = new Signature(myName);
sig.toString();
Person.prototype.getName = function() {
//=> ‘Sent from teh Fogus iPhone’
return this._name;
};
?
82. Pseudo-classical Inlining
Person = function(name) {
this._name = name;
var myName = me.getName();
};
var sig = new Signature(myName);
sig.toString();
Person.prototype.getName = function() {
//=> ‘Sent from teh Fogus iPhone’
return this._name;
};
var sig = new Signature(me._name);
sig.toString();
//=> ‘Sent from teh Fogus iPhone’
91. Dead-code Elimination
Intro
(function (){
(let [a 1 var a__847 = 1;
b 2 var b__848 = 2;
a b] var a__849 = b__848;
a)
return a__849;
;=> 2 })();
92. Dead-code Elimination
Intro
(function (){
(let [a 1 var a__847 = 1;
b 2 var b__848 = 2;
a b] var a__849 = b__848;
a)
return a__849;
;=> 2 })();
?
93. Dead-code Elimination
Intro
(function (){
(let [a 1 var a__847 = 1;
b 2 var b__848 = 2;
a b] var a__849 = b__848;
a)
return a__849;
;=> 2 })();
2
94. Dead-code Elimination
Intro
(function (){
(let [a 1 var a__847 = 1;
b 2 var b__848 = 2;
a b] var a__849 = b__848;
a)
return a__849;
;=> 2 })();
95. Functional Inheritance
Dead-code
var person = function(spec) {
var that = {};
var hasBeard = false;
that.getName = function() {
return spec.name;
};
return that;
};
var man = function(spec) {
var that = person(spec);
var super_getName = that.superior(‘getName’);
that.getName = function() {
return "Mr. " + super_getName();
};
return that;
};
96. Functional Inheritance
Dead-code
var person = function(spec) {
var that = {};
var hasBeard = false;
that.getName = function() {
return spec.name;
};
return that;
};
var man = function(spec) {
var that = person(spec);
All good
var super_getName = that.superior(‘getName’);
Right?
that.getName = function() {
return "Mr. " + super_getName();
};
return that;
};
97. Functional Inheritance
Dead-code
var person = function(spec) {
var that = {};
var hasBeard = false;
that.getName = function() { Cursed
return spec.name;
};
return that;
Closures!
};
var man = function(spec) {
var that = person(spec);
var super_getName = that.superior(‘getName’);
that.getName = function() {
return "Mr. " + super_getName();
};
return that;
};
106. Functional Inheritance
Updating
var person = function(spec) {
var that = {};
that.getName = function() {
return spec.name;
};
return that;
};
var me = person({'name' : 'Fogus'});
var you = person({'name' : 'Cersei'});
How can we add hasBeard to every instance
now and in the future?
107. Functional Inheritance
Updating
var person = function(spec) {
var that = {};
that.getName = function() {
return spec.name;
};
return that;
};
var me = person({'name' : 'Fogus'});
var you = person({'name' : 'Cersei'});
How can we add hasBeard to every instance
now and in the future?
One at a time... every single time
109. ClojureScript
Extending
(defprotocol Catable
(cat [this other]))
(def sound1 "Meow")
(def sound2 "Mao")
(cat sound1 sound2)
; ASPLODE!
How can we allow cat to work with every instance
now and in the future?
111. Pseudo-classical
Updating
Person = function(name) {
this._name = name;
};
Person.prototype.getName = function() {
return this._name;
};
var me = new Person(‘Fogus’);
var you = new Person(‘Cercei’);
How can we add hasBeard to every instance
now and in the future?
112. Pseudo-classical
Updating
Person = function(name) {
this._name = name;
};
Person.prototype.getName = function() {
return this._name;
};
var me = new Person(‘Fogus’);
var you = new Person(‘Cercei’);
How can we add hasBeard to every instance
now and in the future?
Person.prototype.hasBeard = function() { return false };
128. ClojureScript
applyTo
• A function stored on ClojureScript functions
• Only if they have var args
• Compiled to only deal with the args seq at
the maximum fixed arity
• This ensures that an infinite seq is not
realized
129. ClojureScript apply
(defn apply
[f arg-seq]
(if (.applyTo f)
(if (<= bounded-count fixed-arity)
(.apply f (to-array arg-seq))
(.applyTo f args))
(.apply f (to-array arg-seq)))
130. ClojureScript apply
(defn apply
[f arg-seq]
(if (.applyTo f)
(if (<= bounded-count fixed-arity)
(.apply f (to-array arg-seq))
(.applyTo f args))
(.apply f (to-array arg-seq)))
131. ClojureScript apply
(defn apply
[f arg-seq]
(if (.applyTo f)
(if (<= bounded-count fixed-arity)
(.apply f (to-array arg-seq))
(.applyTo f args))
(.apply f (to-array arg-seq)))
132. ClojureScript apply
(defn apply
[f arg-seq]
(if (.applyTo f)
(if (<= bounded-count fixed-arity)
(.apply f (to-array arg-seq))
(.applyTo f args))
(.apply f (to-array arg-seq)))
166. Don’t Tread on Me
Function.prototype.method = function(name, f) {
this.prototype[name] = f;
return this;
};
Object.method('superior', function(name) {
var that = function;
var method = that[name];
return function() {
return method.apply(that, arguments);
};
});
167. ClojureScript Gently
Caresses
Object & Function
as One Might Caress
the finest Fabergé Egg
168. Look with your eyes,
not with your hands
var inherits = function(child, parent) {
function tmp() {};
tmp.prototype = parent.prototype;
child._super = parent.prototype;
child.prototype = new tmp();
child.prototype.constructor = child;
};
169. Var Args
JavaScript ClojureScript
function stuffToArray() { (defn stuff->seq
// create an array of stuff [& stuff]
} stuff)
stuffToArray(1,2,3,4); (stuff->seq 1 2 3 4)
//=> [1,2,3,4] ;=> (1 2 3 4)
170. Var Args
JavaScript ClojureScript
function stuffToArray() { (defn stuff->seq
// create an array of stuff [& stuff]
} stuff)
stuffToArray(1,2,3,4); (stuff->seq 1 2 3 4)
//=> [1,2,3,4] ;=> (1 2 3 4)
* Really this is the only snarky language joke I&#x2019;ll say all day. \n
* This is Douglass Crockford\n* Seems like a nice guy\n* Wrote a book about using only the nice parts of JS\n
* But there is something sinister about this advice!\n
* How is JavaScript like a Russian novel?\n
\n
* Semicolons\n
* Namespacing\n
* Closure pattern\n
* LET pattern\n
* VAR\n
* privates\n
* publics\n
* And after all of that... you can add some functionality.\n
* You need to introduce thousands of characters before anything happens.\n
\n
\n
Anyone have experience with GClosure? It&#x2019;s tough to follow the conventions. I wrote every function/form in js *before* I wrote the cljs impl.\n
\n
\n
\n
* conventions conventions conventions\n* It&#x2019;s too much to think about sometimes, but people will get used to *anything*\n* But you&#x2019;ve still not yet solved the problem at hand.\n* JS with a rabid adherence to conventions is 84% as safe as Haskell!\n
* All useful semantics become syntax.\n* In JS, its syntax betrays its semantics because intent is buried in idiom, verbosity, and safety checks.\n* CLJS (and LISP) leave syntax as an open set in order to allow one to build the syntax to match the semantics.\n\n| Isn't the semantics more important the syntax?\n\ngenerally speaking, if this were true, we would not see the syntax-fixation\nthat the whole industry is built on. syntax _distinguishes_ programming\nlanguages from each other; the semantics is only a question of how much of\nthe syntax you need to implement it. e.g., if CFRONT can produce C code\nfrom C++, so can a human. syntax can help automate the expression of the\nsemantics in very important ways, or serve to frustrate the same process.\nhowever, certain operations are possible (that is, sufficiently easy) on\ncertain syntaxes, which leads me to the more specific interpretation.\n
* Let&#x2019;s talk a moment to talk about CoffeeScript\n* Enjoyed my time\n* JS the good parts -- eliminates much of the ceremony, many edge cases (while introducing some of its own)\n* But maybe we need something more...\n
Asynchronous - DOM - Constant serialization - Limited resources and slow pipes - Hostile environment\n
\n
\n
* Because you can!\n* Although there are many flavors, they tend to a common pattern...\n
* This is a namespaced JS function.\n* Nested objects\n
\n
* This leads me into a nice discussion about compression potential...\n
* What do I mean?\n* Not golfing!\n
* You can get fairly compressed in the second example, but this is indicative only of this problem, and not in the language itself.\n* CoffeeScript doesn&#x2019;t fix this. It shaves characters, but that&#x2019;s all.\n
\n
* Why so many class systems? \n* Because you can!\n* Many suffer from a lack of safety or composability\n
\n
\n
\n
\n
\n
\n
* Clojure ensures safety by...\n
* To Crockford&#x2019;s credit, his is better encapsulated.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
* functions cause complication\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
* Simple as CLJ\n
* Compiler Macros\n
* AST\n
* AST\n
\n
* Well, this is what I&#x2019;ve already talked about\n
\n
* People are already using the analyzer\n* typing, Marg, etc.\n
\n
\n
\n
* AWESOME Syntax Tree!!!\n
\n
\n
\n
* Get rid of everything but the emitter\n* Remember it takes an AST\n