What JavaScript taught me about programming in Scala

James Earl Douglas

August 14, 2015

Structure of this talk

What JavaScript taught me about programming in Scala

A series of lessons that take ideas from JavaScript and use them to improve Scala development.

Context of this talk

What JavaScript taught me about programming in Scala

These are subjective and biased, and I only recommend them to an audience of me.

A few ways to write JavaScript

8ball, acorn, ADsafe, Agda, Alasql, Amber, ANTLR 3, asm.js, AtScript, Babel, Bck2Brwsr, bdParse, Bennu, BicaJVM, BiwaScheme, Blade, Blockly, Blue Storm, Bonsai-C, Bridge.NET, browserl, Brozula, Brython, Bulbul, burrito, Caffeine, Caja, Canopy, Ceylon, Cheerp, Chlorinejs, CirruScript, Clamato, ClojureJS, ClojureScript, Closure Compiler, Closure Compiler AST Documentation, Clue, CobolScript, Coco, CoffeeScript, CoffeeScript II: The Wrath of Khan, CokeScript, ColaScript, ColdRuby, ContextJS, Continuation.js, Contracts.coffee, Ć Programming Language, Cruiser.Parse, Daonode, Dart, defrac, Dogescript, Dojo Secure, Doppio, DotNetWebToolkit, Dragome SDK, DuoCode, E, Earl Grey, ecma-ast, EcmaScript 5 Parser (es-lab), EcmaScript 5 Parser (qfox), EdgeLisp, Elevate Web Builder, Elm, EmberScript, Emscripten, ErlyJS, escodegen, esmangle, Esprima, Este.js, estraverse, ESTree Specification, falafel, Fantom, Fargo, fay, FBJS, Flapjax, Flow, forml, Fun, FunScript, Gatekeeper, ghcjs, Gnusto, Go2js, GopherJS, GorillaScript, GrooScript, GWT, Ham, haste, Haxe, heap.coffee, Hodor, Hot Cocoa Lisp, Hot Cocoa Lisp, HotRuby, IcedCoffeeScript, Idris, Illumination, j2js, Jacaranda, Jack, jangaroo, Java2Script, JavaScript++, JavaScript Shaper, JavaScript types, JEnglish, jison, jisp, jisp, jLang, jmacro, Jotlin, js--, jsc, JScala, JS/CC, JScriptSuite, jsForth, JSGLR, jshaskell, JSIL, js.js, JsMaker, Js_of_ocaml, JsonML AST, jsparse, JSPipe, js-scala, JSX, JSX, jwacs, Kaffeine, Kal, Khepri, ki, ki, Kotlin, lambdascript, languagejs, Latte JS, LispyScript, LispyScript, LiteScript, Little Smallscript, Lively Kernel, LLJS, Local.js, Logo Interpreter, lua.js, LuvvieScript, LZX (Laszlo XML), Maeda Block, maja, Mandreel, Mascara, Meemoo, MetaCoffee, Microsoft Web Sandbox, MileScript, mobl, mobl, Moby Scheme, Mochiscript, Moescript, Monkey, MoonScript, move, Narcissus, NarrativeJS, nconc, nearley, NemerleWeb, Netjs, NGN APL, Nim, node-jvm, NoFlo, NS Basic/App Studio, Objective-J, O'Browser, Ocamljs, Oia, oj, OMeta/JS, Opa, Opal, Oppo, Outlet, p2js, p4js, Parenscript, Parsec CoffeeScript, parse-js, PEG.js, Perlito, php.js, phype, Pit, Plaid, pogoscript, Prefix, Processing, promiseLand, PureScript, PyCow, Pygmy, Pyjaco, Pyjamas, Pyjs, PyPyJS, pythonscript, PythonScript, PyvaScript, PYXC-PJ, qb.js, Quby, Quixe, QWT, Ralph, RapydScript, rb2js, Reb2Static, Red, RedScript, reflect.js, ReParse, Restrict Mode, rocambole, Roy, RPN, Ruby2JS, RubyJS, Rusthon, Saltarelle, scalagwt, Scala.js, scheme2js, Script#, ScriptBlocks, Scriptjure, SharpKit, Shen, Shift JavaScript AST Specification, Sibilant, Sibilant, Silver Smalltalk, Six, Skulpt, Smart Mobile Studio, SMLtoJs, Snap, SourceMap, Spider, Spiderbasic, SpiderMonkey Parser API, Spock, sqld3, sql-parser, SqueakJS, STIP.js, StratifiedJS, Streamline.js, Stripes, Strongly-Typed JavaScript (STJS), Sugar, sweet.js, Swym, Taijilang, TameJS, TARDISgo, TeaVM, TeJaS, TIScript, TLC, ToffeeScript, Topaz, Traceur, treehugger, Typecast.js, TypeScript, Uberscript, UglifyJS, UHC, uilang, uniter, Ur, Waterbear, WebAssembly, WebSharper, wForth, Whalesong, Wind.js, wisp, wisp, WootzJs, Wortel, XLCC, YHC, Zeon, ZeParser

Colloquial Scala

Hey, this looks just like Java/Haskell/ML/BASIC!

-- Java/Haskell/ML/BASIC developer

Scala language features aplenty

SIP-18 imposes language modularization via imports, which influence coding style.

import language.macros
import language.dynamics
import language.postfixOps
import language.reflectiveCalls
import language.implicitConversions
import language.higherKinds
import language.existentials

Lesson learned: Choose a dialect

Consensus is unlikely to happen on its own.

Constructors in JavaScript

