Completed
Push — master ( f97cd2...687d27 )
by Amine
04:25
created

functions.php ➔ compose()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 2
nop 0
dl 12
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Tarsana\Functional;
2
3
/**
4
 * Functions dealing with functions.
5
 * @file
6
 */
7
8
/**
9
 * Returns a curried equivalent of the provided function.
10
 *
11
 * ```php
12
 * $add = F\curry(function($x, $y) {
13
 *     return $x + $y;
14
 * });
15
 *
16
 * $add(1, 2); //=> 3
17
 * $addFive = $add(5); // this is a function
18
 * $addFive(1); //=> 6
19
 *
20
 * $data = [1, 2, 3, 4, 5];
21
 * $slice = F\curry('array_slice');
22
 * $itemsFrom = $slice($data);
23
 * $itemsFrom(2); //=> [3, 4, 5]
24
 * $itemsFrom(1, 2); //=> [2, 3, 4, 5]
25
 * // Notice that optional arguments are ignored !
26
 *
27
 * $polynomial = F\curry(function($a, $b, $c, $x) {
28
 *     return $a * $x * $x + $b * $x + $c;
29
 * });
30
 * $f = $polynomial(0, 2, 1); // 2 * $x + 1
31
 * $f(5); //=> 11
32
 * ```
33
 *
34
 * @signature (* -> a) -> (* -> a)
35
 * @param  callable $fn
36
 * @return callable
37
 */
38
function curry($fn) {
39
    $n = _number_of_args($fn);
40
    switch($n) {
41
        case 0: return $fn;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
42
        case 1: return _curry_one($fn);
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
43
        case 2: return _curry_two($fn);
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
44
        case 3: return _curry_three($fn);
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
45
    }
46
    return _curry_n($fn, $n);
47
}
48
49
/**
50
 * Argument placeholder to use with curried functions.
51
 *
52
 * ```php
53
 * $reduce = F\curry('array_reduce');
54
 * $sum = $reduce(F\__(), F\plus());
55
 * $sum([1, 2, 3, 4], 0); //=> 10
56
 *
57
 * $polynomial = F\curry(function($a, $b, $c, $x) {
58
 *     return $a * $x * $x + $b * $x + $c;
59
 * });
60
 *
61
 * $multiplier = $polynomial(0, F\__(), 0, F\__());
62
 * $triple = $multiplier(3);
63
 * $triple(5); //=> 15
64
 * $multipleOfThree = $multiplier(F\__(), 3);
65
 * $multipleOfThree(4); //=> 12
66
 * ```
67
 *
68
 * @signature * -> Placeholder
69
 * @return Tarsana\Functional\Placeholder
70
 */
71
function __() {
72
    return Placeholder::get();
73
}
74
75
/**
76
 * Apply the provided function to the list of arguments.
77
 *
78
 * ```php
79
 * F\apply('strlen', ['Hello']); //=> 5
80
 * $replace = F\apply('str_replace');
81
 * $replace(['l', 'o', 'Hello']); //=> 'Heooo'
82
 * ```
83
 *
84
 * @signature (*... -> a) -> [*] -> a
85
 * @param  callable $fn
0 ignored issues
show
Bug introduced by
There is no parameter named $fn. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
86
 * @param  array    $args
0 ignored issues
show
Bug introduced by
There is no parameter named $args. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
87
 * @return mixed
88
 */
89
function apply() {
90
    static $apply = false;
91
    $apply = $apply ?: curry(_f('_apply'));
92
    return _apply($apply, func_get_args());
93
}
94
95
/**
96
 * Performs left-to-right function composition.
97
 *
98
 * The leftmost function may have any arity;
99
 * the remaining functions must be unary.
100
 * The result of pipe is **not curried**.
101
 * **Calling pipe() without any argument returns the `identity` function**.
102
 *
103
 * ```php
104
 * $double = function($x) { return 2 * $x; };
105
 * $addThenDouble = F\pipe(F\plus(), $double);
106
 * $addThenDouble(2, 3); //=> 10
107
 * ```
108
 *
109
 * @signature (((a, b, ...) -> o), (o -> p), ..., (y -> z)) -> ((a, b, ...) -> z)
110
 * @param  callable $fns...
0 ignored issues
show
Bug introduced by
There is no parameter named $fns.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
111
 * @return callable
112
 */
