What have you found for these years?

2016-01-19

A few thoughts on "nil Is Not NULL, and Other Tales"

A few thoughts on: nil Is Not NULL, and Other Tales.

nil is an Object, NULL is a memory pointer
[...]
To be absolutely clear, a segmentation fault and a runtime exception are radically different things. But the consequence, your program aborts, makes them seem quite similar.
Yes, I agree. However:
and if its "nil" doesn't support that, something must be done. This characteristic of a statically-typed language makes nil seem dangerous.
No, it's not. It's called NullPointerException in Java, it's still just an exception. This is not the characteristic of a statically-typed language. They could be the same in both statically/dynamically typed language. My point is that nils are not superior in a dynamically typed language.

nil is mathematically realistic
[...]
A value like nil signals that "no mapping is available for that input". It's a very useful concept.
The concept is useful but are we talking about the concept or nil in Ruby in particular? You're talking about null set here but it's not the nil in Ruby. The nil in Ruby represents "nothing" not "null set".

nil is mistreated in Ruby
[...]
In the first case, developers tend to see nil as an error, and hence, it is quite unwelcomed. "Oh look, that method returned a nil, something must be wrong!"

No, not at all! Nothing need be wrong. The method returned a nil to say, "no value here!"
This really depends on the API. If an API claimed that it should never return nil, then it should never return nil and if nil was returned, surely something must be wrong! On the other hand, of course we could design an API which could potentially return nil. In Haskell, this would be called Maybe. A value of Maybe would either be Nothing (nil in Ruby) or Just something else. In this case, indeed nothing need to be wrong. It's all depending on the specification. The question is, could the specification really enforce the requirements/constraints?
In Rubinius, this is such a problem that we had to introduce a special value we called "undefined" to be able to implement the Ruby core library in Ruby. The problem is that some methods take nil as a value, but also have a default value. So, we were unable to distinguish between some_method() and some_method(nil).
Indeed this is really annoying. The concept of undefined was there but Ruby doesn't have a value for it. I often wrote Undefined = Object.new for default arguments. I hate that JavaScript has null and undefined, but seriously sometimes we do want "undefined" in Ruby. Hash is another example which has this difference.
h = {}
h[0] = nil
h[0] # => nil
h[1] # => nil
h.has_key?(0) # => true
h.has_key?(1) # => false
h.keys # => [0]
h.values # => [nil]

The "lonely operator" is an unnecessary mistake
The "lonely operator" supposedly solves the problem of calling a method on nil that then raises a runtime exception causing the program to abort.

Unfortunately, it only partially solves this problem
I totally agree that this is a mistake and it does not partially solve the problem, because it does not solve the problem at all. It's merely making it easier for us to write sloppy codes which could hinder real bugs, encouraging the use of unstructured data (i.e. hash tables) over the places, spreading nils around the world.

It's somehow a curse, from the `try` method from Rails, from people abusing JSON and hash tables, using them like JavaScript objects without classes.

It's backward.

Not to mention a special syntax for it. Ruby's syntax is already horribly complex.

nil is A Good Thing™
Well, I can't say it's a good thing or bad thing. We need the concept, but not necessarily the current nil. The problem is that, there's no way to enforce an API that would never return nil. Granted, actually this is the same issue lacking static types. It's just that nil is the most possible value which could sneak in because THERE ARE SO MANY METHODS could return nil. If we made that most of the API, the objects and methods from standard library would seldom return nil, then we are probably saved.

For example, h[0] could give me HashMissingValue instead of nil, and other methods could return other similar values, and the point is, they should be distinguishable.

Yes, and that's actually very similar to that Traceable nils in Rubinius which mentioned in the bottom of the post. That, yes, could probably solve the issue.

The simple alternative to the "lonely operator"
Defining method_missing on nil can't be the alternative to that lonely operator, unless we're using "refinements" to localize the changes. I am not saying refinements are good, but I think we all agreed that global behaviour modification is 99% bad, unless we're talking about a small script.

Ruby developers need to see where nil is
[...]
This problem of knowing where a value comes from is much bigger than nil. It is a result of the fundamental tradeoff that a late-bound language (usually called a dynamically-typed language) makes relative to an eagerly-bound language (usually called a statically-typed language).
Yes, exactly, though even a statically typed language could still be plagued by nil. See NullPointerException again. Some talked about Option type (again, Maybe in Haskell) could be the answer, for example Scala does return this for some of the methods. However, I don't feel this solve all the problems because there are still nulls somewhere else. Guess what, Option value could also be null. So actually there are 3 possible values: Some something, None, and our beloved null.

Well.
Late binding provides a malleable system that easily manages the complexity of high-uncertainty contexts. Objects can interact with other objects that provide certain behaviors. They do not have to be specific kinds of objects. The lessening of the constraints that the developer's assumptions impose on the system can increase the utility and resilience of the system.
This is certainly true though. While now I strongly believe in static typing much more, this is the reason why I still cannot give up Ruby and still prefer Ruby in some cases. Playing around it, interact with it. That's sometimes much more pleasant, until, I need to scale the complexity...

Traceable nils in Rubinius
[...]
In the past, we have only ever used that precise value. In other words, all the other bits are zero. But nothing requires this, and those other bits don't need to be wasted. On 64bit architectures, this gives Rubinius approximately 259 values of "nil". That's more than enough for a typical Rails app, I'm sure.
I was like, what the hell was this? when I read this paragraph. But to think about it, indeed we could use this trick to provide insights about where the nil was generated from. I highly hope this would work well and debugging nils could be much easier, and then, perhaps we could really afford returning nil everywhere. I don't know, but maybe.

Cheers,

0 retries:

Post a Comment

All texts are licensed under CC Attribution 3.0