I get the point, but I still feel like the problem surface is not really reduced that much by static typing it away, and the bulk of the problem is simply moved elsewhere rather than solved outright since the root of these issues is incorrect specification anyhow.
As in, I take issue with the word "dramatic", not the word reduce.
Additionally - it might just be my python bias talking - but I tend to find that most useful "real-life" libraries need to have an opinion about what happens when a variable is None, because it is almost always meaningful. I guess if I were writing haskell my code would end up looking like a bad carly rae jephsen song :)
The key with Maybe/Option types is pattern matching ensures you don't forget to handle the case where it is none (it can be meaningful!). If you have a value of `Maybe a` you are required to pattern match on it.
case myMaybe of
Nothing -> foundNothing
Just v -> ...
In Python and Ruby any value can be None or nil at anytime.
In stark contrast languages like Rust and Haskell (that have no concept of None/nil/null) this is very useful as you know you always have a value in your variable and not a null.
This means when you do introduce an Option/Maybe type you are saying the null case should be treated with special semantics, exactly what you usually want in a dynamic language.
> I get the point, but I still feel like the problem surface is not really reduced that much by static typing it away
Ok, that's a valid point, which means that we need to precisely characterize what is meant by "problem surface". Let's continue with our IpAddress example. Imagine you are the author of the IpAddress package. Imagine we have an IpAddress type accompanied by an API of say 15 functions that take an IpAddress and do something with it. How many of those functions need to have tests that cover the case of passing None? I believe that number represents our problem surface.
If you agree that this is the problem surface, then I can confidently say the reduction is dramatic. (If you don' agree, then I refer you to the previous point about Maybe making it very clear what needs None tests.) The Python IpAddress package has to have None tests on every single function that takes an IpAddress. The Haskell package has to have None tests on zero. It only needs the equivalent of a None test on the functions that return a Maybe IpAddress. This is going to be a very small number. This brings us to your other point.
> Additionally - it might just be my python bias talking - but I tend to find that most useful "real-life" libraries need to have an opinion about what happens when a variable is None, because it is almost always meaningful. I guess if I were writing haskell my code would end up looking like a bad carly rae jephsen song :)
I'm pretty sure this is indeed largely caused by your Python bias. In my experience, Haskell code does not end up littered with Maybes. I think you don't appreciate the full weight of the statement that when you have an IpAddress, you never need to worry about the None case. Never ever. Period. There are cases where you need to deal with a Maybe IpAddress. Those cases don't end up polluting all your code, contrary to what you seem to think. In fact, it's the other way around. The pure cases tend to be the ones that are the most common, making the Maybe cases clearly visible.
Even in the situation where they do start to pollute some things, Haskell gives you dramatically better tools for dealing with the problem. Yes, you'll still have to have a test for it, but the code you're testing will have been much more concise and less prone to error. Furthermore, Haskell can make it so you don't even have to think about testing that case. You write an Arbitrary instance for IpAddress and the Arbitrary instance for "Maybe IpAddress" comes for free! This means that your QuickCheck test for the Maybe IpAddress function will automatically be sure to try it with Nothing.
From what you've said it is clear to me that your python bias is somehow making it hard for you to see/believe the benefits that we're talking about. I'm concerned that this gap seems to be so big and we seem to be having such a hard time crossing it. Give Haskell a try and see for yourself. But don't go off into a cave and play with it alone. There are abstractions and best practices for dealing with some of these things that took awhile to emerge. So you should be sure to talk to people who have experience using them while you're learning.
> I'm pretty sure this is indeed largely caused by your Python bias. In my experience, Haskell code does not end up littered with Maybes.
I think the Maybes and other optional/sum types tend to be limited to the boundaries, where you do IO or handle errors, while your internal functions can deal with the concrete type. A code base littered with Maybes is certainly a code smell.
> A code base littered with Maybes is certainly a code smell.
It's not because Maybe (and usually more practical Either) have functor instances. What this means is you can use fmap to apply pure functions to Maybes, Eithers, or any other type that has a Functor instance.
As in, I take issue with the word "dramatic", not the word reduce.
Additionally - it might just be my python bias talking - but I tend to find that most useful "real-life" libraries need to have an opinion about what happens when a variable is None, because it is almost always meaningful. I guess if I were writing haskell my code would end up looking like a bad carly rae jephsen song :)