3 ms·
You just described an implementation of Option types, albeit one with syntactic sugar of a ? Rather than Option<String>.
by zeckalpha 5y ago
You just described an implementation of Option types, albeit one with syntactic sugar of a ? Rather than Option<String>.
- kevinmgranger 5y agoThere is no way to have the equivalent of an Option<Option<String>>, so it's not quite the same. That said, if one ever finds themselves seriously using a nested Option, it's probably time to write a new enum for that use case.
- ohgodplsno 5y agoNot necessarily. Not only is it Option.Some/None/ProbablySomeBuyMaybeNoneBecauseJava, it doesn't offer the exact separation that Options can offer: you can stick a value in Option.None (String? can contain null or ""). Nullable types are different to options. (Bonus point for Java that has introduced Option types, that can still be null)
- nemetroid 5y agoThat sounds like an Option type to me: String? :: Option String :: Option.Some null :: Option.None
- Nullabillity 5y agoThe problem with Kotlin is that it only has one global "anything-but-null", so nested ?s are collapsed (String?? is equivalent to String?). This is because it needs to run on the JVM, which only knows about is-null and is-not-null. This interacts terribly with generics, since it means that only one side of the generic boundary can "own" the null value at any one time. To simplify a case where this can cause issues: class Loadable<T>( // null until value is loaded var valueIfLoaded: T?, ) // returns null if user is not found fun getUser(id: String): Loadable<User?> Code that interprets Loadable<T> will get stuck showing a loading bar, since it has no idea about getUser using null for anything else. This doesn't even generate a warning! The "correct thing to do here would be to add a `T: Any` bound on Loadable, which prevents T from itself being a nullable type. That would notify the author of getUser that they need to box the User?, so that the cases are kept separate: data class Box<T>(val value: T) class Loadable<T>( // null until value is loaded var valueIfLoaded: T?, ) // returns null if user is not found fun getUser(id: String): Loadable<Box<T?>> These are things you don't even have to consider when using a typical well-designed Option type (which, mind you, could still have a syntax sugar for T? if you wanted it).
- dtech 5y agoI'd argue that they are fundamentally different. Option is "just another type". You interact with it using constructors, map/flatMap/get etc. You could implement Optional in Java 1.5 but can't implement nullable types in current Java. Nullable types are a feature of the type system and require language support, but the benefit is that you don't touch typical programming patterns val foo: T? = x() if(foo == null) { return ... } // foo is now T, optionality gone v.s. val foo: Option<T> = x() // following must be wrapped and sprinkled in .map, .flatMap, .orElse You can sort-of achieve the first with .isPresent() + .get(), but it clutters the scope, is more verbose, and is not really idiomatic.
- nemetroid 5y ago> not really idiomatic. Really? I haven't used Java in a long time, but in the languages where I've used Optional types, you'd always check for presence once and then extract the value before further use.
- tel 5y agoI don't know Kotlin, but these usually are distinct from Options in an important way, idempotency `String?? = String?` or `String|null|null = String|null`. This equation makes sense for some mental models, but isn't quite the same as Option. In particular, Option[T] has the nice property that you can map over its inner type in a naive fashion `forall S. (T -> S) -> (Option[T] -> Option[S])` whereas the coalescing version above needs an additional assumption that `S` is non-nullable. forall S not null. (T -> S) -> (T? -> S?) This can really get in the way of some kinds of generic programming.