RFC Proposal: Maybe monad and execution time polymorphic methods

php.internals

Facundo Martinez Correa

10 years ago
Hello, I am reaching you in order to obtain feedback on this RFC proposal. I have been playing with PHP 7 for a project in the company that I am currently working for. One of the many drawbacks that I saw in PHP was the there were no types. With type hinting I had confindence again in PHP. With return type hinting a wasn't able to resist to try it. So here I am, making a whole new platform entirely in PHP 7, using type hints in everything. Arguments and returns alike. Given my tendency to language purity imagine my surprise when my code inadvertently tryied to sneak a NULL into a method expecting an object... And failing thanks to strictness in object and null treatments in the new PHP 7. "SWEET!" I yelled. Now imagine my dumbfoundedness when a Repository of mine tryed to return a NULL, and because it ought to return have returned a User, it threw a TypeError. At first I said "sweet." But then I realized the problem. There are many times where we need uncertainty. Code is a reflection of reality, and as such, it must reflect uncertainty. NULL is a good enough way to express nonexistence, albeit a bad one. We have been using it in code for years, but it is also used to say many things. Things that cause uncertainty in the code. I receive a null, does it mean it doesn't exists? That it will exist? Should I allow it? Is it a good value? I for sure don't know. And in my experience, I have used it for many of those cases. And in many situations that I'm not proud of, all of them at the same time. So I want to "return a NULL". I want to express uncertainty, a nonexistence. But I want to express that I will return something. And I want to have this NULL checking at interpretation time. I want everything, and none of the drawbacks. As we say here in Argentina, I want the bread and the cake. What can we do to have it? After a bit of thought the Maybe monad came to me. My experience in Haskell reminded me of this. To have a structure than represents uncertainty, is the best way to take away the responsibility from NULL. To express it in no other way. But my experience in Java has taught me that Optional of something is not a good way to tackle the problem. I still have to check if my Optional isEmpty. I still have to check for null. That is because Java has that type erasure after compilation time that doesn't allow me to play nice with polymorphism. But maybe PHP can. Maybe PHP will give me the polymorphism at execution time to discern an empty Maybe from the one that is not. So I don't have to check for null ever again in my life. So my code can be free of nulls. So I can express uncertainty and nonexistance, while I return a User from my repository. I hope someone would like this idea. I'm also open to suggestions. Kind regards Facundo Martínez

Levi Morrison

10 years ago
> My experience in Haskell reminded me of this. To have a structure than > represents uncertainty, is the best way to take away the responsibility > from NULL. To express it in no other way. But my experience in Java has > taught me that Optional of something is not a good way to tackle the > problem. I still have to check if my Optional isEmpty. I still have to > check for null. That is because Java has that type erasure after > compilation time that doesn't allow me to play nice with polymorphism. But > maybe PHP can. Maybe PHP will give me the polymorphism at execution time to > discern an empty Maybe from the one that is not. So I don't have to check > for null ever again in my life. So my code can be free of nulls. So I can > express uncertainty and nonexistance, while I return a User from my > repository.
I don't fully understand what you are asking for here. You want a Maybe<T> to automatically become a T if it's not null or something?

Facundo Martinez Correa

10 years ago
Hi Levi, Thanks for replying. I don't think that automagically taking the T out of the Maybe<T> would be a good idea, mainly because sometimes you don't need the T, you just want to know the result of it. What I would want in that PHP automagically executes the method myMethod(Maybe<T> $object) if something was returned or the method myMethod(EmptyMaybe $object) if the returned was an EmptyMaybe. The use of Maybe is, in fact, a motivation for real operators oveloading. The kind that will be decided at execution time, with the type at hand. This motivation came when what I explained happened. And that's the reason why I'm also sugesting a Maybe. Regards, On Mon, Mar 21, 2016 at 5:27 PM, Levi Morrison <levim@php.net> wrote:

Stephen Coakley

