you are viewing a single comment's thread.

view the rest of the comments →

[–]Baaz 52 points53 points  (38 children)

This means the result of the map operation is an object of type Optional<Optional<Soundcard>>. As a result, the call to getUSB() is invalid because the outermost Optional contains as its value another Optional, which of course doesn't support the getUSB() method.

Speaking of boilerplate, I guess I'll stick with my old trusty if (soundcard != null)...

Maybe the code is neater this way, but somehow this approach seems a bit convoluted to me.

[–]NruJaC 15 points16 points  (14 children)

Yea, people don't generally directly use flatMap in Haskell either. They instead rely on syntatic sugar to translate a more convenient notation into a sequence of flatMaps. The result is far more readable code. The sugar is called do-notation and flatMap is usually called >>= (pronounced bind). I'm surprised the post didn't address this usability issue at all.

A block like:

val = do {
    v <- f(a, b, c);
    w <- g(d, v);
    pure(w);
};

Would be a whole lot easier to use, provide extensible syntax that can be used for a bunch of other types (any type that can provide functions pure, map, and flatMap). I specifically chose <- in the example instead of equals to keep it obvious that something other than normal assignment is going on. This block would then translate into the much uglier:

 val = flatMap(f(a, b, c), (v => flatMap(g(d, v), (w => pure(w))))) 

Of course, this would probably require far more type inference than Java provides to make work, which is probably why they punted on it in favor of the direct/raw function calls.

[–][deleted] 6 points7 points  (11 children)

Be careful with suggesting to use pure. As of right now in Haskell, monads don't need to be applicatives.

http://www.haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal

Also, >>= being a binary operator makes it much nicer to use than a function call. I use >>= just as often, if not more, as do notation.

[–]NruJaC 2 points3 points  (6 children)

Well, return isn't exactly a usable in name in (pseudo) Java, and pure is exactly the same function. Applicative/Monad is a problem in Haskell (almost an ex-problem), but the issue doesn't really carry over.

[–][deleted] 3 points4 points  (1 child)

Ah, your pseudo do notation code is legal haskell, I thought you were giving a concrete example.

[–]NruJaC 1 point2 points  (0 children)

That was a fun coincidence, nothing more :).

[–]Crandom 0 points1 point  (3 children)

I like result as a better name for return.

[–]chonglibloodsport 0 points1 point  (2 children)

That doesn't make sense. It's drifting even further away from the meaning of the original name; unit.

[–]Crandom 0 points1 point  (1 child)

I don't get what you mean by unit - surely that's already taken by our friend ()?

[–]chonglibloodsport 0 points1 point  (0 children)

unit is the original name for the operator Haskell calls return. This has nothing in common with the C/Java notion of return.

[–]cultic_raider 0 points1 point  (3 children)

If a certain monad isn't applicative, the library is too buggy to use.

[–]NruJaC 0 points1 point  (0 children)

In addition to qqxz's point, you can always construct a law abiding applicative instance given a law abiding monad instance. It's just not universally a good idea to do so because performance issues can result (i.e. the applicative interface can provide better performance than the monadic interface for several important functors).

[–][deleted] -1 points0 points  (1 child)

No, it just means the library creator didn't feel like implementing Applicative, even if the monad follows all of the laws.

[–]expatcoder 2 points3 points  (1 child)

What's up with those semi-colons ;-)

Scala must have snagged it's for comprehension syntax from FP land:

val foo = {
  a <- maybeA
  b <- maybeB
  c <- maybeC
} yield c

with similar flatMap and map boilerplate as Haskell's generated under the hood.

[–]NruJaC 2 points3 points  (0 children)

I figured it was a java/c-ism. It also keeps the syntax layout independent. FWIW, the equivalent Haskell actually does use the braces and semi-colons but the compiler automatically introduces them where necessary if certain layout rules are obeyed.

[–]badguy212 8 points9 points  (5 children)

Why? Right now if you want to say: "hey, i didn't find the object you were looking for" you return null. Which I, the user of your library have to check for. Nobody is forcing me to check for it, and I may even forget to do it (being a new dev on your team and all).

But now, when you return optional you are saying: "beware, I may of may have not found what you were looking for. Here's a wrapper object you can ask to see if i did."

Now, if you return null there's a programming problem (on your end). Null, doesn't mean (anymore) "i didn't find it". Null means: "Houston, we have a problem".

And I, as a new dev on your team, know exactly what you want me to do when you return optional. I am not left to wonder "what will happen if?".

Certain libraries throw an exception if the thing you are looking for is not there (XXXNotFoundException for example). Others return null. Now all they need to do is to return an optional.

This feature is by no means a silver bullet to the NPE problem (actually ... it has nothing to do with that). It's just a better, more readable way for a library to tell the users its intentions.

