Completed
Pull Request — master (#3)
by Amine
02:54
created

functions.php ➔ curry()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 1
dl 0
loc 10
rs 8.8571
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
 * // A closure
13
 * $add = F\curry(function($x, $y) {
14
 *     return $x + $y;
15
 * });
16
 *
17
 * $add(1, 2); //=> 3
18
 * $addFive = $add(5); // this is a function
19
 * $addFive(1); //=> 6
20
 *
21
 * $sum = F\curry(function() use($add) {
22
 *     $numbers = func_get_args();
23
 *     return F\reduce($add, 0, $numbers);
24
 * });
25
 *
26
 * $sum(); //=> 0
27
 * $sum(1, 2, 3, 4); //=> 10
28
 * ```
29
 *
30
 * @signature (* -> a) -> (* -> a)
31
 * @param  callable $fn
32
 * @return callable
33
 */
34
function curry($fn) {
35
    $n = _number_of_args($fn);
36
    switch($n) {
37
        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...
38
        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...
39
        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...
40
        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...
41
    }
42
    return _curry_n($fn, $n);
43
}
44
45
/**
46
 * Argument placeholder to use with curried functions.
47
 *
48
 * ```php
49
 * $minus = F\curry(function ($x, $y) { return $x - $y; });
50
 * $decrement = $minus(F\__(), 1);
51
 * $decrement(10); //=> 9
52
 *
53
 * $reduce = F\curry('array_reduce');
54
 * $sum = $reduce(F\__(), F\plus());
55
 * $sum([1, 2, 3, 4], 0); //=> 10
56
 * ```
57
 *
58
 * @signature * -> Placeholder
59
 * @return Tarsana\Functional\Placeholder
60
 */
61
function __() {
62
    return Placeholder::get();
63
}
64
65
/**
66
 * Apply the provided function to the list of arguments.
67
 *
68
 * ```php
69
 * F\apply('strlen', ['Hello']); //=> 5
70
 * $replace = F\apply('str_replace');
71
 * $replace(['l', 'o', 'Hello']); //=> 'Heooo'
72
 * ```
73
 *
74
 * @signature (*... -> a) -> [*] -> a
75
 * @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...
76
 * @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...
77
 * @return mixed
78
 */
79
function apply() {
80
    static $apply = false;
81
    $apply = $apply ?: curry(_f('_apply'));
82
    return _apply($apply, func_get_args());
83
}
84
85
/**
86
 * Performs left-to-right function composition.
87
 *
88
 * The leftmost function may have any arity;
89
 * the remaining functions must be unary.
90
 * The result of pipe is **not curried**.
91
 * **Calling pipe() without any argument returns the `identity` function**.
92
 *
93
 * ```php
94
 * $double = function($x) { return 2 * $x; };
95
 * $addThenDouble = F\pipe(F\plus(), $double);
96
 * $addThenDouble(2, 3); //=> 10
97
 * ```
98
 *
99
 * @signature (((a, b, ...) -> o), (o -> p), ..., (y -> z)) -> ((a, b, ...) -> z)
100
 * @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...
101
 * @return callable
102
 */
103
function pipe() {
104
    $fns = func_get_args();
105
    if(count($fns) < 1)
106
        return identity();
107
    return function () use ($fns) {
108
        $result = _apply(array_shift($fns), func_get_args());
109
        foreach ($fns as $fn) {
110
            $result = $fn($result);
111
        }
112
        return $result;
113
    };
114
}
115
116
/**
117
 * A function that takes one argument and
118
 * returns exactly the given argument.
119
 *
120
 * ```php
121
 * F\identity('Hello'); //=> 'Hello'
122
 * F\identity([1, 2, 3]); //=> [1, 2, 3]
123
 * F\identity(null); //=> null
124
 * ```
125
 *
126
 * @signature * -> *
127
 * @return mixed
128
 */
129
function identity() {
130
    static $identity = false;
131
    $identity = $identity ?: curry(function($value) {
132
        return $value;
133
    });
134
    return _apply($identity, func_get_args());
135
}
136
137
/**
138
 * Returns a function which whenever called will return the specified value.
139
 *
140
 * ```php
141
 * $five = F\give(5);
142
 * $five(); //=> 5
143
 * $null = F\give(null);
144
 * $null(); //=> null
145
 * ```
146
 *
147
 * @signature a -> (* -> a)
148
 * @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...
149
 * @return callable
150
 */
151
function give() {
152
    static $give = false;
153
    $give = $give ?: curry(function($value) {
154
        return function() use($value) {
155
            return $value;
156
        };
157
    });
158
    return _apply($give, func_get_args());
159
}
160
161
/**
162
 * Takes many predicates and returns a new predicate that
163
 * returns `true` only if all predicates are satisfied.
164
 *
165
 * If no predicate is given as argument, this function
166
 * will return an always passing predicate.
167
 * ```php
168
 * $betweenOneAndTen = F\all(F\lt(1), F\gt(10));
169
 * $betweenOneAndTen(5); //=> true
170
 * $betweenOneAndTen(0); //=> false
171
 * $alwaysTrue = F\all();
172
 * $alwaysTrue(1); //=> true
173
 * $alwaysTrue(null); //=> true
174
 * ```
175
 *
176
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
177
 * @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...
178
 * @return callable
179
 */
180 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...
181
    $predicates = func_get_args();
182
    return _curry_one(function($value) use(&$predicates) {
183
        foreach ($predicates as $predicate) {
184
            if (! $predicate($value))
185
                return false;
186
        }
187
        return true;
188
    });
189
}
190
191
/**
192
 * Takes many predicates and returns a new predicate that
193
 * returns `true` if any of the predicates is satisfied.
194
 *
195
 * If no predicate is given as argument, this function
196
 * will return an always non-passing predicate.
197
 * ```php
198
 * $startsOrEndsWith = function($text) {
199
 *     return F\any(F\startsWith($text), F\endsWith($text));
200
 * };
201
 * $test = $startsOrEndsWith('b');
202
 * $test('bar'); //=> true
203
 * $test('bob'); //=> true
204
 * $test('foo'); //=> false
205
 * $alwaysFlase = F\any();
206
 * $alwaysFlase(1); //=> false
207
 * $alwaysFlase(null); //=> false
208
 * ```
209
 *
210
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
211
 * @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...
212
 * @return callable
213
 */
214 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...
215
    $predicates = func_get_args();
216
    return _curry_one(function($value) use(&$predicates) {
217
        foreach ($predicates as $predicate) {
218
            if ($predicate($value))
219
                return true;
220
        }
221
        return false;
222
    });
223
}
224
225
/**
226
 * Takes a function `f` and returns a function `g` so that if `f` returns
227
 * `x` for some arguments; `g` will return `! x` for the same arguments.
228
 *
229
 * Note that `complement($fn) == pipe($fn, not())`, So the resulting function is not curried !.
230
 * ```php
231
 * $isOdd = function($number) {
232
 *     return 1 == $number % 2;
233
 * };
234
 *
235
 * $isEven = F\complement($isOdd);
236
 *
237
 * $isEven(5); //=> false
238
 * $isEven(8); //=> true
239
 * ```
240
 *
241
 * @signature (* -> ... -> *) -> (* -> ... -> Boolean)
242
 * @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...
243
 * @return callable
244
 */
245
function complement() {
246
    static $complement = false;
247
    $complement = $complement ?: curry(function($fn) {
248
        return function() use($fn) {
249
            return !_apply($fn, func_get_args());
250
        };
251
    });
252
    return _apply($complement, func_get_args());
253
}
254
255
/**
256
 * Takes a function telling if the first argument is less then the second, and return a compare function.
257
 *
258
 * A compare function returns `-1`, `0`, or `1` if the first argument is considered
259
 * to be respectively less than, equal to, or greater than the second.
260
 * ```php
261
 * $users = [
262
 *     ['name' => 'foo', 'age' => 21],
263
 *     ['name' => 'bar', 'age' => 11],
264
 *     ['name' => 'baz', 'age' => 15]
265
 * ];
266
 *
267
 * usort($users, F\comparator(function($a, $b){
268
 *     return $a['age'] < $b['age'];
269
 * }));
270
 *
271
 * F\map(F\get('name'), $users); //=> ['bar', 'baz', 'foo']
272
 * ```
273
 *
274
 * @signature (a -> a -> Boolean) -> (a -> a -> Number)
275
 * @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...
276
 * @return callable
277
 */
278
function comparator() {
279
    static $comparator = false;
280
    $comparator = $comparator ?: curry(function($fn) {
281
        return function($a, $b) use($fn) {
282
            if ($fn($a, $b)) return -1;
283
            if ($fn($b, $a)) return 1;
284
            return 0;
285
        };
286
    });
287
    return _apply($comparator, func_get_args());
288
}
289