113 View Code Duplication
function pipe() {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114
    $fns = func_get_args();
115
    if(count($fns) < 1)
116
        return identity();
117
    return function () use ($fns) {
118
        $result = _apply(array_shift($fns), func_get_args());
119
        foreach ($fns as $fn) {
120
            $result = $fn($result);
121
        }
122
        return $result;
123
    };
124
}
125
126
/**
127
 * Performs right-to-left function composition.
128
 *
129
 * The rightmost function may have any arity;
130
 * the remaining functions must be unary.
131
 * The result of `compose` is **not curried**.
132
 * **Calling compose() without any argument returns the `identity` function**.
133
 *
134
 * ```php
135
 * $double = function($x) { return 2 * $x; };
136
 * $addThenDouble = F\compose($double, F\plus());
137
 * $addThenDouble(2, 3); //=> 10
138
 * ```
139
 *
140
 * @signature (((a, b, ...) -> o), (o -> p), ..., (y -> z)) -> ((a, b, ...) -> z)
141
 * @param  callable $fns...
0 ignored issues
show
Bug introduced by
There is no parameter named $fns.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
142
 * @return callable
143
 */
144 View Code Duplication
function compose() {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
    $fns = array_reverse(func_get_args());
146
    if(count($fns) < 1)
147
        return identity();
148
    return function () use ($fns) {
149
        $result = _apply(array_shift($fns), func_get_args());
150
        foreach ($fns as $fn) {
151
            $result = $fn($result);
152
        }
153
        return $result;
154
    };
155
}
156
157
/**
158
 * A function that takes one argument and
159
 * returns exactly the given argument.
160
 *
161
 * ```php
162
 * F\identity('Hello'); //=> 'Hello'
163
 * F\identity([1, 2, 3]); //=> [1, 2, 3]
164
 * F\identity(null); //=> null
165
 * ```
166
 *
167
 * @signature * -> *
168
 * @return mixed
169
 */
170
function identity() {
171
    static $identity = false;
172
    $identity = $identity ?: curry(function($value) {
173
        return $value;
174
    });
175
    return _apply($identity, func_get_args());
176
}
177
178
/**
179
 * Returns a function which whenever called will return the specified value.
180
 *
181
 * ```php
182
 * $five = F\give(5);
183
 * $five(); //=> 5
184
 * $null = F\give(null);
185
 * $null(); //=> null
186
 * ```
187
 *
188
 * @signature a -> (* -> a)
189
 * @param  mixed $value
0 ignored issues
show
Bug introduced by
There is no parameter named $value. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
190
 * @return callable
191
 */
192
function give() {
193
    static $give = false;
194
    $give = $give ?: curry(function($value) {
195
        return function() use($value) {
196
            return $value;
197
        };
198
    });
199
    return _apply($give, func_get_args());
200
}
201
202
/**
203
 * Takes many predicates and returns a new predicate that
204
 * returns `true` only if all predicates are satisfied.
205
 *
206
 * If no predicate is given as argument, this function
207
 * will return an always passing predicate.
208
 * ```php
209
 * $betweenOneAndTen = F\all(F\lt(1), F\gt(10));
210
 * $betweenOneAndTen(5); //=> true
211
 * $betweenOneAndTen(0); //=> false
212
 * $alwaysTrue = F\all();
213
 * $alwaysTrue(1); //=> true
214
 * $alwaysTrue(null); //=> true
215
 * ```
216
 *
217
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
218
 * @param  callable $predicates...
0 ignored issues
show
Bug introduced by
There is no parameter named $predicates.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
219
 * @return callable
220
 */
