Review of Transpilers [Part 1]

One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections. The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked.

”Metamorphosis” by Franz Kafka

Transpilation is the process of compiling source code written in one language to generate source code for a desired target language.  With the ubiquity of JavaScript used not only in almost all modern web browsers but also in a growing number of server-side applications, a new generation of transpilers that produce ECMA-compliant JavaScript have emerged to provide developers with a robust set of tools to develop efficient and elegant source-to-source solutions.

The motivation behind the transpilation process is that it accelerates the use of next-generation language syntax and language features while helping to ensure that the adoption of the language features results in browser-compatible JavaScript that meets the functional goals of applications.

In Part 1 of this series, two example transpilers are presented: TypeScript and Fable.

TypeScript

TypeScript offers not just transpilation but a language for developing more scalable, testable, and maintainable JavaScript. Currently available at version 2.6, TypeScript allows development with types, classes, and modules, interfaces, and generics. In addition to supporting ECMAScript 2015 features, the latest version of TypeScript supports additional features of ECMAScript 2016+ including the async/await features.

Code Sample 1:  A Shape Class in TypeScript with Sample Transpilation via TypeScript

TypeScript CodeTranspiled JavaScript
class Shape

{

constructor(public readonly nameOfShape: string){}

calculateArea() {};    

}

class Square extends Shape

{

readonly lengthOfSideInMeters: number;

constructor(lengthOfSideInMeters: number)

{

    super(“Square”);

    this.lengthOfSideInMeters = lengthOfSideInMeters;

}

calculateArea()

{

    super.calculateArea();

    return this.lengthOfSideInMeters * this.lengthOfSideInMeters;

}

}

class Rectangle extends Shape

{

readonly lengthInMeters: number;

readonly heightInMeters: number;

constructor(lengthInMeters: number, heightInMeters: number)

{

    super(“Rectangle”);

    this.lengthInMeters = lengthInMeters;

    this.heightInMeters = heightInMeters;

}

calculateArea()

{   

    return this.lengthInMeters * this.heightInMeters;

}

}

class Circle extends Shape

{

readonly radiusInMeters: number;

constructor(radiusInMeters: number)

{

    super(“Circle”);

    this.radiusInMeters = radiusInMeters;

}

calculateArea()

{

    super.calculateArea();

    return Math.PI * (this.radiusInMeters ** 2);

}

}

let square = new Square(4);

square.calculateArea();

console.log(“Area of square in m^2: ” + square.calculateArea());

let rectangle = new Rectangle(4, 3);

console.log(“Area of rectangle in m^2: ” + rectangle.calculateArea());

let circle = new Circle(3);

console.log(“Area of circle in m^2: ” + circle.calculateArea());

var __extends = (this && this.__extends) || (function () {

var extendStatics = Object.setPrototypeOf ||

    ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||

    function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };

return function (d, b) {

    extendStatics(d, b);

    function __() { this.constructor = d; }

    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());

};

})();

var Shape = /** @class */ (function () {

function Shape(nameOfShape) {

    this.nameOfShape = nameOfShape;

}

Shape.prototype.calculateArea = function () { };

;

return Shape;

}());

var Square = /** @class */ (function (_super) {

__extends(Square, _super);

function Square(lengthOfSideInMeters) {

    var _this = _super.call(this, “Square”) || this;

    _this.lengthOfSideInMeters = lengthOfSideInMeters;

    return _this;

}

Square.prototype.calculateArea = function () {

    _super.prototype.calculateArea.call(this);

    return this.lengthOfSideInMeters * this.lengthOfSideInMeters;

};

return Square;

}(Shape));

var Rectangle = /** @class */ (function (_super) {

__extends(Rectangle, _super);

function Rectangle(lengthInMeters, heightInMeters) {

    var _this = _super.call(this, “Rectangle”) || this;

    _this.lengthInMeters = lengthInMeters;

    _this.heightInMeters = heightInMeters;

    return _this;

}

Rectangle.prototype.calculateArea = function () {

    return this.lengthInMeters * this.heightInMeters;

};

return Rectangle;

}(Shape));

var Circle = /** @class */ (function (_super) {

__extends(Circle, _super);

function Circle(radiusInMeters) {

    var _this = _super.call(this, “Circle”) || this;

    _this.radiusInMeters = radiusInMeters;

    return _this;

}

Circle.prototype.calculateArea = function () {

    _super.prototype.calculateArea.call(this);

    return Math.PI * (Math.pow(this.radiusInMeters, 2));

};

return Circle;

}(Shape));

var square = new Square(4);

square.calculateArea();

console.log(“Area of square in m^2: ” + square.calculateArea());

var rectangle = new Rectangle(4, 3);

console.log(“Area of rectangle in m^2: ” + rectangle.calculateArea());

var circle = new Circle(3);

console.log(“Area of circle in m^2: ” + circle.calculateArea());

Fable

Targeting functional programmers, and built using Babel (more on Babel next time!), Fable provides transpilation from F# to JavaScript. Fable provides F# developers with type inference, pattern matching, and language-enforced immutability principles that functional programmers love about the F# language with transpilation to ECMAScript 2015-compliant JavaScript.

Consider the following F# code and the resulting transpiled JavaScript code.

Code Sample 2:  A Shape Type in F# with Sample Transpilation via Fable

F# CodeTranspiled JavaScript
let Pi = 3.141592653589

type Shape =

  | Square of float

  | Rectangle of float * float

  | Circle of float

 

let square = Square 16.0

let rectangle = Rectangle(10.0, 2.0)

let circle = Circle 5.0

let areaOfShape (shape : Shape) =

  match shape with

  | Square side -> (side ** 2.0)

  | Rectangle(width, height) -> (width * height)

  | Circle radius -> Pi * (radius ** 2.0)

 

areaOfShape(circle)

|> printfn “%A”

import { setType } from “fable-core/Symbol”;

import _Symbol from “fable-core/Symbol”;

import { compareUnions, equals } from “fable-core/Util”;

import { printf, toConsole } from “fable-core/String”;

export const Pi = 3.141592653589;

export class Shape {

constructor(tag, data) {

  this.tag = tag | 0;

  this.data = data;

}

[_Symbol.reflection]() {

  return {

    type: “Test.Shape”,

    interfaces: [“FSharpUnion”, “System.IEquatable”, “System.IComparable”],

    cases: [[“Square”, “number”], [“Rectangle”, “number”, “number”], [“Circle”, “number”]]

  };

}

Equals(other) {

  return this === other || this.tag === other.tag && equals(this.data, other.data);

}

CompareTo(other) {

  return compareUnions(this, other) | 0;

}

}

setType(“Test.Shape”, Shape);

export const square = new Shape(0, 16);

export const rectangle = new Shape(1, [10, 2]);

export const circle = new Shape(2, 5);

export function areaOfShape(shape) {

if (shape.tag === 1) {

  return shape.data[0] * shape.data[1];

} else if (shape.tag === 2) {

  return Pi * Math.pow(shape.data, 2);

} else {

  return Math.pow(shape.data, 2);

}

}

toConsole(printf(“%A”))(areaOfShape(circle));

 

It’s not all graphic design and wordplay in the agency world. We write code, too. #WeSpeakComputer #AgencyLife

Share this:

Subscribe

Read more about Technology