Bug 947501: Uplift Add-on SDK to Firefox. r=me
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 06 Dec 2013 15:53:30 -0800
changeset 159347 9ece804de146a24dea306018fec9e3a52c7aa08b
parent 159346 e00a2572d85bb14e44e60906fb1f51fb9c7a196b
child 159348 d0f6fb31e9823db7ef48fbc969a313bf27403e1e
push id25785
push usercbook@mozilla.com
push dateSat, 07 Dec 2013 11:48:28 +0000
treeherdermozilla-central@393f409b98ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs947501
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 947501: Uplift Add-on SDK to Firefox. r=me https://github.com/mozilla/addon-sdk/compare/11344b2...dc6000a
addon-sdk/source/doc/dev-guide-source/tutorials/reusable-modules.md
addon-sdk/source/doc/module-source/sdk/lang/functional.md
addon-sdk/source/lib/sdk/event/target.js
addon-sdk/source/lib/sdk/lang/functional.js
addon-sdk/source/test/test-functional.js
--- a/addon-sdk/source/doc/dev-guide-source/tutorials/reusable-modules.md
+++ b/addon-sdk/source/doc/dev-guide-source/tutorials/reusable-modules.md
@@ -30,23 +30,23 @@ independently of your add-on, making the
 and effectively extending the SDK itself.
 
 In this tutorial we'll do exactly that with a module that exposes the
 geolocation API in Firefox.
 
 ## Using Geolocation in an Add-on ##
 
 Suppose we want to use the