221 View Code Duplication
function all() {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
    $predicates = func_get_args();
223
    return _curry_one(function($value) use(&$predicates) {
224
        foreach ($predicates as $predicate) {
225
            if (! $predicate($value))
226
                return false;
227
        }
228
        return true;
229
    });
230
}
231
232
/**
233
 * Takes many predicates and returns a new predicate that
234
 * returns `true` if any of the predicates is satisfied.
235
 *
236
 * If no predicate is given as argument, this function
237
 * will return an always non-passing predicate.
238
 * ```php
239
 * $startsOrEndsWith = function($text) {
240
 *     return F\any(F\startsWith($text), F\endsWith($text));
241
 * };
242
 * $test = $startsOrEndsWith('b');
243
 * $test('bar'); //=> true
244
 * $test('bob'); //=> true
245
 * $test('foo'); //=> false
246
 * $alwaysFlase = F\any();
247
 * $alwaysFlase(1); //=> false
248
 * $alwaysFlase(null); //=> false
249
 * ```
250
 *
251
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
252
 * @param  callable $predicates...
0 ignored issues
show
Bug introduced by
There is no parameter named $predicates.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
253
 * @return callable
254
 */
255 View Code Duplication
function any() {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
    $predicates = func_get_args();
257
    return _curry_one(function($value) use(&$predicates) {
258
        foreach ($predicates as $predicate) {
259
            if ($predicate($value))
260
                return true;
261
        }
262
        return false;
263
    });
264
}
265
266
/**
267
 * Takes a function `f` and returns a function `g` so that if `f` returns
268
 * `x` for some arguments; `g` will return `! x` for the same arguments.
269
 *
270
 * Note that `complement($fn) == pipe($fn, not())`, So the resulting function is not curried !.
271
 * ```php
272
 * $isOdd = function($number) {
273
 *     return 1 == $number % 2;
274
 * };
275
 *
276
 * $isEven = F\complement($isOdd);
277
 *
278
 * $isEven(5); //=> false
279
 * $isEven(8); //=> true
280
 * ```
281
 *
282
 * @signature (* -> ... -> *) -> (* -> ... -> Boolean)
283
 * @param  callable $fn
0 ignored issues
show
Bug introduced by
There is no parameter named $fn. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
284
 * @return callable
285
 */
286
function complement() {
287
    static $complement = false;
288
    $complement = $complement ?: curry(function($fn) {
289
        return function() use($fn) {
290
            return !_apply($fn, func_get_args());
291
        };
292
    });
293
    return _apply($complement, func_get_args());
294
}
295
296
/**
297
 * Takes a function telling if the first argument is less then the second, and return a compare function.
298
 *
299
 * A compare function returns `-1`, `0`, or `1` if the first argument is considered
300
 * to be respectively less than, equal to, or greater than the second.
301
 * ```php
302
 * $users = [
303
 *     ['name' => 'foo', 'age' => 21],
304
 *     ['name' => 'bar', 'age' => 11],
305
 *     ['name' => 'baz', 'age' => 15]
306
 * ];
307
 *
308
 * usort($users, F\comparator(function($a, $b){
309
 *     return $a['age'] < $b['age'];
310
 * }));
311
 *
312
 * F\map(F\get('name'), $users); //=> ['bar', 'baz', 'foo']
313
 * ```
314
 *
315
 * @signature (a -> a -> Boolean) -> (a -> a -> Number)
316
 * @param  callable $fn
0 ignored issues
show
Bug introduced by
There is no parameter named $fn. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
317
 * @return callable
318
 */
319
function comparator() {
320
    static $comparator = false;
321
    $comparator = $comparator ?: curry(function($fn) {
322
        return function($a, $b) use($fn) {
323
            if ($fn($a, $b)) return -1;
324
            if ($fn($b, $a)) return 1;
325
            return 0;
326
        };
327
    });
328
    return _apply($comparator, func_get_args());
329
}
330