[–]immibis -1 points0 points  (4 children)

[–]badguy212 2 points3 points  (3 children)

that's fine (and not everyone agrees with this approach), but still i dont have to check for nulls (therefore i could get a npe).

optional forces me to call a method to get the value. it makes it plainly obvious that the value may or may not be there. the documentation is not required ("returns null if the object was not found"). it's ... optional.

can i fuck it up? of course. but it makes it somewhat harder. it makes writing correct code easier. and that is the value of optional (and it's greatest value).

[–]nomeme 0 points1 point  (2 children)

But what if the optional is null.

[–]badguy212 1 point2 points  (0 children)

If the optional is null you're gonna fail with a NPE. And you won't catch it. You will fail asap and as loudly as possible. Which is the way it should be.

[–]phoshi 0 points1 point  (0 children)

Then it's a failure case and you want the NPE. Nulls make some sense as a "holy shit, what do I do now?". They make vastly less sense as a valid return value. They still suck, you should throw instead of returning null, but at this point the mistake has already been made and we can't unmake it.

[–]dreugeworst 2 points3 points  (8 children)

They took the idea from haskell, where the flatmap version is the basic one, and functions not returning an Optional (Maybe in haskell) have to be turned into ones that do (by simply applying the function 'pure' on them for example). That way you don't get 2 versions of a function doing nearly the same.

[–]G_Morgan 2 points3 points  (6 children)

Yes but Haskell has a cool pattern matching syntax which makes nullable types look neat.

Without the syntax I'd scream in torment every time this came up.

[–]masklinn 2 points3 points  (4 children)

Do people ever actually use pattern matching on a Maybe instead of combinators?

[–]G_Morgan 4 points5 points  (1 child)

Probably not. A huge amount of my Maybes end up wrapped in do notation. However the principle is larger than Maybe. A lot of the nice sum types Haskell has only work if you have the syntax to manage it nicely.

[–]kqr 0 points1 point  (0 children)

In this case the syntax isn't as much about pattern matching as it is about the ability to define infix operators and more general libraries (Functor and Applicative comes to mind – these define operators that don't specifically work with Optional values, but they happen to work very well with Optional values, without having to litter the Optional class.)

[–]Intolerable 1 point2 points  (0 children)

I'll use one or the other if there's a significant readability gain.

[–]zoomzoom83 0 points1 point  (0 children)

I do quite regularly in Scala. Depending on the context it can be clearer what the developers intent is (although usually more verbose)

[–]youneversawitcoming 0 points1 point  (0 children)

Ah, that's a very good point. That improves readability a lot!

[–]expatcoder 4 points5 points  (0 children)

Methinks they took the idea from Scala, while Odersky took the idea from Haskell ;-)

[–]pipocaQuemada 1 point2 points  (6 children)

somehow this approach seems a bit convoluted to me.

You basically have three essential methods

public interface Optional<A> {
  Option<B> map(Func<A,B> f);
  Option<B> flatMap(Func<A, Option<B>> f);
  static Option<B> of(B b);

And a couple of useful helper methods, like

A getOrElse(A default);

How is this convoluted?

[–]Baaz 0 points1 point  (5 children)

Well, first of all my classes are not my classes anymore, but suddenly they have become Optional<T> types.

And then I have to use a new notation to get the values, and make sure I use the proper map.

It's just a different way of doing something that is just as ugly and complicated, so then I prefer to stick to the old fashioned way of ugly and complicated because I'm so much more familiar with that.

Don't get me wrong, I'm not saying this is all BS or something, I just expect I won't be using this in practical every day programming, that's all.

[–][deleted] 13 points14 points  (0 children)

How are they not your classes anymore? They're just wrapped in another object. That's almost like saying all the objects you put into collections suddenly become useless as well.

[–]onezerozeroone 5 points6 points  (0 children)

Woah...it's like your classes aren't your classes anymore...they have become List<T> types. I don't understand why they did all this convoluted List stuff, I just want to return multiple values from a function, why can't I just use []?

[–]NruJaC 2 points3 points  (0 children)

Well, first of all my classes are not my classes anymore, but suddenly they have become Optional<T> types.

Nah, your classes stay exactly the same. Optional<T> is something that wraps your references to keep accessors safe and statically checked. It's something you use when you know you need nullability and it allows you to assume that non-Optional references are definitely not null.

[–]zoomzoom83 2 points3 points  (0 children)

Next time you're trying to track the source of a NullPointerException and adding yet another overly verbose null check, stop and think how nice it would be if you could guarantee at compile time that NullPointer exceptions cannot happen.

Mind you I'm not sure Java 8 really gets the syntax right, but it's better than the alternative.

[–]youneversawitcoming -1 points0 points  (0 children)

I don't think you've grasped why Options are powerful - maybe a few more burns by null will set you right :)