Completed
Push — master ( 97f147...5a66ad )
by Amine
02:11
created

functions.php ➔ identity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Tarsana\Functional;
2
/**
3
 * This file contains functions dealing with functions.
4
 */
5
6
use Tarsana\Functional\Exceptions\InvalidArgument;
7
8
/**
9
 * Adds the `Tarsana\Functional\` namespace to a function name.
10
 * This is useful to pass non-curried functions as parameter.
11
 *
12
 * @internal
13
 * @signature String -> Sring
14
 * @param  string $name
15
 * @return string
16
 */
17
function _f($name) {
18
    return "Tarsana\\Functional\\{$name}";
19
}
20
21
/**
22
 * Gets the number of arguments of a function.
23
 *
24
 * @internal
25
 * @signature (* -> *) -> Number
26
 * @param  callable $fn
27
 * @return int
28
 */
29
function _number_of_args($fn) {
30
    $reflector = is_array($fn) ?
31
        new \ReflectionMethod($fn[0], $fn[1]) :
32
        new \ReflectionFunction($fn);
33
    return $reflector->getNumberOfRequiredParameters();
34
}
35
36
/**
37
 * Checks if `$a` is am argument placeholder.
38
 *
39
 * @internal
40
 * @signature * -> Boolean
41
 * @param  mixed  $a
42
 * @return boolean
43
 */
44
function _is_placeholder($a) {
45
    return $a instanceof Placeholder;
46
}
47
48
/**
49
 * Adds new given arguments to the list of bound arguments while filling placeholders.
50
 *
51
 * @internal
52
 * @signature Number -> [a] -> [a] -> [a]
53
 * @param  int   $fnArgsCount
54
 * @param  array $boundArgs
55
 * @param  array $givenArgs
56
 * @return array
57
 */
58
function _merge_args($fnArgsCount, $boundArgs, $givenArgs) {
59
    $addArgument = function($currentBoundArgs, $arg) use($fnArgsCount) {
60
        $currentBoundArgsCount = count($currentBoundArgs);
61
        $placeholderPosition = 0;
62
        while($placeholderPosition < $currentBoundArgsCount && !_is_placeholder($currentBoundArgs[$placeholderPosition]))
63
            $placeholderPosition ++;
64
        if ($currentBoundArgsCount < $fnArgsCount || $placeholderPosition == $currentBoundArgsCount) {
65
            $currentBoundArgs[] = $arg;
66
        } else { // There is a placeholder and number of bound args >= $fnArgsCount
67
            $currentBoundArgs[$placeholderPosition] = $arg;
68
        }
69
        return $currentBoundArgs;
70
    };
71
72
    return array_reduce($givenArgs, $addArgument, $boundArgs);
73
}
74
75
/**
76
 * Returns the curried version of a function with some arguments bound to it.
77
 *
78
 * @internal
79
 * @signature (* -> *) -> Number -> [*] -> (* -> *)
80
 * @param  callable $fn
81
 * @param  int $argsCount
82
 * @param  array  $boundArgs
83
 * @return callable
84
 */
85
function _curried_function($fn, $argsCount, $boundArgs = []) {
86
    return function() use($fn, $argsCount, $boundArgs) {
87
        $boundArgs = _merge_args($argsCount, $boundArgs, func_get_args());
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $boundArgs, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
88
        $numberOfPlaceholders = count(array_filter($boundArgs, _f('_is_placeholder')));
89
        $numberOfGivenArgs = count($boundArgs) - $numberOfPlaceholders;
90
        if ($numberOfGivenArgs >= $argsCount)
91
            return call_user_func_array($fn, $boundArgs);
92
        return _curried_function($fn, $argsCount, $boundArgs);
93
    };
94
}
95
96
/**
97
 * Returns a curried equivalent of the provided function.
98
 * ```php
99
 * $add = curry(function($x, $y){
100
 *     return $x + $y;
101
 * });
102
 * $addFive = $add(5); // a function
103
 * $addFive(5); // 10
104
 * $add(5, 5) // 10
105
 * ```
106
 *
107
 * @signature (* -> a) -> (* -> a)
108
 * @param  callable $fn
109
 * @return callable
110
 */
111
function curry($fn) {
112
    return _curried_function($fn, _number_of_args($fn));
113
}
114
115
/**
116
 * Argument placeholder to use with curried functions.
117
 * ```php
118
 * $minus = curry(function ($x, $y) { return $x - $y; });
119
 * $decrement = $minus(__(), 1);
120
 * $decrement(10) // 9
121
 *
122
 * $reduce = curry('array_reduce');
123
 * $sum = $reduce(__(), 'Tarsana\Functional\plus');
124
 * $sum([1, 2, 3, 4], 0) // 10
125
 * ```
126
 *
127
 * @signature * -> Placeholder
128
 * @return \Cypress\Curry\Placeholder
129
 */
