News for hackers. Stuff that matters.
Re:Things Go needs more than generics by ohgodplsno on Monday October 04, 2021 @08:03AM (#28745599)
* val input: String is a non nullable String. You can never assign null to it, and null will never be assigned to it (unless you do some reflection stupidity, or really, really rare cases of you're-fucked-anyways-if-that-happens-something-is-really-wrong)

* val input: String? is a nullable String. It's basically String|null.

There's a third option, which is String!, platform types: since Kotlin has interop with Java, Kotlin will err on trying to be practical for you when calling Java code: it will (rightly so) assume that it is a non-nullable String, but still warn you that it doesn't have the necessary info to infer nullability/non-nullability, so it could technically be null. It's up to you to decide if you want to null-check it. This can be solved in two ways:

- Update your java code to include @Nullable/@NonNull annotations. You should already be doing that anyways, especially if you have control over it. - Don't call Java code/wrap it in Kotlin wrappers. Kotlin code does not have this type inference issue.

Re:Things Go needs more than generics by zeckalpha on Monday October 04, 2021 @08:05AM (#28745611)
You just described an implementation of Option types, albeit one with syntactic sugar of a ? Rather than Option<String>.
Re:Things Go needs more than generics by tel on Monday October 04, 2021 @09:00AM (#28746104)
I 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.
Re:Things Go needs more than generics by ohgodplsno on Monday October 04, 2021 @08:24AM (#28745756)
Not 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)

Re:Things Go needs more than generics by nemetroid on Monday October 04, 2021 @08:44AM (#28745933)
That sounds like an Option type to me:

  String? :: Option
  String :: Option.Some
  null :: Option.None
Re:Things Go needs more than generics by Nullabillity on Monday October 04, 2021 @09:17AM (#28746293)
The 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).
Re:Things Go needs more than generics by dtech on Monday October 04, 2021 @08:55AM (#28746047)
I'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.
Re:Things Go needs more than generics by nemetroid on Monday October 04, 2021 @09:48AM (#28746642)
> 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.

Re:Things Go needs more than generics by kevinmgranger on Monday October 04, 2021 @09:08AM (#28746194)
There 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.

Re:Things Go needs more than generics by Semaphor on Monday October 04, 2021 @08:51AM (#28746003)
This is pretty much exactly how it works in C# nowadays (though it’s opt-in).