1 of 45

Keeping your code in Check

Michael Bolin

2 of 45

Make it hard to do the wrong thing.

3 of 45

Static typing benefits

  • Correctness
  • Readability
  • Tools

4 of 45

Static typing myths

  • Requires code to be object-oriented.
  • Requires code to be more verbose.

5 of 45

Classes of type systems

  • Dynamic typing (unchecked)
  • Explicit typing
  • Implicit typing
  • Optional typing

6 of 45

Dynamic typing

Languages: JavaScript, Python

function doQuack(duck) {

if (duck && duck.quack) duck.quack();

}

7 of 45

Dynamic typing

Languages: JavaScript, Python

function doQuack(duck) {

if (duck && duck.quack) duck.quack();

}

var duck1 = new Duck();

var duck2 = {

quack: function() {console.log('quack');}

};

var duck3 = new String('duck');

duck3.quack = duck2.quack;

8 of 45

Explicit typing

Languages: Java, C

String name = "mloc.js";

int year = 2013;

Duck duck = new Duck();

public void doQuack(Duck duck) {

duck.quack();

}

9 of 45

Implicit typing

Languages: OCaml, Haskell

$ let name = "mloc.js";;

10 of 45

Implicit typing

Languages: OCaml, Haskell

$ let name = "mloc.js";;

val name : string = "mloc.js"

11 of 45

Implicit typing

Languages: OCaml, Haskell

$ let name = "mloc.js";;

val name : string = "mloc.js"

$ let year = 2013;;

val year : int = 2013

12 of 45

Implicit typing

Languages: OCaml, Haskell

$ let name = "mloc.js";;

val name : string = "mloc.js"

$ let year = 2013;;

val year : int = 2013

$ let conf = (name, year);;

val conf : string * int = ("mloc.js", 2013)

13 of 45

Implicit typing: mind === blown

$ let print_conf c =

let (name, year) = c in

sprintf "%s is in %d" name year

;;

14 of 45

Implicit typing: mind === blown

$ let print_conf c =

let (name, year) = c in

sprintf "%s is in %d" name year

;;

val print_conf : string * int -> string = <fun>

15 of 45

Implicit typing: mind === blown

$ let print_conf c =

let (name, year) = c in

sprintf "%s is in %d" name year

;;

val print_conf : string * int -> string = <fun>

$ print_conf conf;;

- : string = "mloc.js is in 2013"

16 of 45

Implicit typing: mind === blown

$ let print_conf c =

let (name, year) = c in

sprintf "%s is in %d" name year

;;

val print_conf : string * int -> string = <fun>

$ print_conf conf;;

- : string = "mloc.js is in 2013"

$ print_conf ("mloc.js", "2013");;

Error: This expression has type string

but an expression was expected of type int

17 of 45

Algebraic data types

Only create values for valid states:

type bool = true | false;;

18 of 45

Algebraic data types

Only create values for valid states:

type bool = true | false;;

type checkBoxState = Checked

| Unchecked

| Indeterminate;;

19 of 45

Algebraic data types

Only create values for valid states:

type bool = true | false;;

type checkBoxState = Checked

| Unchecked

| Indeterminate;;

As compared to using two booleans:

boolean checked;

boolean indeterminate;

20 of 45

Algebraic data types:

Never encounter an NPE again

Think: enums with satellite data.

Imagine getElementById returns this type:

type optionalElement = Some of Element.t

| None;;

21 of 45

Algebraic data types:

Never encounter an NPE again

Think: enums with satellite data.

Imagine getElementById returns this type:

type optionalElement = Some of Element.t

| None;;

let getElementById_exn id =

match getElementById id with

| Some e -> e

| None -> raise Not_found

;;

val getElementById_exn : string -> Element.t

22 of 45

Optional Typing

Google Closure

Pure JavaScript with annotations.

TypeScript

Superset of JavaScript with annotations.

Dart

New language designed to compile to JavaScript.

23 of 45

Optional Typing: Google Closure

/**

* @param {!Duck} duck

* @return {string}

*/

function doQuack(duck) {

duck.quack();

return duck.name();

}

24 of 45

Optional Typing: Google Closure

/**

* @param {!Duck} duck

* @return {string}

*/

function doQuack(duck) {

duck.quack();

return duck.name();

}

var name = 'mloc.js';