-[geolocation API built into Firefox](https://developer.mozilla.org/en/using_geolocation).
+[geolocation API built into Firefox](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation).
 The SDK doesn't provide an API to access geolocation, but we can
 [access the underlying XPCOM API using `require("chrome")`](dev-guide/guides/xul-migration.html#xpcom).
 
 The following add-on adds a [button to the toolbar](dev-guide/tutorials/adding-toolbar-button.html):
 when the user clicks the button, it loads the
-[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
+[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
 object, and retrieves the user's current position:
 
     var {Cc, Ci} = require("chrome");
 
     // Implement getCurrentPosition by loading the nsIDOMGeoGeolocation
     // XPCOM object.
     function getCurrentPosition(callback) {
       var xpcomGeolocation = Cc["@mozilla.org/geolocation;1"]
@@ -83,17 +83,17 @@ Click the button, and after a short dela
 this in the console:
 
 <pre>
 info: latitude:  29.45799999
 info: longitude:  93.0785269
 </pre>
 
 So far, so good. But the geolocation guide on MDN tells us that we must
-[ask the user for permission](https://developer.mozilla.org/en/using_geolocation#Prompting_for_permission)
+[ask the user for permission](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation#Prompting_for_permission)
 before using the API.
 
 So we'll extend the add-on to include an adapted version of the code in
 that MDN page:
 
 <pre><code>
 var activeBrowserWindow = require("sdk/window/utils").getMostRecentBrowserWindow();
 var {Cc, Ci} = require("chrome");
@@ -379,9 +379,9 @@ for your package. See the
 full details. If you intend to distribute the package, this is a good place
 to add your name as the author, choose a distribution license, and so on.
 
 ## Learning More ##
 
 To see some of the modules people have already developed, see the page of
 [community-developed modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules).
 To learn how to use third-party modules in your own code, see the
-[tutorial on adding menu items](dev-guide/tutorials/adding-menus.html).
\ No newline at end of file
+[tutorial on adding menu items](dev-guide/tutorials/adding-menus.html).
--- a/addon-sdk/source/doc/module-source/sdk/lang/functional.md
+++ b/addon-sdk/source/doc/module-source/sdk/lang/functional.md
@@ -8,25 +8,27 @@ and all credits go to him and his contri
 
 <api name="method">
 @function
 Takes a function and returns a method associated with an object.
 When the method is invoked on an instance of the object, the original
 function is called. It is passed the object instance (i.e. `this`) as
 the first parameter, followed by any parameters passed into the method.
 
-    let { method } = require("sdk/lang/functional");
-    let myNumber = {
+    const { method } = require("sdk/lang/functional");
+
+    const times = (target, x) => target.number *= x;
+    const add = (target, x) => target.number += x;
+
+    const myNumber = {
       times: method(times),
       add: method(add),
       number: 0
     };
 
-    function times (target, x) { return target.number *= x; }
-    function add (target, x) { return target.number += x; }
 
     console.log(myNumber.number); // 0
     myNumber.add(10);      // 10
     myNumber.times(2);     // 20
     myNumber.add(3);       // 23
 
 @param lambda {function}
   The function to be wrapped and returned.
@@ -39,18 +41,18 @@ the first parameter, followed by any par
 @function
 Takes a function and returns a wrapped version of the function. Calling the
 wrapped version will call the original function during the next event loop.
 This is similar to calling [setTimeout](modules/sdk/timers.html#setTimeout(callback%2C ms)) with no
 wait (i.e. `setTimeout(function () { ... }, 0)`), except that the wrapped function
 may be reused and does not need to be repeated each time. This also enables you
 to use these functions as event listeners.
 
-    let { defer } = require("sdk/lang/functional");
-    let fn = defer(function myEvent (event, value) {
+    const { defer } = require("sdk/lang/functional");
+    const fn = defer((event, value) => {
       console.log(event + " : " + value);
     });
 
     fn("click", "#home");
     console.log("done");
 
     // This will print 'done' before 'click : #home' since
     // we deferred the execution of the wrapped `myEvent`
@@ -69,43 +71,38 @@ to use these functions as event listener
 An alias for [defer](modules/sdk/lang/functional.html#defer(fn)).
 </api>
 
 <api name="invoke">
 @function
 Invokes `callee`, passing `params` as an argument and `self` as `this`.
 Returns the value that is returned by `callee`.
 
-    let { invoke } = require("sdk/lang/functional");
-
-    invoke(sum, [1,2,3,4,5], null); // 15
+    const { invoke } = require("sdk/lang/functional");
 
-    function sum () {
-      return Array.slice(arguments).reduce(function (a, b) {
-        return a + b;
-      });
-    }
+    const sum = (...args) => args.reduce((a, b) => a + b);
+    invoke(sum, [1,2,3,4,5], null); // 15
 
 @param callee {function}
   Function to invoke.
 @param params {Array}
   Parameters to be passed into `callee`.
 @param self {mixed}
   Object to be passed as the `this` context to `callee`.
 @returns {mixed}
   Returns the return value of `callee`.
 </api>
 
 <api name="partial">
 @function
 Takes a function and bind values to one or more arguments, returning a new function of smaller arity.
 
-    let { partial } = require("sdk/lang/functional");
-    let add = function add (x, y) { return x + y; }
-    let addOne = partial(add, 1);
+    const { partial } = require("sdk/lang/functional");
+    const add = (x, y) => x + y;
+    const addOne = partial(add, 1);
 
     addOne(5); // 6
     addOne(10); // 11
     partial(add, addOne(20))(2); // 23
 
 @param fn {function}
   Function on which partial application is to be performed.
 
@@ -117,66 +114,66 @@ Takes a function and bind values to one 
 </api>
 
 <api name="compose">
 @function
 Returns the [composition](http://en.wikipedia.org/wiki/Function_composition_(computer_science)) of a list of functions, where each function consumes the
 return value of the function that follows. In math terms, composing the functions
 `f()`, `g()`, and `h()` produces `f(g(h()))`.
 
-    let { compose } = require("sdk/lang/functional");
+    const { compose } = require("sdk/lang/functional");
 
-    let welcome = compose(exclaim, greet);
+    const square = x => x * x;
+    const increment = x => x + 1;
 
-    welcome('moe'); // "hi: moe!";
+    const f1 = compose(increment, square);
+    f1(5); // => 26
 
-    function greet (name) { return "hi: " + name; }
-    function exclaim (statement) { return statement + "!"; }
+    const f2 = compose(square, increment);
+    f2(5); // => 36
 
 @param fn... {function}
   Takes a variable number of functions as arguments and composes them from right to left.
 
 @returns {function}
   The composed function.
 </api>
 
 <api name="wrap">
 @function
 Returns the first function passed as an argument to the second,
 allowing you to adjust arguments, run code before and after, and
 conditionally execute the original function.
 
-    let { wrap } = require("sdk/lang/functional");
+    const { wrap } = require("sdk/lang/functional");
 
-    let wrappedHello = wrap(hello, function (fn, name) {
-      return "before, " + fn(name) + "after";
-    });
+    const hello = name => "hello: " + name;
+    const wrappedHello = wrap(hello, (fn, name) =>
+      "before, " + fn(name) + "after");
 
     wrappedHello("moe"); // "before, hello: moe, after"
 
-    function hello (name) { return "hello: " + name; }
-
 @param fn {function}
   The function to be passed into the `wrapper` function.
 
 @param wrapper {function}
   The function that is called when the return function is executed,
   taking the wrapped `fn` as the first parameter.
 
 @returns {function}
   A function which, when called, executes `wrapper` with `fn` as the first parameter,
   and passes in any additional parameters to the `wrapper` function.
 </api>
 
 <api name="identity">
 @function
 Returns the same value that is used as the argument. In math: f(x) = x.
 
-    let { identity } = require("sdk/lang/functional");
-    let x = 5;
+    const { identity } = require("sdk/lang/functional");
+    const x = 5;
     identity(x); // 5
 
 @param value {mixed}
   The value to be returned.
 
 @returns {mixed}
   The value that was originally passed in.
 </api>
@@ -185,45 +182,43 @@ Returns the same value that is used as t
 @function
 [Memoizes](http://en.wikipedia.org/wiki/Memoization) a given function by caching
 the computed result. Useful for speeding up slow-running computations. If
 passed an optional `hashFunction`, it will be used to compute the hash key for
 storing the result, based on the arguments to the original function. The
 default `hashFunction` just uses the first argument to the memoized function as
 the key.
 
-    let { memoize } = require("sdk/lang/functional");
+    const { memoize } = require("sdk/lang/functional");
 
-    let memoizedFn = memoize(primeFactorization);
+    const memoizedFn = memoize(primeFactorization);
 
     memoizedFn(50); // Returns [2, 5, 5], had to compute
     memoizedFn(100); // Returns [2, 2, 5, 5], had to compute
     memoizedFn(50); // Returns [2, 5, 5] again, but pulled from cache
 
-    function primeFactorization (x) {
+    const primeFactorization = x => {
       // Some tricky stuff
     }
 
     // We can also use a hash function to compute a different
     // hash value. In this example, we'll fabricate a function
     // that takes a string of first and last names that
     // somehow computes the lineage of that name. Our hash
     // function will just parse the last name, as our naive
     // implementation assumes that they will share the same lineage
 
-    let getLineage = memoize(function (name) {
+    const getLineage = memoize(name => {
       // computes lineage
       return data;
     }, hasher);
 
     // Hashing function takes a string of first and last name
     // and returns the last name.
-    function hasher (input) {
-      return input.split(" ")[1];
-    }
+    const hasher = input => input.split(" ")[1];
 
     getLineage("homer simpson"); // Computes and returns information for "simpson"
     getLineage("lisa simpson"); // Returns cached for "simpson"
 
 @param fn {function}
   The function that becomes memoized.
 
 @param hasher {function}
@@ -235,22 +230,21 @@ the key.
 </api>
 
 <api name="delay">
 @function
 Much like `setTimeout`, `delay` invokes a function after waiting a set number of
 milliseconds. If you pass additional, optional, arguments, they will be forwarded
 on to the function when it is invoked.
 
-    let { delay } = require("sdk/lang/functional");
+    const { delay } = require("sdk/lang/functional");
 
+    const printAdd = (a, b) console.log(a + "+" + b + "=" + (a+b));
     delay(printAdd, 2000, 5, 10);
-
     // Prints "5+10=15" in two seconds (2000ms)
-    function printAdd (a, b) { console.log(a + "+" + b + "=" + (a+b)); }
 
 @param fn {function}
   A function to be delayed.
 
 @param ms {number}
   Number of milliseconds to delay the execution of `fn`.
 
 @param arguments {mixed}
@@ -259,59 +253,318 @@ on to the function when it is invoked.
 
 <api name="once">
 @function
 Creates a version of the input function that can only be called one time.
 Repeated calls to the modified function will have no effect, returning
 the value from the original call. Useful for initialization functions, instead
 of having to set a boolean flag and checking it later.
 
-    let { once } = require("sdk/lang/functional");
-    let setup = once(function (env) {
+    const { once } = require("sdk/lang/functional");
+    const setup = once(env => {
       // initializing important things
       console.log("successfully initialized " + env);
       return 1; // Assume success and return 1
     });
 
     setup('dev'); // returns 1
     // prints "successfully initialized dev"
-    
+
     // Future attempts to call this function just return the cached
     // value that was returned previously
     setup('production'); // Returns 1
     // No print message is displayed since the function isn't executed
 
 @param fn {function}
   The function that will be executed only once inside the once wrapper.
 
 @returns {function}
   The wrapped `fn` that can only be executed once.
 </api>
 
-<api name="chain">
+<api name="cache">
+@function
+An alias for [once](modules/sdk/lang/functional.html#once(fn)).
+</api>
+
+<api name="complement">
+@function
+Takes a `f` function and returns a function that takes the same
+arguments as `f`, has the same effects, if any, and returns the
+opposite truth value.
+
+    const { complement } = require("sdk/lang/functional");
+
+    let isOdd = x => Boolean(x % 2);
+
+    isOdd(1)     // => true
+    isOdd(2)     // => false
+
+    let isEven = complement(isOdd);
+
+    isEven(1)     // => false
+    isEven(2)     // => true
+
+@param lambda {function}
+  The function to compose from
+
+@returns {boolean}
+  `!lambda(...)`
+</api>
+
+<api name="constant">
+@function
+Constructs function that returns `x` no matter what is it
+invoked with.
+
+    const { constant } = require("sdk/lang/functional");
+
+    const one = constant(1);
+
+    one();              // => 1
+    one(2);             // => 1
+    one.apply({}, 3);   // => 1
+
+@param x {object}
+  Value that will be returned by composed function
+
+@returns {function}
+</api>
+
+
+<api name="apply">
+@function
+Apply function that behaves like `apply` in other functional
+languages:
+
+    const { apply } = require("sdk/lang/functional");
+
+    const dashify = (...args) => args.join("-");
+
+    apply(dashify, 1, [2, 3]);        // => "1-2-3"
+    apply(dashify, "a");              // => "a"
+    apply(dashify, ["a", "b"]);       // => "a-b"
+    apply(dashify, ["a", "b"], "c");  // => "a,b-c"
+    apply(dashify, [1, 2], [3, 4]);   // => "1,2-3-4"
+
+@param f {function}
+  function to be invoked
+</api>
+
+<api name="flip">
+@function
+Returns function identical to given `f` but with flipped order
+of arguments.
+
+    const { flip } = require("sdk/lang/functional");
+
+    const append = (left, right) => left + " " + right;
+    const prepend = flip(append);
+
+    append("hello", "world")       // => "hello world"
+    prepend("hello", "world")      // => "world hello"
+
+@param f {function}
+  function whose arguments should to be flipped
+
+@returns {function}
+  function with flipped arguments
+</api>
+
+<api name="when">
+@function
+Takes `p` predicate, `consequent` function and an optional
+`alternate` function and composes function that returns
+application of arguments over `consequent` if application over
+`p` is `true` otherwise returns application over `alternate`.
+If `alternate` is not a function returns `undefined`.
+
+    const { when } = require("sdk/lang/functional");
+
+    function Point(x, y) {
+      this.x = x
+      this.y = y
+    }
+
+    const isPoint = x => x instanceof Point;
+
+    const inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
+
+    inc({});                 // => undefined
+    inc(new Point(0, 0));    // => { x: 1, y: 1 }
+
+    const axis = when(isPoint,
+                      ({ x, y }) => [x, y],
+                      _ => [0, 0]);
+
+    axis(new Point(1, 4));   // => [1, 4]
+    axis({ foo: "bar" });    // => [0, 0]
+
+@param p {function}
+  predicate function whose return value determines to which
+  function be delegated control.
+
+@param consequent {function}
+  function to which arguments are applied if `predicate` returned
+  `true`.
+
+@param alternate {function}
+  function to which arguments are applied if `predicate` returned
+  `false`.
+
+@returns {object|string|number|function}
+  Return value from `consequent` if `p` returned `true` or return
+  value from `alternate` if `p` returned `false`. If `alternate`
+  is not provided and `p` returned `false` then `undefined` is
+  returned.
+</api>
+
+
+<api name="chainable">
 @function
 Creates a version of the input function that will return `this`.
 
-    let { chain } = require("sdk/lang/functional");
+    const { chainable } = require("sdk/lang/functional");
 
     function Person (age) { this.age = age; }
-    Person.prototype.happyBirthday = chain(function () this.age++);
+    Person.prototype.happyBirthday = chainable(function() {
+      return this.age++
+    });
 
-    let person = new Person(30);
+    const person = new Person(30);
 
     person
       .happyBirthday()
       .happyBirthday()
       .happyBirthday()
 
     console.log(person.age); // 33
 
 @param fn {function}
   The function that will be wrapped by the chain function.
 
 @returns {function}
   The wrapped function that executes `fn` and returns `this`.
 </api>
 
-<api name="cache">
+<api name="field">
+@function
+
+Takes field `name` and `target` and returns value of that field.
+If `target` is `null` or `undefined` it would be returned back
+instead of attempt to access it's field. Function is implicitly
+curried, this allows accessor function generation by calling it
+with only `name` argument.
+
+    const { field } = require("sdk/lang/functional");
+
+    field("x", { x: 1, y: 2});     // => 1
+    field("x")({ x: 1 });          // => 1
+    field("x", { y: 2 });          // => undefiend
+
+    const getX = field("x");
+    getX({ x: 1 });               // => 1
+    getX({ y: 1 });               // => undefined
+    getX(null);                   // => null
+
+@param name {string}
+  Name of the field to be returned
+
+@param target {object}
+  Target to get a field by the given `name` from
+
+@returns {object|function|string|number|boolean}
+  Field value
+</api>
+
+<api name="query">
+@function
+
+Takes `.` delimited string representing `path` to a nested field
+and a `target` to get it from. For convinience function is
+implicitly curried, there for accessors can be created by invoking
+it with just a `path` argument.
+
+    const { query } = require("sdk/lang/functional");
+
+    query("x", { x: 1, y: 2});           // => 1
+    query("top.x", { x: 1 });            // => undefiend
+    query("top.x", { top: { x: 2 } });   // => 2
+
+    const topX = query("top.x");
+    topX({ top: { x: 1 } });             // => 1
+    topX({ y: 1 });                      // => undefined
+    topX(null);                          // => null
+
+@param path {string}
+  `.` delimited path to a field
+
+@param target {object}
+  Target to get a field by the given `name` from
+
+@returns {object|function|string|number|boolean}
+  Field value
+</api>
+
+<api name="isInstance">
 @function
-An alias for [once](modules/sdk/lang/functional.html#once(fn)).
+
+Takes `Type` (constructor function) and a `value` and returns
+`true` if `value` is instance of the given `Type`. Function is
+implicitly curried this allows predicate generation by calling
+function with just first argument.
+
+    const { isInstance } = require("sdk/lang/functional");
+
+    function X() {}
+    function Y() {}
+    let isX = isInstance(X);
+
+    isInstance(X, new X);     // true
+    isInstance(X)(new X);     // true
+    isInstance(X, new Y);     // false
+    isInstance(X)(new Y);     // false
+
+    isX(new X);               // true
+    isX(new Y);               // false
+
+@param Type {function}
+  Type (constructor function)
+
+@param instance {object}
+  Instance to test
+
+@returns {boolean}
 </api>
+
+<api name="is">
+@function
+
+Functions takes `expected` and `actual` values and returns `true` if
+`expected === actual`. If invoked with just one argument returns pratially
+applied function, which can be invoked to provide a second argument, this
+is handy with `map`, `filter` and other high order functions:
+
+    const { is } = require("sdk/util/oops");
+    [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
+
+@param expected {object|string|number|boolean}
+@param actual {object|string|number|boolean}
+
+@returns {boolean}
+</api>
+
+<api name="isnt">
+@function
+
+Functions takes `expected` and `actual` values and returns `true` if
+`expected !== actual`. If invoked with just one argument returns pratially
+applied function, which can be invoked with a second argument, which is
+handy with `map`, `filter` and other high order functions:
+
+    const { isnt } = require("sdk/util/oops");
+    [ 1, 0, 1, 0, 1 ].map(isnt(0)) // => [ true, false, true, false, true ]
+
+@param expected {object|string|number|boolean}
+@param actual {object|string|number|boolean}
+
+@returns {boolean}
+</api>
--- a/addon-sdk/source/lib/sdk/event/target.js
+++ b/addon-sdk/source/lib/sdk/event/target.js
@@ -4,17 +4,17 @@
 
 'use strict';
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { on, once, off, setListeners } = require('./core');
-const { method, chain } = require('../lang/functional');
+const { method, chainable } = require('../lang/functional');
 const { Class } = require('../core/heritage');
 
 /**
  * `EventTarget` is an exemplar for creating an objects that can be used to
  * add / remove event listeners on them. Events on these objects may be emitted
  * via `emit` function exported by 'event/core' module.
  */
 const EventTarget = Class({
@@ -38,26 +38,26 @@ const EventTarget = Class({
    *    The type of event.
    * @param {Function} listener
    *    The listener function that processes the event.
    * @example
    *      worker.on('message', function (data) {
    *        console.log('data received: ' + data)
    *      })
    */
-  on: chain(method(on)),
+  on: chainable(method(on)),
   /**
    * Registers an event `listener` that is called once the next time an event
    * of the specified `type` is emitted.
    * @param {String} type
    *    The type of the event.
    * @param {Function} listener
    *    The listener function that processes the event.
    */
-  once: chain(method(once)),
+  once: chainable(method(once)),
   /**
    * Removes an event `listener` for the given event `type`.
    * @param {String} type
    *    The type of event.
    * @param {Function} listener
    *    The listener function that processes the event.
    */
   removeListener: function removeListener(type, listener) {
--- a/addon-sdk/source/lib/sdk/lang/functional.js
+++ b/addon-sdk/source/lib/sdk/lang/functional.js
@@ -1,147 +1,152 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// Disclaimer: Most of the functions in this module implement APIs from
+// Disclaimer: Some of the functions in this module implement APIs from
 // Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
 // those goes to him.
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
+const { deprecateFunction } = require("../util/deprecate");
 const { setImmediate, setTimeout } = require("../timers");
-const { deprecateFunction } = require("../util/deprecate");
+
+const arity = f => f.arity || f.length;
+
+const name = f => f.displayName || f.name;
+
+const derive = (f, source) => {
+  f.displayName = name(source);
+  f.arity = arity(source);
+  return f;
+};
 
 /**
- * Takes `lambda` function and returns a method. When returned method is
- * invoked it calls wrapped `lambda` and passes `this` as a first argument
- * and given argument as rest.
+ * Takes variadic numeber of functions and returns composed one.
+ * Returned function pushes `this` pseudo-variable to the head
+ * of the passed arguments and invokes all the functions from
+ * left to right passing same arguments to them. Composite function
+ * returns return value of the right most funciton.
  */
-function method(lambda) {
-  return function method() {
-    return lambda.apply(null, [this].concat(Array.slice(arguments)));
-  }
-}
+const method = (...lambdas) => {
+  return function method(...args) {
+    args.unshift(this);
+    return lambdas.reduce((_, lambda) => lambda.apply(this, args),
+                          void(0));
+  };
+};
 exports.method = method;
 
 /**
  * Takes a function and returns a wrapped one instead, calling which will call
  * original function in the next turn of event loop. This is basically utility
  * to do `setImmediate(function() { ... })`, with a difference that returned
  * function is reused, instead of creating a new one each time. This also allows
  * to use this functions as event listeners.
  */
-function defer(f) {
-  return function deferred() setImmediate(invoke, f, arguments, this);
-}
+const defer = f => derive(function(...args) {
+  setImmediate(invoke, f, args, this);
+}, f);
 exports.defer = defer;
 // Exporting `remit` alias as `defer` may conflict with promises.
 exports.remit = defer;
 
-/*
- * Takes a funtion and returns a wrapped function that returns `this`
- */
-function chain(f) {
-  return function chainable(...args) {
-    f.apply(this, args);
-    return this;
-  };
-}
-exports.chain = chain;
-
 /**
  * Invokes `callee` by passing `params` as an arguments and `self` as `this`
  * pseudo-variable. Returns value that is returned by a callee.
  * @param {Function} callee
  *    Function to invoke.
  * @param {Array} params
  *    Arguments to invoke function with.
  * @param {Object} self
  *    Object to be passed as a `this` pseudo variable.
  */
-function invoke(callee, params, self) callee.apply(self, params);
+const invoke = (callee, params, self) => callee.apply(self, params);
 exports.invoke = invoke;
 
 /**
  * Takes a function and bind values to one or more arguments, returning a new
  * function of smaller arity.
  *
  * @param {Function} fn
  *    The function to partial
  *
  * @returns The new function with binded values
  */
-function partial(fn) {
-  if (typeof fn !== "function")
-    throw new TypeError(String(fn) + " is not a function");
+const partial = (f, ...curried) => {
+  if (typeof(f) !== "function")
+    throw new TypeError(String(f) + " is not a function");
 
-  let args = Array.slice(arguments, 1);
-
-  return function() fn.apply(this, args.concat(Array.slice(arguments)));
-}
+  let fn = derive(function(...args) {
+    return f.apply(this, curried.concat(args));
+  }, f);
+  fn.arity = arity(f) - curried.length;
+  return fn;
+};
 exports.partial = partial;
 
 /**
  * Returns function with implicit currying, which will continue currying until
  * expected number of argument is collected. Expected number of arguments is
  * determined by `fn.length`. Using this with variadic functions is stupid,
  * so don't do it.
  *
  * @examples
  *
  * var sum = curry(function(a, b) {
  *   return a + b
  * })
  * console.log(sum(2, 2)) // 4
  * console.log(sum(2)(4)) // 6
  */
-var curry = new function() {
-  function currier(fn, arity, params) {
+const curry = new function() {
+  const currier = (fn, arity, params) => {
     // Function either continues to curry arguments or executes function
     // if desired arguments have being collected.
-    return function curried() {
-      var input = Array.slice(arguments);
+    const curried = function(...input) {
       // Prepend all curried arguments to the given arguments.
       if (params) input.unshift.apply(input, params);
       // If expected number of arguments has being collected invoke fn,
       // othrewise return curried version Otherwise continue curried.
       return (input.length >= arity) ? fn.apply(this, input) :
              currier(fn, arity, input);
     };
-  }
+    curried.arity = arity - (params ? params.length : 0);
 
-  return function curry(fn) {
-    return currier(fn, fn.length);
-  }
+    return curried;
+  };
+
+  return fn => currier(fn, arity(fn));
 };
 exports.curry = curry;
 
 /**
  * Returns the composition of a list of functions, where each function consumes
  * the return value of the function that follows. In math terms, composing the
  * functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
  * @example
  *
  *   var greet = function(name) { return "hi: " + name; };
  *   var exclaim = function(statement) { return statement + "!"; };
  *   var welcome = compose(exclaim, greet);
  *
  *   welcome('moe');    // => 'hi: moe!'
  */
-function compose() {
-  let lambdas = Array.slice(arguments);
-  return function composed() {
-    let args = Array.slice(arguments), index = lambdas.length;
+function compose(...lambdas) {
+  return function composed(...args) {
+    let index = lambdas.length;
     while (0 <= --index)
-      args = [ lambdas[index].apply(this, args) ];
+      args = [lambdas[index].apply(this, args)];
+
     return args[0];
   };
 }
 exports.compose = compose;
 
 /*
  * Returns the first function passed as an argument to the second,
  * allowing you to adjust arguments, run code before and after, and
@@ -150,61 +155,176 @@ exports.compose = compose;
  *
  *  var hello = function(name) { return "hello: " + name; };
  *  hello = wrap(hello, function(f) {
  *    return "before, " + f("moe") + ", after";
  *  });
  *
  *  hello();    // => 'before, hello: moe, after'
  */
-function wrap(f, wrapper) {
-  return function wrapped()
-    wrapper.apply(this, [ f ].concat(Array.slice(arguments)))
-};
+const wrap = (f, wrapper) => derive(function wrapped(...args) {
+  return wrapper.apply(this, [f].concat(args));
+}, f);
 exports.wrap = wrap;
 
 /**
  * Returns the same value that is used as the argument. In math: f(x) = x
  */
-function identity(value) value
+const identity = value => value;
 exports.identity = identity;
 
 /**
  * Memoizes a given function by caching the computed result. Useful for
  * speeding up slow-running computations. If passed an optional hashFunction,
  * it will be used to compute the hash key for storing the result, based on
  * the arguments to the original function. The default hashFunction just uses
  * the first argument to the memoized function as the key.
  */
-function memoize(f, hasher) {
+const memoize = (f, hasher) => {
   let memo = Object.create(null);
+  let cache = new WeakMap();
   hasher = hasher || identity;
-  return function memoizer() {
-    let key = hasher.apply(this, arguments);
-    return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments));
-  };
-}
+  return derive(function memoizer(...args) {
+    const key = hasher.apply(this, args);
+    const type = typeof(key);
+    if (key && (type === "object" || type === "function")) {
+      if (!cache.has(key))
+        cache.set(key, f.apply(this, args));
+      return cache.get(key);
+    }
+    else {
+      if (!(key in memo))
+        memo[key] = f.apply(this, args);
+      return memo[key];
+    }
+  }, f);
+};
 exports.memoize = memoize;
 
 /**
  * Much like setTimeout, invokes function after wait milliseconds. If you pass
  * the optional arguments, they will be forwarded on to the function when it is
  * invoked.
  */
-function delay(f, ms) {
-  let args = Array.slice(arguments, 2);
-  setTimeout(function(context) { return f.apply(context, args); }, ms, this);
+const delay = function delay(f, ms, ...args) {
+  setTimeout(() => f.apply(this, args), ms);
 };
 exports.delay = delay;
 
 /**
  * Creates a version of the function that can only be called one time. Repeated
  * calls to the modified function will have no effect, returning the value from
  * the original call. Useful for initialization functions, instead of having to
  * set a boolean flag and then check it later.
  */
-function once(f) {
+const once = f => {
   let ran = false, cache;
-  return function() ran ? cache : (ran = true, cache = f.apply(this, arguments))
+  return derive(function(...args) {
+    return ran ? cache : (ran = true, cache = f.apply(this, args));
+  }, f);
 };
 exports.once = once;
 // export cache as once will may be conflicting with event once a lot.
 exports.cache = once;
+
+// Takes a `f` function and returns a function that takes the same
+// arguments as `f`, has the same effects, if any, and returns the
+// opposite truth value.
+const complement = f => derive(function(...args) {
+  return args.length < arity(f) ? complement(partial(f, ...args)) :
+         !f.apply(this, args);
+}, f);
+exports.complement = complement;
+
+// Constructs function that returns `x` no matter what is it
+// invoked with.
+const constant = x => _ => x;
+exports.constant = constant;
+
+// Takes `p` predicate, `consequent` function and an optional
+// `alternate` function and composes function that returns
+// application of arguments over `consequent` if application over
+// `p` is `true` otherwise returns application over `alternate`.
+// If `alternate` is not a function returns `undefined`.
+const when = (p, consequent, alternate) => {
+  if (typeof(alternate) !== "function" && alternate !== void(0))
+    throw TypeError("alternate must be a function");
+  if (typeof(consequent) !== "function")
+    throw TypeError("consequent must be a function");
+
+  return function(...args) {
+    return p.apply(this, args) ?
+           consequent.apply(this, args) :
+           alternate && alternate.apply(this, args);
+  };
+};
+exports.when = when;
+
+// Apply function that behaves as `apply` does in lisp:
+// apply(f, x, [y, z]) => f.apply(f, [x, y, z])
+// apply(f, x) => f.apply(f, [x])
+const apply = (f, ...rest) => f.apply(f, rest.concat(rest.pop()));
+exports.apply = apply;
+
+// Returns function identical to given `f` but with flipped order
+// of arguments.
+const flip = f => derive(function(...args) {
+  return f.apply(this, args.reverse());
+}, f);
+exports.flip = flip;
+
+
+// Takes field `name` and `target` and returns value of that field.
+// If `target` is `null` or `undefined` it would be returned back
+// instead of attempt to access it's field. Function is implicitly
+// curried, this allows accessor function generation by calling it
+// with only `name` argument.
+const field = curry((name, target) =>
+  // Note: Permisive `==` is intentional.
+  target == null ? target : target[name]);
+exports.field = field;
+
+// Takes `.` delimited string representing `path` to a nested field
+// and a `target` to get it from. For convinience function is
+// implicitly curried, there for accessors can be created by invoking
+// it with just a `path` argument.
+const query = curry((path, target) => {
+  const names = path.split(".");
+  const count = names.length;
+  let index = 0;
+  let result = target;
+  // Note: Permisive `!=` is intentional.
+  while (result != null && index < count) {
+    result = result[names[index]];
+    index = index + 1;
+  }
+  return result;
+});
+exports.query = query;
+
+// Takes `Type` (constructor function) and a `value` and returns
+// `true` if `value` is instance of the given `Type`. Function is
+// implicitly curried this allows predicate generation by calling
+// function with just first argument.
+const isInstance = curry((Type, value) => value instanceof Type);
+exports.isInstance = isInstance;
+
+/*
+ * Takes a funtion and returns a wrapped function that returns `this`
+ */
+const chainable = f => derive(function(...args) {
+  f.apply(this, args);
+  return this;
+}, f);
+exports.chainable = chainable;
+exports.chain =
+  deprecateFunction(chainable, "Function `chain` was renamed to `chainable`");
+
+// Functions takes `expected` and `actual` values and returns `true` if
+// `expected === actual`. Returns curried function if called with less then
+// two arguments.
+//
+// [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
+const is = curry((expected, actual) => actual === expected);
+exports.is = is;
+
+const isnt = complement(is);
+exports.isnt = isnt;
--- a/addon-sdk/source/test/test-functional.js
+++ b/addon-sdk/source/test/test-functional.js
@@ -1,43 +1,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
+"use strict";
 
 const { setTimeout } = require('sdk/timers');
 const utils = require('sdk/lang/functional');
-const { invoke, defer, partial, compose, memoize, once, delay, wrap, curry, chain } = utils;
+const { invoke, defer, partial, compose, memoize, once, is, isnt,
+        delay, wrap, curry, chainable, field, query, isInstance } = utils;
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 
 exports['test forwardApply'] = function(assert) {
-  function sum(b, c) this.a + b + c
+  function sum(b, c) { return this.a + b + c; }
   assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
                'passed arguments and pseoude-variable are used');
 
   assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
                'bounded `this` pseoudo variable is used');
-}
+};
 
 exports['test deferred function'] = function(assert, done) {
   let nextTurn = false;
   function sum(b, c) {
     assert.ok(nextTurn, 'enqueued is called in next turn of event loop');
     assert.equal(this.a + b + c, 6,
                  'passed arguments an pseoude-variable are used');
     done();
   }
 
-  let fixture = { a: 1, method: defer(sum) }
+  let fixture = { a: 1, method: defer(sum) };
   fixture.method(2, 3);
   nextTurn = true;
 };
 
 exports['test partial function'] = function(assert) {
-  function sum(b, c) this.a + b + c;
+  function sum(b, c) { return this.a + b + c; }
 
   let foo = { a : 5 };
 
   foo.sum7 = partial(sum, 7);
   foo.sum8and4 = partial(sum, 8, 4);
 
   assert.equal(foo.sum7(2), 14, 'partial one arguments works');
 
@@ -63,21 +64,21 @@ exports['test compose'] = function(asser
                'can compose a function that takes another');
 
   assert.equal(compose(greet, exclaim)('moe'), 'hi: moe!',
                'in this case, the functions are also commutative');
 
   let target = {
     name: 'Joe',
     greet: compose(function exclaim(sentence) {
-      return sentence + '!'
+      return sentence + '!';
     }, function(title) {
       return 'hi : ' + title + ' ' + this.name;
     })
-  }
+  };
 
   assert.equal(target.greet('Mr'), 'hi : Mr Joe!',
                'this can be passed in');
   assert.equal(target.greet.call({ name: 'Alex' }, 'Dr'), 'hi : Dr Alex!',
                'this can be applied');
 
   let single = compose(function(value) {
     return value + ':suffix';
@@ -101,113 +102,321 @@ exports['test wrap'] = function(assert) 
   let inner = function () { return 'Hello '; };
   let target = {
     name: 'Matteo',
     hi: wrap(inner, function(f) { return f() + this.name; })
   };
 
   assert.equal(target.hi(), 'Hello Matteo', 'works with this');
 
-  function noop() { };
+  function noop() { }
   let wrapped = wrap(noop, function(f) {
     return Array.slice(arguments);
   });
 
   let actual = wrapped([ 'whats', 'your' ], 'vector', 'victor');
   assert.deepEqual(actual, [ noop, ['whats', 'your'], 'vector', 'victor' ],
                    'works with fancy stuff');
 };
 
 exports['test memoize'] = function(assert) {
-  function fib(n) n < 2 ? n : fib(n - 1) + fib(n - 2)
+  const fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2);
   let fibnitro = memoize(fib);
 
   assert.equal(fib(10), 55,
         'a memoized version of fibonacci produces identical results');
   assert.equal(fibnitro(10), 55,
         'a memoized version of fibonacci produces identical results');
 
-  function o(key, value) { return value; };
+  function o(key, value) { return value; }
   let oo = memoize(o), v1 = {}, v2 = {};
 
 
   assert.equal(oo(1, v1), v1, 'returns value back');
   assert.equal(oo(1, v2), v1, 'memoized by a first argument');
   assert.equal(oo(2, v2), v2, 'returns back value if not memoized');
   assert.equal(oo(2), v2, 'memoized new value');
   assert.notEqual(oo(1), oo(2), 'values do not override');
   assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized');
 
-  let get = memoize(function(attribute) this[attribute])
-  let target = { name: 'Bob', get: get }
+  let get = memoize(function(attribute) { return this[attribute]; });
+  let target = { name: 'Bob', get: get };
 
   assert.equal(target.get('name'), 'Bob', 'has correct `this`');
   assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob',
-               'name is memoized')
+               'name is memoized');
   assert.equal(get('name'), 'Bob', 'once memoized can be called without this');
 };
 
 exports['test delay'] = function(assert, done) {
   let delayed = false;
   delay(function() {
     assert.ok(delayed, 'delayed the function');
     done();
   }, 1);
   delayed = true;
 };
 
 exports['test delay with this'] = function(assert, done) {
-  let context = {}
+  let context = {};
   delay.call(context, function(name) {
     assert.equal(this, context, 'this was passed in');
     assert.equal(name, 'Tom', 'argument was passed in');
     done();
   }, 10, 'Tom');
-}
+};
 
 exports['test once'] = function(assert) {
   let n = 0;
   let increment = once(function() { n ++; });
 
   increment();
   increment();
 
   assert.equal(n, 1, 'only incremented once');
 
-  let target = { state: 0, update: once(function() this.state ++ ) };
+  let target = {
+    state: 0,
+    update: once(function() {
+      return this.state ++;
+    })
+  };
 
   target.update();
   target.update();
 
   assert.equal(target.state, 1, 'this was passed in and called only once');
 };
 
 exports['test once with argument'] = function(assert) {
   let n = 0;
-  let increment = once(function(a) n++);
+  let increment = once(a => n++);
 
   increment();
   increment('foo');
 
   assert.equal(n, 1, 'only incremented once');
 
   increment();
   increment('foo');
 
   assert.equal(n, 1, 'only incremented once');
 };
 
-exports['test chain'] = function (assert) {
+exports['test complement'] = assert => {
+  let { complement } = require("sdk/lang/functional");
+
+  let isOdd = x => Boolean(x % 2);
+
+  assert.equal(isOdd(1), true);
+  assert.equal(isOdd(2), false);
+
+  let isEven = complement(isOdd);
+
+  assert.equal(isEven(1), false);
+  assert.equal(isEven(2), true);
+
+  let foo = {};
+  let isFoo = function() { return this === foo; };
+  let insntFoo = complement(isFoo);
+
+  assert.equal(insntFoo.call(foo), false);
+  assert.equal(insntFoo.call({}), true);
+};
+
+exports['test constant'] = assert => {
+  let { constant } = require("sdk/lang/functional");
+
+  let one = constant(1);
+
+  assert.equal(one(1), 1);
+  assert.equal(one(2), 1);
+};
+
+exports['test apply'] = assert => {
+  let { apply } = require("sdk/lang/functional");
+
+  let dashify = (...args) => args.join("-");
+
+  assert.equal(apply(dashify, 1, [2, 3]), "1-2-3");
+  assert.equal(apply(dashify, "a"), "a");
+  assert.equal(apply(dashify, ["a", "b"]), "a-b");
+  assert.equal(apply(dashify, ["a", "b"], "c"), "a,b-c");
+  assert.equal(apply(dashify, [1, 2], [3, 4]), "1,2-3-4");
+};
+
+exports['test flip'] = assert => {
+  let { flip } = require("sdk/lang/functional");
+
+  let append = (left, right) => left + " " + right;
+  let prepend = flip(append);
+
+  assert.equal(append("hello", "world"), "hello world");
+  assert.equal(prepend("hello", "world"), "world hello");
+
+  let wrap = function(left, right) {
+    return left + " " + this + " " + right;
+  };
+  let invertWrap = flip(wrap);
+
+  assert.equal(wrap.call("@", "hello", "world"), "hello @ world");
+  assert.equal(invertWrap.call("@", "hello", "world"), "world @ hello");
+
+  let reverse = flip((...args) => args);
+
+  assert.deepEqual(reverse(1, 2, 3, 4), [4, 3, 2, 1]);
+  assert.deepEqual(reverse(1), [1]);
+  assert.deepEqual(reverse(), []);
+
+  // currying still works
+  let prependr = curry(prepend);
+
+  assert.equal(prependr("hello", "world"), "world hello");
+  assert.equal(prependr("hello")("world"), "world hello");
+};
+
+exports["test when"] = assert => {
+  let { when } = require("sdk/lang/functional");
+
+  let areNums = (...xs) => xs.every(x => typeof(x) === "number");
+
+  let sum = when(areNums, (...xs) => xs.reduce((y, x) => x + y, 0));
+
+  assert.equal(sum(1, 2, 3), 6);
+  assert.equal(sum(1, 2, "3"), undefined);
+
+  let multiply = when(areNums,
+                      (...xs) => xs.reduce((y, x) => x * y, 1),
+                      (...xs) => xs);
+
+  assert.equal(multiply(2), 2);
+  assert.equal(multiply(2, 3), 6);
+  assert.deepEqual(multiply(2, "4"), [2, "4"]);
+
+  function Point(x, y) {
+    this.x = x;
+    this.y = y;
+  }
+
+  let isPoint = x => x instanceof Point;
+
+  let inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
+
+  assert.equal(inc({}), undefined);
+  assert.deepEqual(inc(new Point(0, 0)), { x: 1, y: 1 });
+
+  let axis = when(isPoint,
+                  ({ x, y }) => [x, y],
+                  _ => [0, 0]);
+
+  assert.deepEqual(axis(new Point(1, 4)), [1, 4]);
+  assert.deepEqual(axis({ foo: "bar" }), [0, 0]);
+};
+
+exports["test chainable"] = function(assert) {
   let Player = function () { this.volume = 5; };
   Player.prototype = {
-    setBand: chain(function (band) this.band = band),
-    incVolume: chain(function () this.volume++)
+    setBand: chainable(function (band) { return (this.band = band); }),
+    incVolume: chainable(function () { return this.volume++; })
   };
   let player = new Player();
   player
     .setBand('Animals As Leaders')
     .incVolume().incVolume().incVolume().incVolume().incVolume().incVolume();
 
   assert.equal(player.band, 'Animals As Leaders', 'passes arguments into chained');
   assert.equal(player.volume, 11, 'accepts no arguments in chain');
 };
 
+exports["test field"] = assert => {
+  let Num = field("constructor", 0);
+  assert.equal(Num.name, Number.name);
+  assert.ok(typeof(Num), "function");
+
+  let x = field("x");
+
+  [
+    [field("foo", { foo: 1 }), 1],
+    [field("foo")({ foo: 1 }), 1],
+    [field("bar", {}), undefined],
+    [field("bar")({}), undefined],
+    [field("hey", undefined), undefined],
+    [field("hey")(undefined), undefined],
+    [field("how", null), null],
+    [field("how")(null), null],
+    [x(1), undefined],
+    [x(undefined), undefined],
+    [x(null), null],
+    [x({ x: 1 }), 1],
+    [x({ x: 2 }), 2],
+  ].forEach(([actual, expected]) => assert.equal(actual, expected));
+};
+
+exports["test query"] = assert => {
+  let Num = query("constructor", 0);
+  assert.equal(Num.name, Number.name);
+  assert.ok(typeof(Num), "function");
+
+  let x = query("x");
+  let xy = query("x.y");
+
+  [
+    [query("foo", { foo: 1 }), 1],
+    [query("foo")({ foo: 1 }), 1],
+    [query("foo.bar", { foo: { bar: 2 } }), 2],
+    [query("foo.bar")({ foo: { bar: 2 } }), 2],
+    [query("foo.bar", { foo: 1 }), undefined],
+    [query("foo.bar")({ foo: 1 }), undefined],
+    [x(1), undefined],
+    [x(undefined), undefined],
+    [x(null), null],
+    [x({ x: 1 }), 1],
+    [x({ x: 2 }), 2],
+    [xy(1), undefined],
+    [xy(undefined), undefined],
+    [xy(null), null],
+    [xy({ x: 1 }), undefined],
+    [xy({ x: 2 }), undefined],
+    [xy({ x: { y: 1 } }), 1],
+    [xy({ x: { y: 2 } }), 2]
+  ].forEach(([actual, expected]) => assert.equal(actual, expected));
+};
+
+exports["test isInstance"] = assert => {
+  function X() {}
+  function Y() {}
+  let isX = isInstance(X);
+
+  [
+    isInstance(X, new X()),
+    isInstance(X)(new X()),
+    !isInstance(X, new Y()),
+    !isInstance(X)(new Y()),
+    isX(new X()),
+    !isX(new Y())
+  ].forEach(x => assert.ok(x));
+};
+
+exports["test is"] = assert => {
+
+  assert.deepEqual([ 1, 0, 1, 0, 1 ].map(is(1)),
+                   [ true, false, true, false, true ],
+                   "is can be partially applied");
+
+  assert.ok(is(1, 1));
+  assert.ok(!is({}, {}));
+  assert.ok(is()(1)()(1), "is is curried");
+  assert.ok(!is()(1)()(2));
+};
+
+exports["test isnt"] = assert => {
+
+  assert.deepEqual([ 1, 0, 1, 0, 1 ].map(isnt(0)),
+                   [ true, false, true, false, true ],
+                   "is can be partially applied");
+
+  assert.ok(!isnt(1, 1));
+  assert.ok(isnt({}, {}));
+  assert.ok(!isnt()(1)()(1));
+  assert.ok(isnt()(1)()(2));
+};
+
 require('test').run(exports);