[Из песочницы] И снова про монады в PHP

82bf9a89d0ce43c39caa40c2470ed7f4.pngПосле прочтения вот этого материала томным и прохладным вечером пятницы у меня осталось некоторое чувство неудовлетворенности и жжения где то снизу. Я сидел со рвением безумца обновлял комментарии в надежде, что найдется человек который скажет отчего же это происходит и я пойму что я не одинок. Но… увы этого не произошло. После чего я посетил сие творение и почувствовал тоже чувство и понял, что что-то нужно менять.

Разбор полетовВсе свое негодование я вылью на конкретную монаду, а именно на опциональные значения.Но для начала заглянем в код корневого класса Monad: public static function unit ($value) { if ($value instanceof static) { return $value; } return new static ($value); } И сразу же повергаемся в ужас — нельзя засунуть монаду в монаду!

Быстро устраняем эту несправедливость во всех функциях:

//Вобщем просто убираем эти проверки public static function unit ($value) { return new static ($value); } Как то и код сразу покрасивше стал (ибо зачем нам if’ы О_о).

Но так как у нас могут теперь быть монады в монадах надо позаботится о функциях для этих ситуаций. Собственно:

//Типо аппликативный функтор abstract public function fbind ($function, array $args = array ()); //Будет вытаскивать монаду из монады abstract public function flatten (); Итак теперь перейдем таки к монаде Maybe.

Что за ущемление прав NULL’я, это же тоже значение. И почему автор не наполнил ее дополнительным специфичным функционалом ума не приложу:

abstract class Maybe extends Monad { abstract public function extractOrElse ($val, array $args = array ()); } Как несложно догадаться этот метод возвращает нам внутреннее или переданное значение.Но зачем $args? Спросите вы. Да вобщем то, а почему бы и не передать функцию, которую нужно вычислить только в том случае, если это значение нам будет нужно. (Чертово отсутствие call-by-name!)

Итак, теперь опишем таки классы Just и Nothing:

class Just extends Maybe {

public function extractOrElse ($val, array $args = array ()) { return $this→value; }

public function fbind ($function, array $args = array ()) { $res = $this→runCallback ($function, $this→value, $args) if (res instanceof Maybe) return $res; else throw new \InvalidArgumentException ('Returned value must be an instanceof Maybe monad'); }

public function flatten () { if ($this→value instanceof Maybe) return $this→value; else throw new Exception ('Value of just is not an instance of Maybe monad'); }

} class Nothing extends Maybe {

protected static $_instance = NULL;

final private function __construct () { }

final private function __clone () { }

final public static function getInstance (){ if (null!== static::$_instance){ return static::$_instance; } static::$_instance = new static (); return static::$_instance; }

public function extractOrElse ($val, array $args = array ()) { if (is_callable ($val) || $val instanceof Closure) return call_user_func_array ($val, $args); else return $val }

public function fbind ($function, array $args = array ()) { return $this; }

public function flatten () { throw new Exception («Nothing flatten call»); }

} Как читатель мог заметить, это Singleton. Ну, а действительно зачем нам много «ничего»?

Пример Разберем аналогичный пример как и в предшествующей статье.Нам нужно получить имя родителя родителя.Вот пример из той статьи:

function getGrandParentName ($item) { $monad = new Maybe ($item); $getParent = function ($item) { // может быть null, но нам уже без разницы! return $item→getParent (); }; $getName = function ($item) { return $item→getName (); } return $monad →bind ($getParent) →bind ($getParent) →bind ($getName) →extract (); } Я же считаю что на вход функции должна уже идти монада Maybe коль уж возможно ситуация что значения может и не быть. Ну и конечно же у элемента может не быть родителя, что как бы намекает нам о типе значения getParent. И тогда выходит:

function getGrandParentName ($item) { $getParent = function ($item) { return $item→getParent (); }; $getName = function ($item) { return $item→getName (); } return $item →fbind ($getParent) →fbind ($getParent) →bind ($getName) →extractOrElse («default»); } И теперь, если на пути к получению у нас встретится Nothing, то вернется default, иначе имя.

Чтож, моя душа немного спокойнее относительно монад в php. Ну или по крайней мере, относительно Maybe…

Хочу услышать мнения об этой и предыдущей статье. А также о «библитеке», что бы вы туда добавили, ведь это было только лишь мое мнения, а голов то у нас много.

P.S. Не особо кидайтесь камнями, все таки first one paper.

© Habrahabr.ru