> It is worth noting that in Haskell you don't have something like NullPointerException, the type system doesn't allow for that - so while you're point is relevant to Java it doesn't hold here. The closest you can get to something like null pointer is using 'maybe' type, but when you do so, you have to everywhere explicitly handle what happens if variable has no value (or the value of 'nothing' more precisely).
Which, as I mentioned, isn't any different from checking from nulls everywhere. Or if you are so inclined, write an Option class with the desired interface and use it everywhere where the value in nullable. My point is using Maybe is the same as manually checking for null.
case maybeValue of
Just value -> ...
Nothing -> ...
is the same as
if (val != null) {
....
} else {
...
}
If you see the compiler forcing you to always use Maybe for nullable types as an advantage, good for you. Personally, I don't see it as a big deal.
Maybe is a monad, which means Maybe computations can be chained together with `>>=` (or using `do` notation) without checking for `Nothing`. You can easily produce a large composition of potentially-failing computations while completely ignoring the possibility of failure.
The case analysis you give as an example is only required at the point when you want to extract the final result into a different monadic context, and even then you would typically use the `maybe` or `fromMaybe` functions to make it more concise.
Only a novice Haskell user would write:
case comp1 of
Nothing -> handleFailure
Just r1 ->
case comp2 r1 of
Nothing -> handleFailure
Just r2 ->
case comp3 r2 of
Nothing -> handleFailure
Just r2 -> handleResult r3
which is indeed, just as bad as explicit null checking in C or Java, with runaway indentation to boot. But anyone who understands Haskell's rich abstractions would instead write:
The fact that you can't forget the "null check" without the compiler telling you about it is a nice convenience afforded by the strong type system, but it's far from the only benefit.
> Maybe is a monad, which means Maybe computations can be chained together with `>>=` (or using `do` notation) without checking for `Nothing`. You can easily produce a large composition of potentially-failing computations while completely ignoring the possibility of failure.
Like
val = foo.bar.blah.boo rescue nil
Or
try {
val = foo.bar().blah().boo()
} catch(NullPointerException ex) {
}
Or
try:
val = foo.bar().blah().boo()
except AttributeError:
pass
Yes, I know Maybe is a monad, but that doesn't make a difference to me. A series of computation where a step depends on the results of the previous step and the previous step can return null is hardly an issue in any language.
> The case analysis you give as an example is only required at the point when you want to extract the final result into a different monadic context, and even then you would typically use the `maybe` or `fromMaybe` functions to make it more concise.
The case analysis I give is an example where either there is a value or null and I need the value. I don't really care how Haskell defines monadic context.
> isn't any different from checking from nulls everywhere
You would only check for nulls "everywhere" if all functions could potentially return null. Then, indeed, there is no difference. But (hopefully) not all functions will return null, so you'll probably have at most a single-digit percentage of functions that do.
The point is that by using Option, you are explicitly stating "This function can return null", making it impossible for the caller to ignore. If you write it somewhere into the docs, it is indeed easy to overlook.
This may not be as relevant when you are only working with your own code, but I find this (and static typing in general) most helpful when dealing with code from someone else, including the libraries one uses.
There is a fundamental difference between throwing an exception and returning null. From http://en.wikipedia.org/wiki/Exception_handling: "Exception handling is the process of responding to the occurrence, during computation, of exceptions – anomalous or exceptional situations requiring special processing – often changing the normal flow of program execution."
One should throw an exception when an anomalous situation appears, e.g. I can not connect to the database. Whereas returning null / returning an Option means that this case needs to be treated in the normal flow of execution, e.g. asking a Person-object for it's spouse. It is perfectly reasonable that a random person isn't married (so throwing an exception is wrong) but at the same time it should be impossible for the caller to ignore.
> It might be overlooked. But is it too much to assume that someone making a call will check the parameters and the return type?
> It is perfectly reasonable that a random person isn't married (so throwing an exception is wrong) but at the same time it should be impossible for the caller to ignore.
The whole discussion started from the claim that Haskell makes the NullPointerException/NoneType non existent. And I am just saying it isn't any different from how you enforce it in any other language - you either handle nulls or throw exceptions.
> (so throwing an exception is wrong)
I am sorry, but I don't play the "throwing an exception is wrong" game. I use it for actual exceptions, control flow, must-handle scenarios. I don't see how exceptions are defined has to do with how I use it if it makes my program logic clear or translates to my intent. The only reason I think before using exceptions for things which aren't exceptions is the stack which is saved, which most of the times so minuscule that it doesn't matter. Ruby has the best compromise as in it defines catch..throw; most of the languages don't so I resort to using exceptions.
Sorry, but it is not a game, it is a convention that afaik holds true for ALL languages that use exceptions. Using exceptions for control flow is widely accepted as a code smell.
> Sorry, but it is not a game, it is a convention that afaik holds true for ALL languages that use exceptions. Using exceptions for control flow is widely accepted as a code smell.
Unless using exceptions either hinders performance(they are expensive but I am yet to see a case where it matters), or makes the control flow incomprehensible, it doesn't matter what is widely accepted. I need a reason for "don't use exceptions because...", and "exceptions for exceptional conditions" or "just because it's widely accepted" doesn't cut it.
I am pretty sure you have very strong opinions about goto as well, which I use a lot when using C. It's simply the cleanest way to directly jump to the cleaner instead of convoluting the flow with non-needed flags. Since you are placing much weight on what others deem acceptable, you can look at linux kernel code and Steven's code in Unix Network Programming.
Also, exceptions are control flow in every sense of the word, though non-local http://en.wikipedia.org/wiki/Control_flow#Exceptions. I don't know where the notion of exceptions not being control flow came from. Among other examples of exceptions for control flow, Python and Ruby raise StopIteration in their iteration protocol. And in Python, exceptions aren't that much costly.
You don't have to handle it everywhere explicitly. This is where Functor, Applicative, Alternative, Monoid and Monad are for. They will do the plumbing for you. Eventually you will unpack the values, but this is only necessary if you change environment.
Say we have a failing computation:
failComp = Nothing
and a couple of succeeding computations:
sucComp = Just 1
sucComp2 = Just 2
We can use the typeclasses to avoid explicit unpacking:
-- Monad result: Just 3
add = do
x <- sucComp2
y <- sucComp
return $ x + y
Applicative result: Just 3
-- the <#> should be the applicative operator.
addapp = (+) <$> sucComp <#> sucComp2
In Java, the compiler won't tell you that a variable may be null. In Haskell (at least when you compile with -Wall, strange this particular warning isn't the default) you'll get an error if you've failed to handle all the variations of data that you've provided.
There's a good presentation my Yaron Minsky about OCaml in the real world where he cites this as a major advantage (in OCaml, failing to match all patterns is an error by default).
> In Java, the compiler won't tell you that a variable may be null. In Haskell (at least when you compile with -Wall, strange this particular warning isn't the default) you'll get an error if you've failed to handle all the variations of data that you've provided.
Connection con = DriverManager.getConnection(
"jdbc:myDriver:myDatabase",
username,
password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}
It's not that bad. In the above example, you either get an SQLException or a ResultSet. ResultSet is your option type here. It wont' be null - it might or might not contain values.
There might be a lot of good things about Haskell(I am not that familiar to make a call), but seriously, Maybe doesn't look that great.
That's right, I've changed my comment to be more relevant reply, sorry for confusion.
> If you see the compiler forcing you to always use Maybe as an advantage, good for you. Personally, I don't see it as a big deal.
One of reasons I like Maybe is I can accidentally put null somewhere I know I should not; Haskell's type system will prevent me from that. I wouldn't use Maybe unless I really need to - whereas in languages with less strict type systems, almost everything is a 'Maybe'. Besides, it seems it's easier to build some layer on abstraction on it, which you can reuse and save some time (for example, you have monad instance for it - however I don't think I've ever used it).
> whereas in languages with less strict type systems, almost everything is a 'Maybe'
I don't know Haskell(I do know F#), so help me with this: How is everything not a Maybe in Haskell? In the languages I know, when you return an object from a method, (it can throw an Exception or return null) or return a valid val. When it throws an Exception, there is no Maybe, or else it's a Maybe. How is it any different in Haskell.
Which, as I mentioned, isn't any different from checking from nulls everywhere. Or if you are so inclined, write an Option class with the desired interface and use it everywhere where the value in nullable. My point is using Maybe is the same as manually checking for null.
is the same as If you see the compiler forcing you to always use Maybe for nullable types as an advantage, good for you. Personally, I don't see it as a big deal.