10 years ago
On 03/21/2016 03:04 PM, Facundo Martinez Correa wrote:
> So I want to "return a NULL". I want to express uncertainty, a > nonexistence. But I want to express that I will return something. And I > want to have this NULL checking at interpretation time. I want everything, > and none of the drawbacks. As we say here in Argentina, I want the bread > and the cake. What can we do to have it? After a bit of thought the Maybe > monad came to me. > > My experience in Haskell reminded me of this. To have a structure than > represents uncertainty, is the best way to take away the responsibility > from NULL. To express it in no other way. But my experience in Java has > taught me that Optional of something is not a good way to tackle the > problem. I still have to check if my Optional isEmpty. I still have to > check for null. That is because Java has that type erasure after > compilation time that doesn't allow me to play nice with polymorphism. But > maybe PHP can. Maybe PHP will give me the polymorphism at execution time to > discern an empty Maybe from the one that is not. So I don't have to check > for null ever again in my life. So my code can be free of nulls. So I can > express uncertainty and nonexistance, while I return a User from my > repository. > > I hope someone would like this idea. I'm also open to suggestions. > Kind regards > Facundo Martínez
First of all, let me confess that I absolutely love monad (or composite) types, and I greatly prefer a Maybe over something that may or may not be null. There's a slight problem however, and something that will not be easy to get around: a lack of generics. This is an issue with wrapper types in general in PHP, where you want to wrap an object in some other type. You lose type safety on the inner object, and so I tend to look for a more type-safe solution in PHP. There is already a generics RFC out there (https://wiki.php.net/rfc/generics) but implementing it won't be trivial. Implementing (or defining) a good Maybe type also depends on better composite/enum types (https://wiki.php.net/rfc/enum is drafted, but doesn't support what we need). The classical definition would be (hypothetical PHP syntax): enum Maybe<T> { Some { public function __construct(T $value); public function isSome(): bool { return true; } public function into(): T { return $this->value; } }, None { public function isSome(): bool { return false; } public function into(): T { throw new Error(); } } } Such a definition depends on two different things we don't have right now in PHP. Maybe there's a simpler way, but hopefully you have a better alternative?
-- Stephen

Levi Morrison

10 years ago
On Mon, Mar 21, 2016 at 5:03 PM, Stephen Coakley <me@stephencoakley.com> wrote:
> On 03/21/2016 03:04 PM, Facundo Martinez Correa wrote: >> >> So I want to "return a NULL". I want to express uncertainty, a >> nonexistence. But I want to express that I will return something. And I >> want to have this NULL checking at interpretation time. I want everything, >> and none of the drawbacks. As we say here in Argentina, I want the bread >> and the cake. What can we do to have it? After a bit of thought the Maybe >> monad came to me. >> >> My experience in Haskell reminded me of this. To have a structure than >> represents uncertainty, is the best way to take away the responsibility >> from NULL. To express it in no other way. But my experience in Java has >> taught me that Optional of something is not a good way to tackle the >> problem. I still have to check if my Optional isEmpty. I still have to >> check for null. That is because Java has that type erasure after >> compilation time that doesn't allow me to play nice with polymorphism. But >> maybe PHP can. Maybe PHP will give me the polymorphism at execution time >> to >> discern an empty Maybe from the one that is not. So I don't have to check >> for null ever again in my life. So my code can be free of nulls. So I can >> express uncertainty and nonexistance, while I return a User from my >> repository. >> >> I hope someone would like this idea. I'm also open to suggestions. >> Kind regards >> Facundo Martínez > > > First of all, let me confess that I absolutely love monad (or composite) > types, and I greatly prefer a Maybe over something that may or may not be > null. There's a slight problem however, and something that will not be easy > to get around: a lack of generics. > > This is an issue with wrapper types in general in PHP, where you want to > wrap an object in some other type. You lose type safety on the inner object, > and so I tend to look for a more type-safe solution in PHP. There is already > a generics RFC out there (https://wiki.php.net/rfc/generics) but > implementing it won't be trivial. > > Implementing (or defining) a good Maybe type also depends on better > composite/enum types (https://wiki.php.net/rfc/enum is drafted, but doesn't > support what we need). The classical definition would be (hypothetical PHP > syntax): > > enum Maybe<T> > { > Some { > public function __construct(T $value); > public function isSome(): bool { > return true; > } > public function into(): T { > return $this->value; > } > }, > None { > public function isSome(): bool { > return false; > } > public function into(): T { > throw new Error(); > } > } > } > > Such a definition depends on two different things we don't have right now in > PHP. Maybe there's a simpler way, but hopefully you have a better > alternative?
This requires you to query state with `isSome()`. This is hardly any different from a null case, just more code. We can already accurately distinguish between `null` and another value. If we want an option for safer PHP code I think we need a safer construct that requires exhaustive matching (such as Rust's `match`). I'm not sure how to pull that off.

Stephen Coakley

10 years ago
On 03/21/2016 11:09 PM, Levi Morrison wrote:
> This requires you to query state with `isSome()`. This is hardly any > different from a null case, just more code. We can already accurately > distinguish between `null` and another value. > > If we want an option for safer PHP code I think we need a safer > construct that requires exhaustive matching (such as Rust's `match`). > I'm not sure how to pull that off. >
Certainly. Ideally some sort of `match`, which is listed as future scope in the enum RFC. The above definition of a Maybe is just some sort of basis for a type. You probably wouldn't use `isSome()` much and would use pattern matching on the enum instead. As much as I do love Rust's type system though, PHP isn't Rust. :)
-- Stephen

Larry Garfield

10 years ago
On 3/22/16 1:34 PM, Stephen Coakley wrote:
> On 03/21/2016 11:09 PM, Levi Morrison wrote: >> This requires you to query state with `isSome()`. This is hardly any >> different from a null case, just more code. We can already accurately >> distinguish between `null` and another value. >> >> If we want an option for safer PHP code I think we need a safer >> construct that requires exhaustive matching (such as Rust's `match`). >> I'm not sure how to pull that off. >> > > Certainly. Ideally some sort of `match`, which is listed as future > scope in the enum RFC. The above definition of a Maybe is just some > sort of basis for a type. You probably wouldn't use `isSome()` much > and would use pattern matching on the enum instead. > > As much as I do love Rust's type system though, PHP isn't Rust. :)
Correct. So we should just sneak up behind Rust, pick it's pocket, and steal the bits that we can make work for us. That's what PHP's always done to every other language we've encountered. :-)
-- --Larry Garfield

Côme Chilliet

10 years ago
Le lundi 21 mars 2016, 17:04:30 Facundo Martinez Correa a écrit :
> But then I realized the problem. There > are many times where we need uncertainty. Code is a reflection of reality, > and as such, it must reflect uncertainty. NULL is a good enough way to > express nonexistence, albeit a bad one. We have been using it in code for > years, but it is also used to say many things. Things that cause > uncertainty in the code. I receive a null, does it mean it doesn't exists? > That it will exist? Should I allow it? Is it a good value? I for sure don't > know. And in my experience, I have used it for many of those cases. And in > many situations that I'm not proud of, all of them at the same time.
If your function is supposed to return a User and can’t, maybe it should throw an Exception. Just wanted to point out it can be a good alternative to returning NULL for error handling. Côme

Larry Garfield

10 years ago
On 3/21/16 10:23 PM, Côme Chilliet wrote:
> Le lundi 21 mars 2016, 17:04:30 Facundo Martinez Correa a écrit : >> But then I realized the problem. There >> are many times where we need uncertainty. Code is a reflection of reality, >> and as such, it must reflect uncertainty. NULL is a good enough way to >> express nonexistence, albeit a bad one. We have been using it in code for >> years, but it is also used to say many things. Things that cause >> uncertainty in the code. I receive a null, does it mean it doesn't exists? >> That it will exist? Should I allow it? Is it a good value? I for sure don't >> know. And in my experience, I have used it for many of those cases. And in >> many situations that I'm not proud of, all of them at the same time. > If your function is supposed to return a User and can’t, maybe it should throw an Exception. > Just wanted to point out it can be a good alternative to returning NULL for error handling. > > Côme
In the PHP 7 and typing presentations I've been giving[1], I've advocated either: 1) Throw an exception if the rest of the code is going to break anyway. 2) Define an empty object with matching interface if you want an equivalent of 0/empty string. While a monad sounds cool, it doesn't seem like the necessary language infrastructure is there for it at the moment. [1] My most recent talk that has a section on returns: https://youtu.be/rWDY4-LJils?t=2m38s (that's where the return type segment starts) Slides: https://www.palantir.net/presentations/midcamp2016-php7/
-- --Larry Garfield

Andreas Heigl

10 years ago
> Am 22.03.2016 um 15:56 schrieb Larry Garfield <larry@garfieldtech.com>: > >> On 3/21/16 10:23 PM, Côme Chilliet wrote: >> Le lundi 21 mars 2016, 17:04:30 Facundo Martinez Correa a écrit : >>> But then I realized the problem. There >>> are many times where we need uncertainty. Code is a reflection of reality, >>> and as such, it must reflect uncertainty. NULL is a good enough way to >>> express nonexistence, albeit a bad one. We have been using it in code for >>> years, but it is also used to say many things. Things that cause >>> uncertainty in the code. I receive a null, does it mean it doesn't exists? >>> That it will exist? Should I allow it? Is it a good value? I for sure don't >>> know. And in my experience, I have used it for many of those cases. And in >>> many situations that I'm not proud of, all of them at the same time. >> If your function is supposed to return a User and can’t, maybe it should throw an Exception. >> Just wanted to point out it can be a good alternative to returning NULL for error handling. >> >> Côme > > In the PHP 7 and typing presentations I've been giving[1], I've advocated either: > > 1) Throw an exception if the rest of the code is going to break anyway. > 2) Define an empty object with matching interface if you want an equivalent of 0/empty string.
When code expects a user-object but can't get one it'll break. Throwing an exception makes it easy to react to that. But when the code has to check whether that's a mocked user object or a real user object it's about as good as returning null or eliminating the return type altogether as you have to build the code around that returned value you can't really use. So in my opinion either return a proper object/type or throw an exception. My 2 cent Cheers Andreas

Larry Garfield

10 years ago
On 3/22/16 11:25 AM, Andreas Heigl wrote:
> >> Am 22.03.2016 um 15:56 schrieb Larry Garfield <larry@garfieldtech.com>: >> >>> On 3/21/16 10:23 PM, Côme Chilliet wrote: >>> Le lundi 21 mars 2016, 17:04:30 Facundo Martinez Correa a écrit : >>>> But then I realized the problem. There >>>> are many times where we need uncertainty. Code is a reflection of reality, >>>> and as such, it must reflect uncertainty. NULL is a good enough way to >>>> express nonexistence, albeit a bad one. We have been using it in code for >>>> years, but it is also used to say many things. Things that cause >>>> uncertainty in the code. I receive a null, does it mean it doesn't exists? >>>> That it will exist? Should I allow it? Is it a good value? I for sure don't >>>> know. And in my experience, I have used it for many of those cases. And in >>>> many situations that I'm not proud of, all of them at the same time. >>> If your function is supposed to return a User and can’t, maybe it should throw an Exception. >>> Just wanted to point out it can be a good alternative to returning NULL for error handling. >>> >>> Côme >> In the PHP 7 and typing presentations I've been giving[1], I've advocated either: >> >> 1) Throw an exception if the rest of the code is going to break anyway. >> 2) Define an empty object with matching interface if you want an equivalent of 0/empty string. > When code expects a user-object but can't get one it'll break. Throwing an exception makes it easy to react to that. But when the code has to check whether that's a mocked user object or a real user object it's about as good as returning null or eliminating the return type altogether as you have to build the code around that returned value you can't really use. > > So in my opinion either return a proper object/type or throw an exception.
An "empty" object as I describe it isn't really a proper empty object unless it does satisfy the type and interface, such that you don't need special handling for it. The example I use in the slides is an Address object, with AddressInterface, whose "empty" implementation just has empty strings for all the various parts of it. That will work in some use cases but not others. It's up to the developer to determine if a reasonable empty object would work in their situation or not. If it doesn't, I agree, an exception is the answer. In any event, it sounds like a native monad won't work well in PHP so we're now drifting off topic. :-)
-- --Larry Garfield

