Hi all, I have the following code, and I was wonde...
# announcements
b
Hi all, I have the following code, and I was wondering how to unwrap my collection of optionals into the actual type. Is there a better way to do this in Kotlin?
Copy code
data class Alert(val message: String)
val alerts : List<Optional<Alert>> = getAlerts()

// filter 
val messages = alerts
    .filter { it.isPresent }
    .map {it.get().message }
n
Just curious why are you using Optional instead of
Alert?
if you had
val alerts : List<Alert?>
then you could
val messages = alerts.mapNotNull { it?.message }
☝️ 2
f
I don’t know why you use Optional with alerts, but any way i suggest to add your unwrap method version as Extension function Or through inheritance -- i found this on StakeOverflow it may help
Copy code
fun <T> Optional<T>.unwrap(): T? = orElse(null)
Then use it like you wanted:
Copy code
val msg: Something? = optional.unwrap()
b
Good question on the use of Optional, not really a good reason for it really. It's just that sometimes we can have alerts and sometimes not, hence we thought optional might be better
n
but why Java's Optional
instead of Kotlin's
b
What is Kotlin's Optional? You mean nullable vs non - nullable? No good reason, except that we didn't want to deal with nulls I guess
n
but... kotlin is null safe?
That's like, a major feature of the language?
Basically in Kotlin if you want to represent an Alert that may or may not be there, you use
Alert?
, not
Optional<Alert>
Kotlin will not let you do anything with an
Alert?
where you need an
Alert
b
That is if you're assuming my code will only be used by kotlin code, but it's mixed with Java
f
@bodiam i just want to be on the same page and make sure that i understand your problem, you need to filter alerts according to value that may be true or false in whatever situation, so what about to add boolean to your Alert class and filter according to its value?
b
And null safe doesn't, at least to me, mean you should just return nulls everywhere. I'll end up with question marks all over my code
n
i don't understand. what's the difference in your mind between Optional<Alert> and
Alert?
? Leaving aside the fact that the code is mixed.
b
Null checks
n
but you have to do checks in both cases?
The only difference is that Kotlin provides tons of syntactic sugar for
Alert?
, and none for
Optional<Alert>
if you want to use Optional b/c of Java interop, sure, ok, do what Fady suggests in the SO link, just add an extension method that unwraps it to nullable. So you have:
Copy code
val messages = alerts
  .mapNotNull { it.unwrap()?.message }
b
Similar, but not the same. I can map an optional, but I can't map a nullable object, cause I need to do an if null then map check. Maybe the difference is a style issue, or maybe I'm just wrong :)
n
you can map a nullable object?
you just have to deal with the null case
I mean, show me code you think you can write with Optional<Alert> that you cannot write with Alert?
I showed already how you can write your example a little more nicely, and it's precisely by making use of
Alert?
b
@Fady Emad that doesn't sound ideal. Then I have to return something when there's nothing, but check the content of the object if it's a real result or a empty result.
n
but.... an empty Optional also holds nothing?
b
Yes, but that's better than a custom solution like Alert(empty = true)
n
ah the bool member, thought you were referring to the SO thing.
yeah, the bool is not good.
I'm not sure what the problem is with the unwrap extension method, and the code I gave
b
@Nir I like the Alert? suggestion btw, I'll do that instead
There's no problem with your suggestion at all, I hope I didn't give that impression
n
cool. keep in mind, honest: outside of Java interop, or existing API's etc, there's really no reason to use Optional<Whatever> in Kotlin. Honest!
nah no worries
b
It's probably a Java leftover in my head. Old habits die hard :)
n
Alert? is semantically identical to Optional<Alert>. just built into the language, so it's super slick.
t
if you're calling a java optional from kotlin a lot and need to retain the interop, I'd write an extension function
👍 1
😉 1
n
Yeah Java is tricky because it's not null safe, so my understanding is that practice is to avoid nulls completely and only use optional if you mean that something can be nullable
In that sense, it's better to think of kotlin nullable types as more like optional types
t
Copy code
fun Optional<T>.unWrap() : T?
n
that capital W offends me 😉
😀 2
t
Optional (or better yet Kotlin-Arrow Option) can be more useful in some circumstances
particularly if you're using a lot of monad transforms
b
unwRap?
t
Copy code
fun Optional<T>.unwrap() : T?
😄
n
my understand is that Arrow is moving towards the kotlin built in optional
(nullable)
t
yeah, but composed using suspend coroutines
n
not sure I follow that
@higherkind sealed class ~~Option~~<out A> : OptionOf<A>
 Deprecated: Option will be deleted soon as it promotes the wrong message of using a slower and memory unfriendly abstraction when the lang provides a better one.
