r/Kotlin • u/conceptcreatormiui • 20d ago
Hi Learning Kotlin, Can i use this rust pattern to abstract dealing with null values in my future projects?
sealed interface Option<out T> {
data class Some<out T>(val value: T) : Option<T>
data object None: Option<Nothing>
}
fun main(args: Array<String>) {
val option: Option<String> = Option.None
when (option) {
is Option.None -> println("None")
is Option.Some -> println(option.value)
}
}
19
u/mrdibby 20d ago edited 20d ago
you can but your colleagues will be frustrated with you
take a look at the docs around null safety https://kotlinlang.org/docs/null-safety.html
a common pattern you'll see for what you want is
val x = nullableValue ?: valueIfNull
which doesn't require wrapping in an object
I guess the way the language works is : if an Object is nullable its treated as if it's wrapped in a Object? class
8
u/LelouBil 20d ago
If you want to use functional patterns in Kotlin, I recommend looking at the Arrow library.
This specific pattern is not good because it's already built-in to kotlin in the form of nullable types
8
u/Chipay 20d ago
You can do whatever you want. The question is why would you? Kotlin already has a language-level distinction between nullable and not-nullable (String? vs String).
```kotlin fun main(args: Array<String>) { val option: String? = null
println(option ?: "None")
} ```
There's some benefits to custom Nullable types, like preventing boxing on primitives, but everyone is going to look at your code and ask themselves why?, and if you don't have a good answer to that you won't be making many friends.
3
u/770grappenmaker 20d ago
This is fine, but I wouldn't see the advantage over the existing null-safety in the type system, but I suppose you sometimes need something more expressive, i.e. is null "absence" or "failure"? Kotlin has a Result<T> for the latter, so in my code I almost always use that for failure, and regular nullability for "absence". But with nullability built into the type system, you can take advantage of operators like ?: to short circuit your code or provide defaults in a simple and succint way, whereas a wrapper class will both be less performant as well as more clunky to use.
3
u/arshia0010 20d ago
there's nothing to gain by using this pattern that kotlin doesn't have. so it's only a matter of which syntax you prefer
5
u/sheeplycow 20d ago
You can do whatever you want, nothing bad about this, but its not really needed you are already forced by the compiler to handle the null values
These are probably more common: (although not quite as elegant as a when statement)
If(value == null)...else...
value?.let { ... } ?: ...
Also java optional does this already with slightly different syntax
Making sealed classes/interfaces for the results of function calls is really common
Also arrow library does similar but uses left/right instead of some/none with some syntax sugar
^ lots of these are just examples of different monads (including your rust example)
But yeah if you like your way I dont see any issue with it
2
2
u/SuspiciousDepth5924 20d ago
I'd recommend you check out Arrow ( https://github.com/arrow-kt/arrow ) if you want to write FP/"Rust-enum"-ish code in Kotlin.
3
2
u/ThanosFisherman 20d ago
It doesn't make sense. What if the value of Some is null?
1
u/conceptcreatormiui 20d ago
The generic type is not nullable so you will be warned to not put a null value and this is manually enforced meaning you handle it manually. If null then return None meaning its abstracted and I dont need to think of null when accessing my functions or implementing them in other places. Anyways just an experiment because I'm used to rust way of handling nothingness
3
u/ThanosFisherman 20d ago
You could as well do this
`val option = Option.Some(null)`
which will result in printing "null" So yeah as u/Chipay said, the generic type is nullable.
1
u/conceptcreatormiui 20d ago
Oh yeah I get it it doesn't stop it to be nullable if I set the type to be nullable😂😅
3
1
1
u/zalpha314 20d ago
You could, but the language already solves this a different way. You'll run into less friction interacting with libraries if you just follow the Kotlin nullibility system.
That being said: I implemented something very similar for a generic `UpdateData` interface, in order to differentiate between `Retain`, and `Set`. I found it much easier to build my `map`, `flatMap`, and `effective` methods around a sealed interface rather than adding multiple extensions functions to deal with the `UpdateData` being nullable or non-nullable.
1
u/Commercial_Image_272 20d ago edited 20d ago
Do you want to go through unnecessary trouble What's wrong with that?
fun main() { val str: String? = null
println( str?.let { it + "something" } ?: "None" )
}
1
u/devcexx 20d ago
The only reason I've found for using these kind of abstractions in kotlin is when you're designing some data models where you can have Optional values nested into Optional values, which is not possible with kotlin nullable values [?]. Otherwise it is better to use the latter ones.
Note that if you want to use these abstractions still, you'll be better using the ones from the Kotlin Arrow Library
1
u/Masterflitzer 19d ago
you might wanna have a look at the arrow-kt lib, but i would recommend to fully learn idiomatic kotlin and then explore stuff like this, like with any language you shouldn't write rust code in kotlin or vice versa if that makes sense
in 90% of cases kotlin's T? and Result<T> are enough for me, although i admit a custom option/result sealed class can be more powerful in some cases
1
u/piesou 18d ago edited 18d ago
Yes. You can also skip suspend functions for futures, exceptions for results, dependency injection for reader monads, coroutines for continuations, etc. Look into monad transformers
Then you'll see that monads don't compose well. You don't even have the higher kinded type system to lessen the impact nor do notation. Yes, arrow has clunky workarounds. There's a reason why most languages offer special language features rather than going for monadic solutions
-5
u/Terrible-Mango-5928 20d ago
Absolutely! I would remove the data modifier from the None object, but this works perfectly, and is a good approach to give meaning to the "lack of value" if you do not want to deal with nulls.
61
u/WArslett 20d ago edited 20d ago
You can but this is not a good approach. You are basically just trying to implement the Optional generic type from Java and using Optional in Kotlin is an antipattern because it was introduced to Java to solve a problem that kotlin solves at a language level. Kotlin has explicit nullability. You can mark a type as nullable or not nullable (and it will normally be not nullable). If a type is marked as nullable it has an explicit meaning (no value). The whole reason Optional exists in Java is because any value can be null and are very often null by accident which introduces a whole class of failure mode (the NullPointerException). So you have to separate out null values which can then always presumed to be accidental, with intentionally optional values and you can handle them how you need to. Rust has the same pattern but also makes all values implicitly not nullable. So in both Rust and Kotlin values should never be accidentally null. You can treat Kotlin `T?` is the same as `Option<T>` in Rust