Skip to content

Commit

Permalink
Add a lift method to Option
Browse files Browse the repository at this point in the history
Lifting allows to use any already existing function with Options as parameters. If any of the parameter is empty, we consider that we cannot call the original function and return None.

Closes #30.

Co-Authored-By: Gilles Crettenand <[email protected]>
  • Loading branch information
GrahamCampbell and krtek4 committed Nov 30, 2019
1 parent 583d328 commit b9f9302
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/PhpOption/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,43 @@ public static function ensure($value, $noneValue = null)
}
}

/**
* Lift a function so that it accepts Option as parameters.
*
* We return a new closure that wraps the original callback. If any of the
* parameters passed to the lifted function is empty, the function will
* return a value of None. Otherwise, we will pass all parameters to the
* original callback and return the value inside a new Option, unless an
* Option is returned from the function, in which case, we use that.
*
* @param callable $callback
* @param mixed $noneValue
*
* @return callable
*/
public static function lift($callback, $noneValue = null)
{
return function() use ($callback, $noneValue)
{
$args = func_get_args();

// if at least one parameter is empty, return None
if (array_reduce($args, function ($status, Option $o) {
return $o->isEmpty() ? true : $status;
}, false)) {
return None::create();
}

$args = array_map(function (Option $o) {
// it is safe to do so because the fold above checked
// that all arguments are of type Some
return $o->get();
}, $args);

return self::ensure(call_user_func_array($callback, $args), $noneValue);
};
}

/**
* Returns the value if available, or throws an exception otherwise.
*
Expand Down
31 changes: 31 additions & 0 deletions tests/PhpOption/Tests/OptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,35 @@ public function testOrElseWithMultipleAlternatives()

$this->assertEquals('foo', $a->orElse($returns)->orElse($throws)->get());
}

public function testLift()
{
$f = function ($a, $b) {
return $a + $b;
};

$fL = Option::lift($f);

$a = new Some(1);
$b = new Some(5);
$n = None::create();

$this->assertEquals(6, $fL($a, $b)->get());
$this->assertEquals(6, $fL($b, $a)->get());
$this->assertEquals($n, $fL($a, $n));
$this->assertEquals($n, $fL($n, $a));
$this->assertEquals($n, $fL($n, $n));
}

public function testLiftDegenerate()
{
$f = function () {
};

$fL1 = Option::lift($f);
$fL2 = Option::lift($f, false);

$this->assertEquals(None::create(), $fL1());
$this->assertEquals(Some::create(null), $fL2());
}
}

0 comments on commit b9f9302

Please sign in to comment.