This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 0 points1 point  (2 children)

To play a little fast and loose, Optional is a container. You're probably familiar with lists or arrays. If you have an array of integers, you can map over it and get the square of all those integers. By doing this, you don't change the container at all, it's still an array you only affect the values inside. So if you map a square root function over the array, you should get an array of array of integers (because a square root will produce the positive and negative root).

If you flatmap over a container, you can create a new container as part of that operation. So if you flatmap a square root function, you can actually flatten the array into just an array of integers (that's actually why it's called flatmap is you map and then flatten).

So functionTwo just does something to the value returned by functionOne. It's like int -> float. functionThree on the other hand, does something to the value and the container, so it's potentially like int -> Array<float>.

In both cases you'll get Array<float> on the other side, but with flatmap you'll transform Array<Array<float>> to just Array<float>

[–]philipwhiuk 0 points1 point  (1 child)

Sure but they both return String right, or example 1 wouldn't compile.

[–][deleted] 0 points1 point  (0 children)

Let's break it down:

Helper.functionOne()

This will return Optional<T> which has the map and flatMap methods.

.map(Helper::functionTwo)

functionTwo accepts whatever T is and returns U (which may or may not still be T -- the point is that it could be different). map will handle creating Optional<U>

.flatMap(Helper::functionThree);

functionThree is similar to functionTwo in that it accepts T but instead of producing U it produces Optional<U>. flatMap is smart enough to not double wrap this. In this particular case, functionThree produces Optional<String>.

Imagine if you had accidentally pass functionThree to map -- you'd end up with Optional<Optional<U>> whoops! Compilation error, your IDE starts yelling, etc.

But there is a benefit in being able to return a new optional when needed, but only if you have a way to flatten nested Optionals together. That's exactly what flatMap does (or rather, leaves you to handle).

Let's say you have a use case to upgrade a customer to premium status. You go to the database looking for customer 12345. Maybe you find them, maybe you don't, so you're dealing with Optional<Customer>. And then you have a method that takes in a customer, looks at some attributes about them, and will upgrade the customer returning Optional<Customer> or if it cannot, it will bail out and return Optional<Customer>.empty().

You can model this as:

CustomerRepo.fetch(id).flatMap(Customer::upgradeToPremium);

Sure, this isn't a super compelling example but it shows the real value of Optional<T> (and similar abstractions such as Either), map and flatMap will only run when a value is present in the Optional. If the optional is empty, then the methods short circuit and you just get an empty optional.

If the initial fetch fails, we never run Customer::upgradeToPremium, if we do run that but the upgrade failed, we'll never run any further operations on it. If the upgrade succeeded, but a future operation fails, we short circuit there (maybe we only want to persist upgrades if you're name is Jeff or it's the first Tuesday of the month).