aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/lib/sdk/lang/functional.js
blob: 3595a27043b5bf7e8579718803b3b3fad9cfc9af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* 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
// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
// those goes to him.

"use strict";

module.metadata = {
  "stability": "unstable"
};

const { setTimeout } = require("../timers");

/**
 * 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.
 */
function method(lambda) {
  return function method() {
    return lambda.apply(null, [this].concat(Array.slice(arguments)));
  }
}
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 `setTimeout(function() { ... }, 0)`, 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()
    setTimeout(invoke, 0, f, arguments, this);
}
exports.defer = defer;
// Exporting `remit` alias as `defer` may conflict with promises.
exports.remit = defer;

/**
 * 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);
exports.invoke = invoke;

/**
 * Curries a function with the arguments given.
 *
 * @param {Function} fn
 *    The function to curry
 *
 * @returns The function curried
 */
function curry(fn) {
  if (typeof fn !== "function")
    throw new TypeError(String(fn) + " is not a function");

  let args = Array.slice(arguments, 1);

  return function() fn.apply(this, args.concat(Array.slice(arguments)));
}
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;
    while (0 <= --index)
      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
 * conditionally execute the original function.
 * @example
 *
 *  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)))
};
exports.wrap = wrap;

/**
 * Returns the same value that is used as the argument. In math: f(x) = x
 */
function 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) {
  let memo = Object.create(null);
  hasher = hasher || identity;
  return function memoizer() {
    let key = hasher.apply(this, arguments);
    return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments));
  };
}
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);
};
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) {
  let ran = false, cache;
  return function() ran ? cache : (ran = true, cache = f.apply(this, arguments))
};
exports.once = once;
// export cache as once will may be conflicting with event once a lot.
exports.cache = once;