📜 ⬆️ ⬇️

And again about monads in PHP



After reading this material on a languid and cool Friday evening, I was left with some feeling of dissatisfaction and burning somewhere below. I sat with the zeal of a madman, updated the comments in the hope that there would be a person who would say why this was happening and I would understand that I was not alone. But ... alas, this did not happen. After that, I visited this creation and felt the same feeling and realized that something needed to be changed.

Debriefing

I will pour out all my indignation on a specific monad, namely on optional values.
But first, let's take a look at the Monad root class code:

public static function unit($value) { if ($value instanceof static) { return $value; } return new static($value); } 

')
And immediately we are horrified - you cannot shove a monad into a monad!

Quickly eliminate this injustice in all functions:

 //     public static function unit($value) { return new static($value); } 


As the code immediately became prettier (for why do we need if'y O_o).

But since we can now have monads in monads, we must take care of the functions for these situations. Actually:

 //   abstract public function fbind($function, array $args = array()); //     abstract public function flatten(); 


So now let's move on to the Maybe monad.

What a violation of NULL 's rights is the same value. And why the author did not fill it with additional specific functionality of the mind I will not apply:

 abstract class Maybe extends Monad { abstract public function extractOrElse($val, array $args = array()); } 


As it is easy to guess, this method returns us an internal or transmitted value.
But why $ args ? You ask. Yes, in general, why not pass a function that needs to be calculated only if we need this value. (Damn lack of call-by-name!)

So, now we will describe the classes Just and 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 bind($function, array $args = array()) { return $this; } public function fbind($function, array $args = array()) { return $this; } public function flatten() { throw new Exception("Nothing flatten call"); } } 


As the reader might have noticed, this is Singleton . But why do we really need a lot of “nothing”?

Example

Let us consider a similar example as in the previous article .
We need to get the parent's name of the parent .

Here is an example from that article:

 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(); } 


I consider that the monad of Maybe should already go to the input of a function. If it’s possible that the values ​​might not be present. And of course, the element may not have a parent, which kind of hints at the type of the value of getParent . And then it goes:

 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"); } 


And now, if on the way to receive, we meet Nothing , then return default , otherwise the name.

Well, my soul is a little calmer with respect to monads in php. Or at least relatively Maybe ...

I want to hear opinions about this and the previous article. And also about the "library" , what would you add there, because it was just my opinion, but we have a lot of heads.

PS Do not really throw stones, all the same first one paper.
UPD thank you for a more "clean" picture TheRaven :)

Source: https://habr.com/ru/post/232071/


All Articles