@WTFE said:
You can't get away from null ... and yet entire language environments exist in which there's not a nil/null/whatever check in sight.
None that I use regularly... except for assembly code. ColdC doesn't need to. It does have a nil object (#-1), but I've never actually used the syntax. You do have to do valid() checks on objects considering they can be user entered. But then that goes to the issue of interactive user programming being quite similar to hotloading code.
For example in Ruby I might have a Character that initially has two stats:
$ irb
irb(main):001:0> load "./character.rb"
=> true
irb(main):002:0> bubba = Character.new "Bubba", 3, 5
=> #<Character:0x00000600359390 @name="Bubba", @str=3, @cha=5>
irb(main):003:0> bubba.score
Name: Bubba
Strength: 3
Charisma: 5
=> nil
I decide to rewrite it to add a third stat:
irb(main):004:0> load "./character2.rb"
=> true
irb(main):005:0> boffo = Character.new "Boffo", 2, 4, 3
=> #<Character:0x0000060038aff8 @name="Boffo", @str=2, @cha=4, @dex=3>
irb(main):006:0> boffo.score
Name: Boffo
Strength: 2
Charisma: 4
Dexterity: 3
=> nil
irb(main):007:0> bubba.score
Name: Bubba
Strength: 3
Charisma: 5
Dexterity:
=> nil
The issue is that @dex doesn't exist on Bubba in memory (or for that matter in any data store).
So the field has no value or nil.
So you have to fix that issue with a handler that does it automatically.
For example, to fix all Characters in memory by setting a default:
irb(main):008:0> ObjectSpace.each_object(Character) {|x|
irb(main):009:1* if !x.instance_variable_get(:@dex)
irb(main):010:2> x.instance_variable_set(:@dex,3)
irb(main):011:2> end
irb(main):012:1> }
=> 2
And of course you'd have to update whatever data storage schema you're using.
In an interactive programming environment like ColdC the above is like:
@add-variable $character, dex = 3
It shouldn't be much of a leap to apply the same automation to hotloading code.
The above is just a simple example though. There are othe issues to handle like
deleting and renaming variables, deleting methods, etc.
You also need to correctly update the code itself.
Using Ruby meta-programming to write your own attribute accessors and consistently use
them in "plugins" helps a lot.
I don't know Erlang but quoting from the Wikipedia entry:
"Successful hot code loading is a tricky subject; Code needs to be written to make use of Erlang's facilities."
Same is true for Ruby and Python. It's a tricky subject.
@WTFE said:
If you can have a value that is "nothing" that needs to be tested for before you do things, you have a null check. It may not be a physical null pointer (although I'd bet large amounts of money that most ARE such behind the scenes), but it's effectively the same thing: a bottom test. An unnecessary complication that boils down to "get X, test if X is actually there, use X". Such a manual cycle is incredibly error-prone (and is, indeed, at the core of a lot of security breaches and other related bugs).
Languages which don't have this basically do proper abstraction behind the scenes. Prolog, for example, has no "nil" value (unless you deliberately put one in for the rare use cases in which it is necessary). Any language with proper higher-order functions (or equivalent) can also dodge the bullet in most cases. The same applies to languages like Erlang or the ML family (although sadly in the libraries people will define an equivalent instead of thinking out their abstraction; imperative thinking still infects even declarative languages a lot of the time when people get started).
As an example of how Erlang (the language, not necessarily the library) avoids the need for nil checks, the core of a proper Erlang program is a process. A process has a mailbox. A typical process waits for a message and reacts according to it. If there is no message it waits until there is one. There's no cycle of "get message; check if message exists; do something with that message" -- unless you explicitly TELL it to work that way (with timeouts, etc.). By default there's no need for nil checks.
Similarly pattern matching in the MLs, combined with the nice array of higher-order functions, eliminates the need for nil checks in the overwhelming majority of cases you'd find them in imperative code (to the point that I don't actually recall when I last did an explicit nil check in SML). Sure, under the covers, there's presence checks galore, but the end-user is shielded from them in 99.44% of the cases unless they're doing some very specific things.
As for Prolog, the most common use case for nil checks in imperative languages coincides with backtracking situations in Prolog. The runtime finds the "nil" (no solution) case for you and just goes back and tries again if there's another path.
Of course I went searching for muds that use Erlang, ML, or Prolog.
I did a search on my own project
Searching for 'nil'
Found 212 occurrence(s) in 60 file(s)
I found PrologMud which is more MOOish and has some very interesting features.
Searching for 'nil'
Found 622 occurrence(s) in 81 file(s)
Turns out my top use of 'nil' is in generated parser tables (Racc - Ruby Yacc). Not disimilar to Prolog's use of them in rules to indicate no solution.
The next biggest use was in test code assertions.
And then in parameters passed to functions. PrologMud appears to also do that also quite a bit.
But then I looked for explicit checks in my code.
Searching for 'nil?' or '== nil'
Found 30 occurrence(s) in 26 file(s)
That doesn't find them all because I also use other forms.
x ||= 56 * y
x && (x = y + z * 42)
I'm not very likely to switch my style of programming in C, C++, Perl or Ruby.
If anything it's a motivation to add more checks on nil or null.
Nor am I likely to eschew a language or environment that uses them heavily or learn one
because it doesn't. Prolog looks interesting just for its possibilities for natural language
parsing and AI which PrologMud seems to make use of.