130
function __() {
131
    return Placeholder::get();
132
}
133
134
/**
135
 * Non curried version of apply for internal use.
136
 *
137
 * @internal
138
 * @param  callable $fn
139
 * @param  array    $args
140
 * @return mixed
141
 */
142
function _apply($fn, $args) {
143
    return call_user_func_array($fn, $args);
144
}
145
146
/**
147
 * Apply the provided function to the list of arguments.
148
 * ```php
149
 * apply('strlen', ['Hello']) // 5
150
 * $replace = apply('str_replace');
151
 * $replace(['l', 'o', 'Hello']) // 'Heooo'
152
 * ```
153
 *
154
 * @signature (*... -> a) -> [*] -> a
155
 * @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...
156
 * @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...
157
 * @return mixed
158
 */
159
function apply() {
160
    return _apply(curry(f('_apply')), func_get_args());
161
}
162
163
/**
164
 * Performs left-to-right function composition.
165
 * The leftmost function may have any arity;
166
 * the remaining functions must be unary.
167
 * The result of pipe is curried.
168
 * **Calling pipe() without any argument returns the `identity` function**
169
 * ```php
170
 * function add($x, $y) { return $x + $y; }
171
 * $double = function($x) { return 2 * $x; };
172
 * $addThenDouble = pipe('add', $double);
173
 * $addThenDouble(2, 3) // 10
174
 * ```
175
 *
176
 * @signature (((a, b, ...) -> o), (o -> p), ..., (y -> z)) -> ((a, b, ...) -> z)
177
 * @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...
178
 * @return callable
179
 */
180
function pipe() {
181
    $fns = func_get_args();
182
    if(count($fns) < 1)
183
        throw new InvalidArgument("pipe() requires at least one argument");
184
    return curry(function () use ($fns) {
185
        $result = _apply(array_shift($fns), func_get_args());
186
        foreach ($fns as $fn) {
187
            $result = $fn($result);
188
        }
189
        return $result;
190
    });
191
}
192
193
/**
194
 * A function that takes one argument and
195
 * returns exactly the given argument.
196
 * ```php
197
 * identity('Hello') // 'Hello'
198
 * identity([1, 2, 3]) // [1, 2, 3]
199
 * identity(null) // null
200
 * ```
201
 *
202
 * @signature * -> *
203
 * @return mixed
204
 */
205
function identity() {
206
    return apply(curry(function($value) {
207
        return $value;
208
    }), func_get_args());
209
}
210
211
/**
212
 * Returns a function which whenever called will return the specified value.
213
 * ```php
214
 * $five = give(5);
215
 * $five(); // 5
216
 * $null = give(null);
217
 * $null(); // null
218
 * ```
219
 *
220
 * @signature a -> (* -> a)
221
 * @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...
222
 * @return callable
223
 */
224
function give() {
225
    $give = function($value) {
226
        return function() use($value) {
227
            return $value;
228
        };
229
    };
230
    return apply(curry($give), func_get_args());
231
}
232
233
/**
234
 * Takes many predicates and returns a new predicate that
235
 * returns `true` only if all predicates are satisfied.
236
 * If no predicate is given as argument, this function
237
 * will return an always passing predicate.
238
 * ```php
239
 * $betweenOneAndTen = all(gte(__(), 1), lte(__(), 10));
240
 * $betweenOneAndTen(5); // true
241
 * $betweenOneAndTen(0); // false
242
 * $alwaysTrue = all();
243
 * $alwaysTrue(1); // true
244
 * $alwaysTrue(null); // true
245
 * ```
246
 *
247
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
248
 * @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...
249
 * @return callable
250
 */
251 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...
252
    $predicates = func_get_args();
253
    return function($value) use($predicates) {
254
        return reduce(function($result, $predicate) use($value) {
255
            return $result && $predicate($value);
256
        }, true, $predicates);
257
    };
258
}
259
260
/**
261
 * Takes many predicates and returns a new predicate that
262
 * returns `true` if any of the predicates is satisfied.
263
 * If no predicate is given as argument, this function
264
 * will return an always passing predicate.
265
 * ```php
266
 * $startsOrEndsWith = function($text) {
267
 *     return any(startsWith($text), endsWth($text));
268
 * };
269
 * $test = $startsOrEndsWith('b');
270
 * $test('bar'); // true
271
 * $test('bob'); // true
272
 * $test('foo'); // false
273
 * $alwaysTrue = any();
274
 * $alwaysTrue(1); // true
275
 * $alwaysTrue(null); // true
276
 * ```
277
 *
278
 * @signature ((a -> Boolean), ..., (a -> Boolean)) -> (a -> Boolean)
279
 * @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...
280
 * @return callable
281
 */
282 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...
283
    $predicates = func_get_args();
284
    return function($value) use($predicates) {
285
        return reduce(function($result, $predicate) use($value) {
286
            return $result || $predicate($value);
287
        }, true, $predicates);
288
    };
289
}
290
291