You might already have an idea what monads are, but putting it together can still be difficult. Often haskell is explained theoretically or haskell is explained in terms of haskell. Which doesn't really help if you don't know haskell.

This post will explain one function, which was taken from the FP101x course. This course is pretty elaborate for "just an introduction" so you will learn a lot. Like Erik Meijer, i think it's a good idea to learn by pointing to the similarities rather than the differences. In other words: it might be easier to learn something new in terms which you already understand.

Like a previous post this post will draw parallel's between haskell and PHP. It's a good idea to first read this post as well as a few introductions on monads like monads in pictures and monads.

Also i would like to stress that these more advanced language features are hard to express in another language. So the analogy is not perfect, to say the least. In fact i'm going to skip some parts of the implementation because they are not essential to explaining this function. In particular i'm leaving out the implementation of Monad where it can return Nothing (or null in other languages). For a decent monad implementation in PHP have a look at Taking Monads to OOP PHP.

This is the function:

atom :: IO a -> Concurrent a
atom a = Concurrent ( \f -> Atom (a >>= \b -> return (f b)) )

With types

data Concurrent a = Concurrent ((a -> Action) -> Action)

data Action 
    = Atom (IO Action)
    | Fork Action Action
    | Stop

The types

class IO {
  public function __construct($side_effect) {
    $this->side_effect = $side_effect;
  }
}

class Concurrent {
  public function __construct($a) {
    $this->a = $a;
  }
}

class Action {}
class Atom extends Action {
  public function __construct($value) {
    $this->value = $value;
  }
}

This is just boilerplate code in PHP, similar to the previous article on types.


With that out of the way, let's dissect our function piece by piece.

atom a = Concurrent ()

The function takes one argument are returns a value of type Concurrent. Since Concurrent holds a function we can run this function later after getting this value from atom.

(a -> Action) -> Action

Is a function which takes a function as it's first argument.

function atom($a) {
  return new Concurrent($a);
}

$a is not the function in Concurrent, it's the IO a value. It's the PHP object's responsibility to give back this function using a. BUT there is one big difference here, and that is that haskell is lazy and PHP is not. In PHP we can not know all the arguments we need for this function. Remember "Concurrent holds a function that takes a function"? Well this function it takes will only be available at a later time. So that's why we have to introduce a second function to our Concurrent PHP class which can be evaluated later.

class Concurrent {
  public function __construct($a) {
    $this->a = $a;
  }

  public function run($f) {
    return new Atom(/* ??? */);
  }
}

$f is that inner function which we need. And we already know that we are going to return an Atom when we run the function in Concurrent (the outer function).

That leaves

a >>= \b -> return (f b)

Which is the monadic part. a is the Monad, b is the result of that monad (pure). With a >>= we feed that into a lambda function which returns f applied to our pure result, which then gets wrapped up in a monad again. a is of the type IO monad, and because of that return will also be an IO monad. This is automatically infered by haskell when you use the ... >>= ... return ... pattern. So how can we do this in PHP? We can make a function for >>= which is called bind in haskell, so we name our php function bind.

function bind(IO $io, $f) {
  $side_effect = $io->side_effect;
  $b = $side_effect();
  return new IO($f( $b ));
}

This function takes an IO Monad and a pure function, then applies the inner function to the monad result. And then wraps it back into a new IO monad.

note: this is the part where we not check for null or Nothing even though we should to get a proper monad

Let's use this bind function when we start running the Concurrent type.

class Concurrent {
  public function __construct($a) {
    $this->a = $a;
  }

  public function run($f) {
    return new Atom(bind($this->a, $f));
  }
}

That's it! That 1 line of haskell is now implemented in PHP. Let's run this code to be sure it works and also to give an idea on how the atom() function is suppose to be called.

atom takes an io action wrapped in a IO monad. Let's make this io action

$a = new IO(function() {
  return 'getting a user string is a side effect';
});

Let's get a Concurrent

$concur = atom($a);

Now let's run this concurrent with our "pure" function and get the result out

$result = $concur->run(
  function($side_effect_result) {
    return strtoupper($side_effect_result);
  }
);

Which should return Atom (IO Action) (in haskell).

object(Atom)#5 (1) {
  ["value"]=>
  object(IO)#6 (1) {
    ["side_effect"]=>
    string(32) "GET USER STRING WITH SIDE EFFECT"
  }
}

Notice that our haskell type is recursive. The value Atom holds yet another Action. The PHP version is therefor NOT correct. I scarified a lot of correctness to make an attempt to explain haskell in PHP. Nevertheless i hope that it helps your understanding of more complicated types and monads.