function RichNumber(num) {
  this.num = num;
  this.squared = num * num;
}
(new RichNumber(7)).squared; // 49

Object literals in JavaScript

function richNumber(num) {
  return {
    num: num,
    squared: num * num,
  };
}
richNumber(7).squared; // 49

Implicit conversions in Scala

trait Squarable[A] {
  def squared: A
}
implicit class SquarableInt(x: Int) extends Squarable[Int] {
  def squared: Int = x * x
}
7.squared // 49

Type classes in Scala

trait Squarer[A] {
  def square(x: A): A
}
implicit object SquarerInt extends Squarer[Int] {
  def square(x: Int): Int = x * x
}
object Mathz {
  def square[A: Squarer](x: A): A =
    implicitly[Squarer[A]].square(x)
}
Mathz.square(7) // 49

Lesson learned: Choose a set of patterns

Again, consensus is unlikely to happen on its own.

Node.js modules

var square = function (x) {
  return x * x;
}
module.exports.square = square;

Web browser JavaScript modules

var mathz;
(function (mathz) {
  var square = function (x) {
    return x * x;
  }
  mathz.square = square;
})(mathz || (mathz = {}));

Modular Scala

// package mathz {
// trait Mathz {
// abstract class Mathz {
// class Mathz {
object Mathz {
  def square(x: Int): Int = x * x
}

Module layout depends on environmental pressures:

Lesson learned: Pick an ecosystem

JavaScript code quality

function helloWorld() {
  return 'Hello, World!';
}
helloworld();

JSHint catches the typo:

One undefined variable
16 helloworld
One unused variable
12 helloWorld

Scala code quality

Lesson learned: Do static analysis

JavaScript function signatures

What does this function do?

function foo(x) {
  // implementation hidden
}

¯\_(ツ)_/¯

Scala function signatures

What about this one?

def foo(x: Int): Int = {
  // implementation hidden
}

Maybe it doubles x.

Perhaps it returns 42.

Parameterization in Scala

How about now?

def foo[A](x: A): A = {
  // implementation hidden
}

foo(x) can only possibly return x.

def foo[A](x: A): A = identity(x)

Lesson learned: Use generics

Testing in JavaScript

JavaScript projects rely heavily on testing to assert correctness.

Testing in Scala

Even with the compiler, tests are crucial in Scala.

Reusability of tests

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you change existing tests
  2. MINOR version when you add new tests
  3. PATCH version when you don't change the tests

Lesson learned: Develop from tests

Mutability in JavaScript

Unexpected things can happen.

function nextYear(date) {
  return {
    day: date.day,
    month: date.month,
    year: date.year++,
  };
}
var d = { day: 14, month: 8, year: 2015 };
nextYear(d) === nextYear(d) // false

nextYear(x) is not referentially transparent.

Immutability in Scala

case class Date(day: Int, month: Int, year: Int)
def nextYear(date: Date): Date = {
  date.copy(year = date.year + 1)
}
val d = Date(day = 14, month = 8, year = 2015)
nextYear(d) == nextYear(d) // true

nextYear(x) can't change the value of x.

Shared state in Node.js

Single-threadedness makes things easy.

var hitCount = 0;
function service(request, response) {
  hitCount = hitCount + 1;
  // ...
}

This would be unsafe with multiple threads.

Shared State in Scala

case class State[A,S](run: S => (A,S)) {
  def map[B](g: A => B): State[B,S] =
    State({ s =>
      val (a,s2) = run(s)
      (g(a),s2)
    })
  def flatMap[B](g: A => State[B,S]): State[B,S] =
    State({ s =>
      val (a,s2) = run(s)
      g(a).run(s2)
    })
}

You could do this in JavaScript too, but holy type system, Batman.

Lesson learned: Avoid mutability

Scala configuration via properties

# config.properties
logLevel=DEBUG
case class Config(logLevel: String)
lazy val config: Config = {
  val props = new java.util.Properties()
  props.load(new java.io.FileInputStream("config.properties"))
  Config(logLevel = props.getProperty("logLevel"))
}

Node.js configuration via import

// config.js
module.exports = {
  logLevel: 'DEBUG',
};
var config = require('./config.js');

This could be done similarly with a YAML configuration file.

Scala configuration via Eval

// config.scala
val l: Level = DEBUG
Config(logLevel = l)
sealed trait Level
case object DEBUG extends Level
case class Config(logLevel: Level)
val config: Config = com.twitter.util.Eval("config.scala")

Real-world configuration via code

Lesson learned: Use code as configuration

Node.js library dependency sources

Node.js projects automatically include the source code of their dependencies.

node_modules/mocha/lib/test.js:

exports.escape = function(html){
  return String(html)
    .replace(/&/g, '&')
    .replace(/"/g, '"')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
};

sbt library dependencies

Lesson learned: Publish documentation

Node.js off the line

console.log('Hello, world!')
$ time node hello.js
Hello, world!

real    0m0.075s
user    0m0.061s
sys     0m0.014s

Scala needs a rolling start

object Main extends App {
  println("Hello, world!")
}
$ time sbt run
Hello, world!

real    0m6.488s
user    0m10.225s
sys     0m0.980s

Lesson learned: Work around the JVM's intertia

Language popularity on GitHub

Source: githut.info

Language support

99% of all Web users can run JavaScript code.

Source: YDN

Lesson learned: Don't use Scala for everything

npm deployment to npmjs.com

First-time users will npm adduser or npm login.

After that, deployment is roughly one step.

$ npm publish

sbt deployment to Sonatype

For new and existing users, deployment is roughly eleventy billion steps.

Lesson learned: Streamline deployment

Questions