Isn't there an inherent need in programming to express an explicit "nothing" value? Coming from Python and JS, I never found None/null to be much of a problem. I in fact like the distinction of null and undefined in JS. Using null allows you to distinguish from the accidental undefined.
The major criticism towards NULL's does not apply to dynamic languages. The problem in languages like Java is that nullability is not represented in the type. This criticism is not relevant in a language without static type checking. The criticism is also not relevant in a language like TypeScript where nulls are specified in the type.
In short, the problem is not nulls per se, the problem is static type systems which does not allow you to specify if a certain value can be null or not.
But I hereby predict that soon people are going to declare that "nulls are bad" and eliminate them even in languages where they are totally fine.
The OP specifically calls out ways that nulls cause problems in dynamic languages. In fact, they're very similar to the problems nulls cause in statically typed languages, just ignoring some of them because danger is already priced in with a dynamic language.
Dynamically typed programs are usually informally "duck typed," since it's impossible to do something meaningful with truly arbitrary types most of the time. But just like in statically typed languages, there's always the very real possibility that your duck will instead be a null — and the bug is not the function returning a non-duck, it's you ever assuming you have something that quacks like a duck.
Well in a dynamically typed language there is also the possibility than something you expect to be an integer is in fact a string. Null or Nil or None is not different in this respect than any other type.
But of course you can make bugs involving nulls. The OP shows an example in a dynamic language where null is returned instead of the regular value if a lookup key is not found. Obviously this is ambiguous when the found value can also be null. But the problem is not the null per se, the problem is using a sentinel value to indicate a special condition when the same value is also a legitimate regular value. This is just bad API design.
The way Clojure handles nulls is ideal for a dynamically typed language. In Clojure, a nil is a perfectly valid object, and it is also a sequence (it's the empty list equivalent). That means that most standard library functions have no problems letting nil flow through them, returning nil. It winds up being quite similar to monadic Option/Maybe, although with no guarantees.
The key word here is explicit. Explicit Maybe/Optional types from Haskell, Swift or Rust are wonderful. After my experience with Elm and Swift, the mere thought of going back to implicit nullability everywhere hurts.
No, there’s no inherent need for every variable to allow a nothing.
There is a need for a nothing value in many cases and languages without an implicit null, such as Haskell, F# and Rust, employ an ‘option’ type. It’s a dedicated type that either contains a value or nothing. It forces you to declare when you expect a potential null and to check for it.
Because it's a 0 overhead abstraction and guard against the case when you (or a coworker) forget to check for null. Are we seriously gonna pretend like you never caused a null pointer exception?
std::optional doesn't do that though, you can deref an empty optional and the result's the same as deref'ing an empty unique_ptr or a null pointer: UB.
Even in dynamic languages, it's generally bad practice to have functions that return multiple possible types, and you are often better served by returning a more proper "null object" that accepts the same messages/methods as what would normally be returned. But for the most part, the problem people have with null is related to compile time checks and it not being opt in, which is unrelated to dynamic languages where every return type cold contain... whatever.
I agree that returning the same type, if possible, is better, since you can model it so that one specific value (zero, empty string, etc.) expresses the "nothing" accordingly. But I have found that several other developers don't share this view and prefer functions to return false, null, etc. instead of a value in the same space as the normal return type.
> Isn't there an inherent need in programming to express an explicit "nothing" value?
In numerical calculus: yes, most certainly. What do you expect the result of log(-1) to be? The alternative is to use specially tagged particular numbers as "no-data", and pray so that they do not appear naturally as the result of computations.
Technically, you could force the value to be one constrained to a valid range, rather then augmenting the domain, but this is a lot of work and maybe not worth it for pracial use.
Some systems already do that. C#'s Double type includes Double.NaN, Double.NegativeInfinity, and Double.PositiveInfinity. Math.Log(-1) does indeed return NaN.