Functional Programming Library for PHP
This library provides a functional-programming core library: Endofunk
, that adds many of the data types needed to employ a functional programming style in your codebase; more data types will be added in due course.
The following is a list of the functional data types included in Endofunk
.
Type | Overview |
---|---|
Identity | The Identity type is a trivial type to access functor, monad, applicative functor, etc. algebras. |
Maybe | The Maybe type encapsulates an optional value. A value of type Maybe a either contains a value of type a (represented as Just a), or it is empty (represented as Nothing) |
Either | The Either type encapsulates a logical disjunction of two possibilities: either Left or Right. |
Result | The Result type is similar to the Either type except that the left disjunction is fixed to capture of a PHP Exception. |
Validation | The Validation data type is isomorphic to Either, but has an instance of Applicative that accumulates on the error side. |
Reader | The Reader type (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment. |
All types support Functor
, Applicative Functor
and Monad
; with monadic lifters
, applicative lifters
, and Kleisli monadic composition
.
The base prelude combinators, e.g. curry, k-combinator, etc... are for the most part a copy of the Vector PHP FP Library developed by joseph-walker. The code for Vector
has however been fully integrated into this framework with a few changes to better fit in with the operation of the monadic data types; meaning there is no dependency on Vector
only PHPrelude
.
...is essentially free with all functions that have the Module
extension.
$addOne = Arrays::map(function($a) { return $a + 1; });
$addOne([1, 2, 3]); // [2, 3, 4]
$addSix = Lambda::compose(Math::add(4), Math::add(2));
$addSix(4); // 10;
$f = Lambda::compose(Strings::toUppercase(), Strings::trim());
$v = Identity::of("hello ")
->map($f)
->Match(function ($v) {
echo Strings::log("Identity", $v);
});
// -------------------------------------------------------------- Identity ----
// string(5) "HELLO"
$incrementM = function ($v) {
return Identity::of($v + 1);
};
$squareM = function ($v) {
return Identity::of($v * $v);
};
$incsquare = Identity::compose($incrementM, $squareM);
Identity::of(2)
->bind($incsquare)
->Match(function ($v) {
echo Strings::log("Identity", $v);
});
// -------------------------------------------------------------- Identity ----
// int(9)
$g = function (string $name, string $surname): string {
return $name . ' ' . $surname;
};
$gc = Module::curry($g);
Identity::of($gc)
->fapply(Identity::of("Jack"))
->fapply(Identity::of("Sprat"))
->Match(function ($v) {
echo Strings::log("Identity", $v);
});
\\ ------------------------------------------------------- Identity ----
\\ string(10) "Jack Sprat"
LiftA(x) supports arities from 1 to 9.
$g = function (string $name, string $surname): string {
return $name . ' ' . $surname;
};
Identity::liftA2($g, Identity::of("Jack"), Identity::of("Sprat")) // liftA(x) auto currys the function
->Match(function ($v) {
echo Strings::log("Identity", $v);
});
\\ ------------------------------------------------------- Identity ----
\\ string(10) "Jack Sprat"
class SQL extends Module {
protected static function __connect(string $dsn, string $database, string $host, string $username, string $password): Result {
return Result::try (function () use ($dsn, $database, $host, $username, $password) {
return new PDO("$dsn:dbname=$database;host=$host", $username, $password);
});
}
protected static function __query($sql): callable {
return function ($conn) use ($sql) {
return Result::try (function () use ($conn, $sql) {
$result = $conn->query($sql)->fetchAll(PDO::FETCH_ASSOC);
if (!$result) {
throw new Exception($mysqli->error0, $severity, $file, $line);
}
return [$conn, $result];
});
};
}
protected static function __close(): callable {
return function ($conn) {
return Result::try (function () use ($conn) {
$conn[0] = null;
return $conn[1];
});
};
}
}
SQL::connect("mysql", "sakila", "127.0.0.1", "username", "password")
->bind(SQL::query("call sakila.SP_actors()"))
->bind(SQL::close())
->match(function ($value) {
echo json_encode($value) . "\n";
}, function ($error) {
echo $error . "\n";
});
// [{"actor_id":"1","first_name":"PENELOPE","last_name":"GUINESS","last_update":"2006-02-15 04:34:33"},
// ...
// ...
// {"actor_id":"200","first_name":"THORA","last_name":"TEMPLE","last_update":"2006-02-15 04:34:33"}]
$f = function () {
throw new Exception("Value must be 1 or below");
return 1;
};
Result::try ($f)
->Match(function ($v) {
echo Strings::log("Result", $v);
});
// ------------------------------------------------------- Result ----
// string(113) "Error Message: Value must be 1 or below
// Exception code: 0
// Line No: 59
// Filename: /Users/JackSprat/Documents/web/index.php"