packages feed

infernu-0.0.0.0: README.md

# Infernu

Static type inference for JavaScript.

See the [intro blog post](https://noamlewis.wordpress.com/2015/01/20/introducing-sjs-a-type-inferer-and-checker-for-javascript/) for a short discussion comparing infernu to **other type checkers**.

*(Formerly known as Inferno / Safe JS / SJS)*

**Features:**

* Full type inference: no type annotations necessary.
* Parametric polymorphism (aka "generics"), based on Hindley-Milner type inference.
* Row-type polymorphism, otherwise known as "static duck typing".
* Simple type classes (which allow for example correct support of JS `+` and `[]` operators).
* Recursive types for true representation of object-oriented methods.
* Correct handling of JS's `this` dynamic scoping rules.

For more information see [Infernu's Type System](docs/type-system.md).

## Installation

### Quick and Dirty

    git clone git@github.com:sinelaw/infernu.git
    cd infernu/
    cabal install

Usage: see `infernu --help`
    
Quick example usage:

    echo 'function getLength(x) { return x.length; }' > getLength.js

    infernu getLength.js

Output:

```javascript
    //       getLength : a.({length: b, ..c} -> b)
    function getLength(x) { return x.length; }
```


### A bit more detailed instructions

1. Install Haskell's **cabal** package manager. See [Haskell.org](https://www.haskell.org/downloads) for some installation options. On ubuntu, I recommend using [Herbert V. Riedel's ppa](https://launchpad.net/~hvr/+archive/ubuntu/ghc).
2. Clone this repository.

Then run:

    cabal update
    cd infernu
    cabal install

The `infernu` executable will be installed to your `~/.cabal/bin`. You may want to add it to your `PATH`.

If you have trouble in the last command due to package incompatibilities, use a **cabal sandbox**:

    cd infernu
    cabal sandbox init
    cabal install

The `infernu` executable will be placed in `infernu/.cabal-sandbox/bin`



## Examples

### Basic

JavaScript:

	var num = 2;
	var arrNums = [num, num];

Infernu infers:

    //  num : Number
    //  arrNums : [Number]

That is, an array of numbers.

Objects:

	var obj = { something: 'hi', value: num };

Inferred type:

    //  obj : {something: String,
               value: Number}

That is, an object with two properties: 'something', of type string, and 'value' of type number.

### Functions and `this`

In JS, `this` is one truly awful part. `this` is a dynamically scoped variable that takes on values depending on how the current function was invoked. Infernu knows about this (pun intended) and infers types for functions indicating what `this` must be.

For example:

	function useThisData() {
		return this.data + 3;
	}

Infernu infers:

    //       useThisData : {data: Number, ..a}.(() -> Number)

In words: a function which expects `this` to be an object with at least one property, "data" of type `Number`. It takes no arguments (hence the empty `()`). It returns a `Number`.

If we call a function that needs `this` incorrectly, Infernu will be angry:

    Error: Could not unify:
        {data: Number, ..a}
      With:
        Undefined

Because we called `useThisData` without a preceding object property access (e.g. `obj.useThisData`), it will get `undefined` for `this`. Infernu is telling us that our expected type for `this` is not unifiable with the type `undefined`.

### Polymorphism

Given the following function:

    function makeData(x) {
	    return {data: x};
	}

Infernu infers the following type:

    a.(b -> {data: b})

In words: A function that takes anything for its `this`, and an argument of any type `b`. It returns an object containing a single field, `data` of the same type `b` as the argument.

### Row-type polymorphism (static duck typing)

Given the following function:

    function getData(obj) {
		return obj.data;
	}

Infernu infers:

    h.({data: i, ..j} -> i)

In words: a function taking any type `h` for `this`, and a parameter that contains **at least one property**, named "data" that has some type `i` (could be any type). The function returns the same type `i` as the data property.


### Type Classes

See [here](docs/type-system.md#type-classes) for more about Infernu's type classes.

The basic example is for the `+` operator:

    function add(x,y) { return x + y; }

The type for `add` is inferred to be:

    //       add : Plus b => a.((b, b) -> b)

Meaning: given any type `a` that is an instance of the `Plus` type class, the function takes two `a`s and returns an `a`.

The two instances of `Plus` currently defined are the types `Number` and `String`.



------------

## TODO

- [ ] consider adding sum types with guards as pattern matchers. required because some functions, like array index access, can return 'undefined' (e.g. if index is out of range)
- [ ] allow empty var decls (use first assignment as starting point for types) - how to prevent uninitialized variable issues?
- [ ] allow defining constructor-object properties using the notation `obj.prototype.something = ...`
- [ ] find a reasonable solution for optional parameters - perhaps using an implicit "Maybe"-like type or implicit type unions, and require guards?
- [ ] when concluding that two recursive types are equivalent, use that information to simplify the resulting types (perhaps using the simpler of the two everywhere)
- [ ] BUG: top-level type of naked object `{a:3}` isn't shown unless it is wrapped in a paren `({a:3})`.
- [ ] support `arguments` (a tuple?) and function `bind`
- [ ] Should we treat functions as objects with properties? the only properties they have are: length (very weird! we might as well leave it out), and call/bind/apply (which need special handling)

### Future

- [ ] type annotations
- [ ] add support for CommonJS modules
- [ ] deal better with inferred polymorphic object properties - requires full rank-n unification

<!--  LocalWords:  JS polymorphism Hindley Milner JS's Equi num arrNums Number String getData
 -->
<!--  LocalWords:  useThisData Undefined unifiable makeData TODO decls paren CommonJS
 -->