doQuack(name); // type mismatch

doQuack(null); // type mismatch

name = 2013; // OK

25 of 45

Optional Typing: Google Closure

function doQuack(duck) {

duck.quack();

return duck.name();

}

var name = 'mloc.js';

doQuack(name); // OK

doQuack(null); // OK

name = 2013; // OK

26 of 45

Optional Typing: TypeScript

function doQuack(duck : Duck) : string {

duck.quack();

return duck.name();

}

27 of 45

Optional Typing: TypeScript

function doQuack(duck : Duck) : string {

duck.quack();

return duck.name();

}

var name = 'mloc.js';

var year = 2013;

var duck = new Duck();

doQuack(name); // signature mismatch

name = doQuack(null); // OK

name = year; // cannot convert

28 of 45

Optional Typing: TypeScript

function doQuack(duck) {

duck.quack();

return duck.name();

}

var name = 'mloc.js';

var year = 2013;

var duck = new Duck();

doQuack(name); // OK

name = doQuack(null); // OK

name = year; // cannot convert

29 of 45

Consider the following API

function geocode(address, callback) {

// ...

}

30 of 45

geocode() API in Google Closure

/**

* @typedef {{lat:number, lng:number}}

*/

var LatLng;

/**

* @param {string} address

* @param {function(?LatLng)} callback

*/

function geocode(address, callback) {

// ...

}

31 of 45

geocode() API in TypeScript

interface LatLng {

lat : number;

lng : number;

}

function geocode(

address : string,

callback : (latLng : LatLng) => void) {

// ...

}

32 of 45

Optimizations with Google Closure

33 of 45

Optimizations with Google Closure:

Property Renaming

/* Library code. */

Bag.prototype.add = function(e) {

this.elements.push(e);

};

34 of 45

Optimizations with Google Closure:

Property Renaming

/* Library code. */

Bag.prototype.add = function(e) {

this.elements.push(e);

};

/* Node JS API */

domain.create().add(emitter);

35 of 45

Optimizations with Google Closure:

Property Renaming

/* Library code. */

Bag.prototype.add = function(e) {

this.elements.push(e);

};

/* Node JS API */

domain.create().add(emitter);

/* Can we minify this? */

obj.add(arg);

36 of 45

Optimizations with Google Closure:

Property Renaming

/* Library code AFTER PROPERTY RENAMING. */

Bag.prototype.a = function(e) {

// elements has been renamed to b.

this.b.push(e);

};

37 of 45

Optimizations with Google Closure:

Property Renaming

/* Library code AFTER PROPERTY RENAMING. */

Bag.prototype.a = function(e) {

// elements has been renamed to b.

this.b.push(e);

};

/** @param {!Bag} obj */

var add42ToBag = function(obj) {

// was obj.add(42);

obj.a(42);

};

38 of 45

Optimizations with Google Closure:

Inlining

/* Library code AFTER PROPERTY RENAMING. */

Bag.prototype.a = function(e) {

this.b.push(e);

};

39 of 45

Optimizations with Google Closure:

Inlining

/* Library code AFTER PROPERTY RENAMING. */

Bag.prototype.a = function(e) {

this.b.push(e);

};

/* Business logic AFTER INLINING. */

/** @param {!Bag} obj */

var add42ToBag = function(obj) {

// was obj.a(42);

obj.b.push(42);

};

40 of 45

Optimizations with Google Closure:

Dead Code Elimination

Constant propagation:

if (goog.userAgent.IE) {� // IE-specific-workaround�}

41 of 45

Optimizations with Google Closure:

Dead Code Elimination

Constant propagation:

if (goog.userAgent.IE) {� // IE-specific-workaround�}

Unused library functions:

dom.createNode = function() { /*...*/ };

dom.createElement = function() { /*...*/ };

dom.createFragment = function() { /*...*/ };

42 of 45

Optimizations with Google Closure:

Modules

A B C D E

A

B

C

D

E

43 of 45

Optimizations with Google Closure:

Effects on code size

4380KB

Original JavaScript

709KB

Closure Compiler

215KB

Module Partitioning

73KB

gzip

44 of 45

Conclusion

Static typing

  • Makes it harder to do the wrong thing.
  • Makes code more readable.
  • Makes code more analyzable.

45 of 45

Thanks!