Hacker News new | ask | show | jobs
by dabfiend19 1766 days ago
as opposed to?
3 comments

Optionals would have been a way to solve this problem
You know what's fun?

Getting a nil where you are supposed to have an Optional.

Afaik this is impossible in swift and kotlin, only optional values can contain nill.
Try Core Data with Swift and you will see that happening. Lazy objects (vaults) are mapped from objc into Swift and will happily crash on something like a = b where both are not optional.
This is happening in objc code or in the swift part? I'm not terribly surprised though, my one experience with core data was miserable once we strayed even a little from the happy path and I ended up rolling my own since we didn't need full functionality anyways. And this was for an internal app, at Apple ┐( ∵ )┌
It's possible in Java.
Right, because the language has the "million dollar mistake" of nullable references by default, which you cannot change without breaking code. And the original comment was bemoaning that Go choose to to have nullable references by default too.
so... Just make that impossible. It's not like this is unprecedented at this point. It's a standard feature even C++ of all languages supports.
Somebody has used Scala
More likely a Java lib form Scala than Scala as such.

In "pure" Scala (not in the FP sense, but just without mixing with Java) something like that is almost impossible.

Unless something drastically changed in Scala 3, there is nothing to protect you from null in Scala. In fact even Java is effectively safer thanks to all the null checking done by IntelliJ
Null is basically non-existent in idiomatic Scala. So technically you're right but besides calling Java libs there is only an infinitesimal small chance to get NPEs form Scala code. (Scala's NPE is the MatchException ;-)).

For Scala 3 there are improvements. It's "null safe" as long as you opt-in (modulo Java libs, and of course doing stupid things like casting a null to some other type).

https://docs.scala-lang.org/scala3/reference/other-new-featu...

So you get an Optional which haven't been set instead of a nil pointer. What's better about that?
The type system knows about it and you're forced to check it
Right, but my point is, the code which would raise an error because the pointer is nil now raises an error because the Optional is not set.

Is there really that much of a difference between those cases?

I agree though that in an interface, Optional conveys a more explicit meaning than something pointer-like, which is always a good thing.

In practice it does make a big difference because if the type system knows about it then it can enforce handling of the exception. (Or more generally, it can just force you to pattern match on the optional time and make sure you handle the empty case.)

Go programs crash at runtime. In general, program failures and bugs should surface as soon as possible. Ideally no later than compile time. Instead, Go makes you wait until the app is running.

For an app that has a lot of configuration options, for example, there can be a latent bug that crashes the binary for some options. And that bug may not be detected for months because nobody was using that combination of config options.

The only real defense of this is to pepper your code with a bunch of nil checks. But these nil checks are also hard to test, so Go devs just learn to ignore missing code coverage. In fact, your code coverage metrics look better if you don't check for nil.

I'm sure at some point Go or a library will offer a version of optional types that is well-adopted. But my point is that by the time Go was designed, null references were already widely considered a bad idea and the source of a huge class of computer bugs. Go still deliberately designed them into the type system.

Some people think Optional/Either/etc are the absolute cure to a certain sort of problems and -- I speak from experience -- it is impossible to convince them that it's not.

Well, of course, if your end users, the business users, are okay if you present them an "optional result" which might or might not be a result, then Optionals/Either _are_ the cure. Unfortunately most end users are pissed if you tell them that you optionally shipped their purchase or that the refund will either be credited to their CC or not.

It's about handling those error cases or failing the build, rather than getting a null pointer exception at runtime that moves execution to some higher part that has no context and little ability to correct the problem, or just crashes. You can still handle it wrong, but you are forced to handle it rather than just, in your example, charging the card and crashing the thread when updating the database that you charged them.
One has to be addressed at compile time and the other is a runtime error. Sure, you can address it wrong, but it's better than any reference anywhere being able to throw and in my experience really does cut down on application crashes.
Interesting. I've been coding in languages which heavily rely on pointers for decades, primarily C, C++, C# and Turbo Pascal/Delphi, and in my experience nil pointer errors are quite rare.

I agree that it's nice to be explicit about optional stuff, but overall it's not been a huge deal in the projects I've been involved with.

Optional doesn't give you that automatically. In C++, you can happily dereference a std::optional without checking it.
As opposed to some modern type system feature that would catch such bugs at compile time rather than let them happen at runtime.
Most modern languages have solved the problem. There are a variety of ways to do it.
Can you list the variety of ways?
Basically if you need to manipulate pointers, use safe pointers and keep track of pointer ownership. This is how modern C++ and Rust work.

If you don't need to manipulate pointers then nil really just represents a degenerate or optional value. For optional values these can be encoded any number of ways depending on the type system. One common pattern is an optional type. Another is to annotate the type to indicate that it might be null.

The idea is that if a programmer doesn't check that an nullable or optional type is missing then the program should fail at compile time instead of crashing at runtime. Golang chose to crash programs at runtime.

So for whatever reason, Go has decided that null pointer dereferences are not a big deal. But God help you if you try to comment out a variable use without assigning it to "_". Then the program fails to compile.

> So for whatever reason, Go has decided that null pointer dereferences are not a big deal. But God help you if you try to comment out a variable use without assigning it to "_". Then the program fails to compile.

I think that's a good explanation of why people are so frustrated with this. There are lots of features like go fmt, go vet, the compiler checking that you use all variables and all imports that can feel a bit restrictive. But for something like null pointers, there is nothing. It's incoherent.

I don't know about "variety of ways". You just make it so pointers can't be null, then provide a mechanism for opt-in nullability, requiring an explicit check / conversion to get a non-nullable pointer (which you can dereference) and allowing free / cheap / implicit conversion from non-nullable to nullable. This can be:

* separate pointer types (e.g. C++ pointers v references)

* a built-in sigil / wrapper / suffix e.g. C#'s Nullable / `?` types

* a bog-standard userland sum type e.g. Maybe/Option/Optional

In modern more procedural languages the third option often will have language-level (non-userland) facilities tackled on for better usability but that's not a requirement.

For cases (2) and (3) it can (depending on language and implementation) also provides a mechanism for making other value types optional without necessarily having to heap-allocate them.

Usually it's forcing the programmer to handle the null case statically, by wrapping the underlying value in something like an optional type and defining the operations that access the underlying value. Think of swift's optional unwrapping in "if let" statements