👍 1
t
yep, and the idea is that you call inside IO blocks or whatever its called, to avoid eager-evaluation
n
yeah I know the basic idea of monads, i don't see why you can only make them work with Optional<T> instead of T?
and it seems like arrow themselves are moving away from that so 🤷
t
Mostly to Either, etc
They also got rid of Try
n
but Either is different, it's generally successful value or error, it holds more information than T?
t
yes, but when you're several layers deep in a call-graph, bubbling nulls up through the layers often not so great
n
I do hope something like Result (but a bit better) is standardized but it's a bit neither here nor there
I mean, doesn't seem different than bubbling Either's
t
If the error can happen several layers deep in a call stack, often do something like
Copy code
object MyError
Copy code
fun myFunc(): Either<MyError, T> = nullableFunc().rightIfNotNull { MyError }
sometimes MyError as a sealed class, or implementing an interface that contains a message I can return to the called. It composes really elegantly if you use the new Either DSL
n
I mean you can make optionals compose just as elegantly? that's essentially already what ?. is doing for you
the difference is that optionals/nullables aren't really designed for error handling because they don't carry error information
but that's not related to ease of bubbling it up
t
because down the line you can do:
Copy code
either {
   val a = myEitherFun().bind()
   val b = anotherEitherFun(a).bind()
}
etc
n
Okay, and what is the effect of this?
if one of the function calls returns an error, will
.bind
cause early exit from the
either
block with that error
t
it ends up being a little like checked-exceptions, but now it composes really cleanly
yes - and the either { } block will itself return an Either<A,B>, shortcircuited to whatever the first error was
n
Okay, but you can trivially write something equivalent for nullables, you understand that right?
t
try it for a non-trivial case and see
n
i mean I've done these things before... so I've already seen 🙂
t
very quickly you end up with nested lets, and it gets ugly fast
n
but you can write your own version of
either
?
t
the way that works under the hood is really non-trivial
n
you could take that code, replace all the Either<A, B> with A?, make sure that A is constrained everywhere to be non-nullable, replace any checks for "is right" with "is null", and replace any return B's with return nulls
and it will... work
Anyway, if you're saying optional is more useful than ? because this code already exists, okay, sure, but arrow is moving away from Optional, and Either is something pretty different to the original discussion, so... the conclusion remains to just use ? if you need a nullable type
t
you could get a similar effect using lets, but it would look like this:
Copy code
val a = nullable()
val b = a.let { nullable2() }
val c = nullable3()

// want to add b + c, but not really clean
// val d = b + c
n
right, my point is that you can just take the exact implementation for either, and write something exactly equivalent for T?
t
no the above is using T? it gets messy when you lose the linearity a -> b -> c because there is no short-circuit
n
I don't honestly know how else I can explain it
t
ie we need a guard around the evaluation of d, because we can only do it if both b + c are non-null
n
yes,
let
is built into the language, you can write a new function that works exactly the way
either
works, but for nullable
t
try it in the above case...
n
I don't need to try it.... you can literally copy the
either
implemenhtation and change it to work for nullable types. Are you asserting that's not possible?
like, I'm not really sure what your claim is, or ever was.
t
don't like where this convo is going, let's drop it
n
sure. why don't you give it a shot, you'll see it's pretty simple.
t
I did, and you immediately run into the val d = b + c issue in the example above. I don't think you can replicate what either {} is doing without a monad container. thanks for the convo 👍
n
lol you mean like:
Copy code
val x = runCatching {
    val a = nullable()!!
    val b = ....
}
hey look... it's built into the language
t
that works, but you don't know which line threw
n
you replace "either" with "runCatching". or, if you prefer, you can wrap runCatching in another lambda that maps the result back to a nullable
Pretty much one of the first things I said is that nullables are not suitable for eror handling because they don't carry error information....
you started this whole convo by saying there was reasons to use Optional instead of ?
If you use Optional, with an either like construct, you still won't know which line threw
Either vs nullable types is totally orthogonal to the entire original discussion
The only person that brought Either into it was you. I was strictly talking about Optional vs ?
👎 1
t
look - I'm here to talk about programming - this isn't the presidential debate. :-) my point is simply that when you've got multiple functions that could return errors and need to be chained together in non-trivial ways, monad containers like Either and Optional can make your life really a lot easier, particularly when using the either {} or validated {} DSLs. it's overkill in Erik's case, but can be a useful option (heh) in your toolkit when it gets a bit more complex
👍 2
n
but what does Optional give you over runCatching and !! ?
It won't tell you which error occurred either
Anyhow, you should probably let the arrow people know they're making a huge mistake deprecating Option... Guess you know something they don't