Skip to content
Shane Brinkman-Davis Delamore edited this page Mar 29, 2018 · 117 revisions

logo Build Status NPM version

Why CaffeineScript and not JavaScript, TypeScript, CoffeeScript or JSX?

I believe less is more. It is the first principle of design and foundation of just about every good programming practice: DRY, KISS, ZEN (YAGNI), single responsibility principle, interface segregation, etc... That's why I wrote CaffeineScript. This infographic shows the result:

logo

CaffeineScript, with the help of ArtSuite, takes 3x less code to write the complete implementation of Tic-Tac-Toe. In general, CaffeineScript is 2.5-3 times more efficient than JSX, TypeScript or JavaScript. Imagine seeing 3x as much code on screen at once. Imagine having 3x less code to write. Imagine having 3x less code to change when you need to refactor. Imagine having 3x less code to debug.

TicTacToe source:

TicTacToe live:

Of course, just because CaffeineScript can reduce code-size by 3x doesn't mean you'll be a full 3x faster. CaffeineScript cannot reduce the essential logic of your code. However, it dramatically cuts back the written noise, so you can see your core logic clearly.

Read more about programming with less:

JavaScript's Golden Heart

JavaScript has a golden heart. At its core, it is a powerful hybrid of object-oriented, functional and dynamically-typed languages. Combined with modern runtimes, world-class garbage collectors and JIT compilers, JavaScript can be a surprisingly good platform for building great applications.

However, JavaScript has an ailing body. Even now, in 2018, JavaScript has major syntax and semantic problems which make it error prone and dangerous. JavaScript's syntax is excessively verbose. Code readability matters, especially for large projects, and JavaScript's syntax is the least readable of any major language. This is easy to fix, and the rewards can be huge.

Further, JavaScript has many dangerous holes in its semantics which can introduce subtle, hard to find bugs. They include accidental globals, weakly-typed truth, weakly-typed equality and other weakly-typed operators. Thankfully, the core JavaScript semantics are good, and these auxiliary semantics can be partially or fully fixed without changing JavaScript.

CaffeineScript's goal is to maximize productivity for JavaScript-based projects. It does this primarily by minimizing syntax and patching the holes in JavaScript's semantics.

Inspired by CoffeeScript

I love CoffeeScript. I love the visual blocking of bracket-less blocks. As I used it over the years, though, I started noting it wasn't very consistent. I was frustrated by all-to-frequent edge cases, where I had to revert to using brackets. For example, all array literals still require brackets ([]) in CoffeeScript. Eventually I couldn't stand it anymore. I set out to write a language that could parse bracketless blocks consistently.

If you love CoffeeScript, or even if you liked some parts but others drove you crazy, I've got an awesome language for you.

Introducing CaffeineScript

CaffeineScript is an open-source programming language that compiles to JavaScript. The goal is to minimize total effort, over a product's lifetime, for the entire team. Design thinking is essential for achieving that goal. That means user-experience and graphic design are as important as computer-science and software engineering. A well-designed language makes code more beautiful, programming more fun, and, ultimately, lets us get more done with less effort.

CaffeineScript starts where CoffeeScripts left off, fixing its shortcomings and taking a big step forward for high-productivity javascript. Two concrete examples: improved React-style programming and 90% reduced module-specific code. The result is a lean, high-level language that empowers you to get the most out of JavaScript.

Example

CaffeineScript: logo

JavaScript:

"use strict";
let Caf = require("caffeine-script-runtime");
Caf.defMod(module, () => {
  return Caf.importInvoke(
    ["FluxComponent", "Element"],
    [global, require("art-suite")],
    (FluxComponent, Element) => {
      let PlayerList;
      return (PlayerList = Caf.defClass(
        class PlayerList extends FluxComponent {},
        function(PlayerList, classSuper, instanceSuper) {
          this.subscriptions("players.currentPlayers");
          this.prototype.render = function() {
            return Element(
              { childrenLayout: "column" },
              Caf.each(
                this.currentPlayers.sort((a, b) => {
                  return b.score - a.score;
                }),
                [],
                (player, cafK, cafInto) => {
                  cafInto.push(
                    require("./PlayerLine")(player, { key: player.name })
                  );
                }
              )
            );
          };
        }
      ));
    }
  );
});

Related: ArtSuite

Live Demo

A brief, interactive slideshow written in CaffeineScript

Status: BETA

CaffeineScript is working and usable. The semantics and syntax may shift slightly as I work through the remaining bugs.

Install

npm install caffeine-script

Learn More

Contribute

More Examples

# CaffeineScript - 27 tokens and 0 must-match-tokens
import &ArtSuite

class Login extends Component

  render: ->
    Element
      TextElement
        text: :username
        size: ww: 1, hch: 1

      TextInput
        placeholder: "" enter username here
        size: ww: 1, hch: 1
// JavaScript - 73 tokens including 28 must-match-tokens
let {
  Component,
  Element,
  TextElement,
  TextInput
} = require('art-suite');

module.exports = class Login extends Component {
  render() {
    return Element(
      TextElement({
        text: 'username',
        size: {ww: 1, hch: 1}
      }),

      TextInput({
        placeholder: 'enter username here',
        size: {ww: 1, hch: 1}
      })
    );
  };
};
Implicit Array and Object literals
# CaffeineScript - 20 tokens
1d: 1 2 3 4 5 6 7 8 9

2d:
  1 2 3
  4 5 6
  7 8 9
// JavaScript - 54 tokens
{
  "1d": [1, 2, 3, 4, 5, 6, 7, 8, 9],
  "2d": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
};
# CaffeineScript - 18 tokens - 0 must-match-tokens
users =
  id: 123 username: :shanebdavis born: 1976 fullName: "" Shane Brinkman-Davis Delamore
  id: 456 username: :alanturing  born: 1912 fullName: "" Alan Turing
// JavaScript - 35 tokens - 14 must-match tokens
let users = [
  {id: 123, username: "shanebdavis", born: 1976, "Shane Brinkman-Davis Delamore"},
  {id: 456, username: "alanturing",  born: 1912, "Alan Turing"}
];
# CaffeineScript - 15 tokens - 0 must-match-tokens
nameToColor:
  red:   #f00
  green: #0f0

colorToName:
  #f00:  :red
  #0f0:  :green

style:
  fontSize: 12pt
  padding:  25px
// JavaScript - 40 tokens - 24 must-match-tokens
{
  nameToColor: { red:      "#f00", green:   "#0f0"  },
  colorToName: { "#f00":   "red",  "#0f0":  "green" },
  style:       { fontSize: "12pt", padding: "25px"  }
};
# CaffeineScript 15 tokens - 2 must-match tokens
fontProps = object value, key from allProps when /^font/.test key
// JavaScript 44 tokens - 16 must-match tokens
var fontProps = {}, key, value;

for (key in allProps) {
  value = allProps[key];
  if (/^font/.test(key)) {
    fontProps[key] = value;
  }
}

What about TypeScript?

Is there interest in TypeScript support? If so, with some help, I could add it. CaffeineScript could easily support a modified TypeScript syntax for type annotation and integration into the TypeScript universe.

More on CaffeineScript and Static Typing

CaffeineScript and CoffeeScript

I owe a debt of gratitude to Jeremy Ashkenas and the CoffeeScript community. It is my primary inspiration, and what the CaffeineScript compiler was originally written in. More on inspirations from CoffeeScript: Coming from CoffeeScript.

Clone this wiki locally