Is Option a Maybe in PHP?

Haskell has a very nice construct called the Maybe Monad. In effect what it does is to specify that a function can return a value or not. The same thing in other languages is usually done by specifying that a function can return either a value of type X or null ( or an empty string or -1 or any other randomly specified value which has a semantic meaning specific only to that function. )

The problem with this approach is that this means that the return value must be checked by the calling method or you will potentially pass it on to other parts of your code which may not expect it. This leads to errors in your code which cannot easily be traced back to the source - the unhandled null value.

The beauty of Haskell's Maybe Monad is that it forces you to handle the null value - in this case called 'None'. It doesn't do this runtime, it actually forces this through compile time type checking. The Maybe Monad does this by defining the Just and None types. Any calling method must have clauses handling both these types or the compiler will complain.

Someone made an adaptation of this concept for Java 8, called Option (based on Scala's Option) . In this case again the compiler can check the types and see if you actually correctly handle the return value. It does this slightly differently through polymorphism and - since this is Java - it is a bit more verbose, but it does so nonetheless.

And finally another programmer converted the Option approach to PHP, which is so dynamically typed I personally think of it as 'vaguely' typed. There is no compile time checking as such, so you can't actually have the main advantage of both Option and Maybe. But if you use an IDE like PhpStorm, it will warn you when you access properties or methods on objects that it thinks don't have them. All you need to do is add docblock comments specifying the return type of each method and function as well as the properties of your classes.

But then there's another problem. The idea of the Option class is that it 'wraps' around any other return value. Instead of returning an instance of class Node, you return an instance of OptionSome wrapped around the Node. Calling the method getOrElse( $somethingElse ) will return the Node again. However, unlike in Java, you cannot specify a return value of Option < Node >. So even PHPStorm has no way that the getOrElse() will return a Node.

So we can't actually do type checking in PHP, even with advanced tools, no surprises there really. Is there any other reason to use the Option approach?

Well, I still dislike returning null. The Option class does force the calling function to handle this explicitly, since you must tell the Option what you want to do. You can still simply get() the raw return value, but that makes it explicit that you are doing something potentially dangerous. 

The one thing that bothers me here is that I like to create fluent interfaces. It makes methods more concise, allows you to easily limit the number of arguments per method and the method names can be much more self-explanatory. But by using Option everywhere where you might potentially return a null value, the fluent api becomes much less fluent:

$result = \arc\xml::parse( $xml )
->getElementById( 'foo' )
->appendChild( $newChild );
becomes something like this:
$result = \arc\xml::parse( $xml )->getOrElse( $emptyDocument )
->getElementById( 'foo' )->getOrElse( $nullNode )
->appendChild( $newChild );

The other alternative to returning null is to return a Null object - an object that implements the same interface as a normal return value but specifies in fact that there is no valid return value.

The advantage of a Null object is that it can still implement all the methods and properties of the normal return value but do nothing and return nothing. This means that you can skip most of the explicit isset() checks and make for more readable code that is also less prone to bugs.

The problem here is that null objects only really work if the return value would have been an object anyway. Even though PHP has a magic __toString() method, most built-in string functions do not accept objects as parameters. So returning a null object where you would normally expect a string will lead to fatal errors or lots of spurious string casting by paranoid developers. And then there are integers, booleans, arrays, etc. ArrayObect may seem to help for arrays, but unfortunately it doesn't allow you to override its behaviour when cast to array.

The Option class would indeed help, since the return value is always an Option object. So the calling code must handle this. The drawback is that the API becomes much more verbose and has a definit non-php feel...

Some may suggest to use exceptions instead of a null value, but I believe that any style of code that uses exceptions for anything less than exceptional circumstances will in the end deliver flawed software.

I have tried to combine the Option and Null object approach. By making the OptionNone also a null object and the OptionSome a Proxy for the real returnvalue I thought I could limit the verbosity of the Option option. Unfortunately this voids the one thing that makes it work in PHP, namely that you cannot ignore the fact that the return value is an Option. In addition the Proxy object only works for objects, not for scalar values, for the same reason the Null object only works in those cases.

So is Option a good idea for a PHP API?

blog comments powered by Disqus