Rowan Collins

10 years ago
Andreas Heigl wrote on 22/03/2016 16:25:
>> In the PHP 7 and typing presentations I've been giving[1], I've advocated either: >> > >> >1) Throw an exception if the rest of the code is going to break anyway. >> >2) Define an empty object with matching interface if you want an equivalent of 0/empty string. > When code expects a user-object but can't get one it'll break. Throwing an exception makes it easy to react to that. But when the code has to check whether that's a mocked user object or a real user object it's about as good as returning null or eliminating the return type altogether as you have to build the code around that returned value you can't really use.
It depends on the context; sometimes, you are right, you would end up checking for validity early anyway, and an exception would be easier. However, the Null Object Pattern can simplify logic significantly where there is an obvious "null behaviour" that it can implement. Wikipedia gives the example of a recursive tree operation having to check for missing child nodes (https://en.wikipedia.org/wiki/Null_Object_pattern#Example); initialising the child nodes with Null Objects eliminates the check. The key point is that in some cases, the calling code *won't* break, because the Null Object can still adhere to the interface, just by returning appropriate empty values. In the tree node case, a NullTreeNode would always return further NullTreeNodes as children, a sub-tree size of 0, and either no-op or error if you attempted modification. Regards,
-- Rowan Collins [IMSoP]

Jakub Kubíček

10 years ago
Hi Facundo Martínez, I think the best solution to yourself described problem above would be the Null Object Design Pattern (https://en.wikipedia.org/wiki/Null_Object_pattern). In my opinion, NULL belongs to PHP and this language never aimed to become Java or similar. Jakub On 22 March 2016 at 01:34, Facundo Martinez Correa <fnmartinez88@gmail.com> wrote:
> Hello, > > I am reaching you in order to obtain feedback on this RFC proposal. > > I have been playing with PHP 7 for a project in the company that I am > currently working for. One of the many drawbacks that I saw in PHP was the > there were no types. With type hinting I had confindence again in PHP. With > return type hinting a wasn't able to resist to try it. So here I am, making > a whole new platform entirely in PHP 7, using type hints in everything. > Arguments and returns alike. Given my tendency to language purity imagine > my surprise when my code inadvertently tryied to sneak a NULL into a method > expecting an object... And failing thanks to strictness in object and null > treatments in the new PHP 7. "SWEET!" I yelled. > > Now imagine my dumbfoundedness when a Repository of mine tryed to return a > NULL, and because it ought to return have returned a User, it threw a > TypeError. At first I said "sweet." But then I realized the problem. There > are many times where we need uncertainty. Code is a reflection of reality, > and as such, it must reflect uncertainty. NULL is a good enough way to > express nonexistence, albeit a bad one. We have been using it in code for > years, but it is also used to say many things. Things that cause > uncertainty in the code. I receive a null, does it mean it doesn't exists? > That it will exist? Should I allow it? Is it a good value? I for sure don't > know. And in my experience, I have used it for many of those cases. And in > many situations that I'm not proud of, all of them at the same time. > > So I want to "return a NULL". I want to express uncertainty, a > nonexistence. But I want to express that I will return something. And I > want to have this NULL checking at interpretation time. I want everything, > and none of the drawbacks. As we say here in Argentina, I want the bread > and the cake. What can we do to have it? After a bit of thought the Maybe > monad came to me. > > My experience in Haskell reminded me of this. To have a structure than > represents uncertainty, is the best way to take away the responsibility > from NULL. To express it in no other way. But my experience in Java has > taught me that Optional of something is not a good way to tackle the > problem. I still have to check if my Optional isEmpty. I still have to > check for null. That is because Java has that type erasure after > compilation time that doesn't allow me to play nice with polymorphism. But > maybe PHP can. Maybe PHP will give me the polymorphism at execution time to > discern an empty Maybe from the one that is not. So I don't have to check > for null ever again in my life. So my code can be free of nulls. So I can > express uncertainty and nonexistance, while I return a User from my > repository. > > I hope someone would like this idea. I'm also open to suggestions. > Kind regards > Facundo Martínez
-- Cheers, Kubis

Facundo Martinez Correa

10 years ago
Hi Jakub, Regarding the Null Object, I believe it to be a second hand solution. Having a Null Object that returns 0, null, "" or false for every method call, or a nil that acts as in Objective C that doesn't tell you when is being poked the wrong way is the first step into madness. It will never tell you about runtime errors, and you will find those errors once some data that is being outputed from the systems gets into your DB/Logs/Screen/whatever and just by chance you see that and say "Hey! that wasn't supposed to be there!". Then all hell breaks loose, and you start chasing code that never happens because your data will never be wrong and will always gracefully degrade. I believe that pattern is a bad pattern and humanity should feel bad about it. In fact, I believe it will be a feature that will drive people away from PHP and not towards it. Besides, the whole point of this idea I brought was to avoid checks that the interpreter could catch. If I return a Null Object, I'm forced to always ask for it. Now, regarding the "NULL belongs to PHP and this language never aimed to become Java" part. That is interesting because a) I never wanted to be Java, and in fact, I want it to be as more different to it as possible because I know what is bad and good and Java. b) As far as I remember, from the beginning of PHP, NULL was as in C a rename for 0. You could use 0, FALSE and NULL indistinctly. I'm not saying to eliminate null. Null does serve some other purposes, specially when you are managing memory, or bits, or whatever. I'm talking about making it represent less things and make the language more expressive. I hope transmitted my idea right. Thanks for the reply, Facundo On Mon, Mar 28, 2016 at 3:13 AM, Kubo2 <kelerest123@gmail.com> wrote:

Stas Malyshev

10 years ago
Hi!
> maybe PHP can. Maybe PHP will give me the polymorphism at execution time to > discern an empty Maybe from the one that is not. So I don't have to check
In order for that to work, we should have a facility to: a) define union types b) ensure somehow that code dealing with the union type covers all variants c) if we really want to be able to handle such types smoothly, we need something like monads to be able to convert f(SomeClass $x) to f(Maybe $x). This does not sound trivial at all. (c) is not strictly required for the rest to work, but will also require some kind of generics, since Maybe should have information about contained type (maybe what? maybe file, maybe database connection, maybe pastrami sandwich?). If you give up on (c), you still need some facility like case-of for (b). And, of course, you will still need to write code to cover the "nothing" case of the Maybe. So it might be easier getting a code analyzer that would just identify missing null checks?
-- Stas Malyshev smalyshev@gmail.com

Facundo Martinez Correa

10 years ago
Hi Stanislav, Yes, as you are saying, it is no easy task. I believe it would make a nice feature, though. I don't know it is really necessary to use union types, I believe that with a class hierarchy would suffice. But maybe there is something that I'm not looking at right, and the unions are needed. It would certainly need some kind of generics or templates, though I don't know how powerfull they ought to be. In fact, having generics was something that also came into my mind when dealing with this project, since I would use the Builder Patter for my Entities, had a couple of Entities that extended one from the other and I couldn't play it nice with the Type Hinting. Maybe is as Jakub told me, and PHP is not aiming to be Java nor anything similar in anyway. But I believe that if it is moving into type checking, it means that the community is seeing it as something needed. So it would be interesting maybe to have something similar to TypeScript, something like Gradual Typing. But to have that, everything from both worlds must work. Thanks for the reply. Kind regards, Facundo On Mon, Mar 28, 2016 at 9:13 PM, Stanislav Malyshev <smalyshev@gmail.com> wrote: