10 ms·
Hoare didn't invent 0-valued pointer. It was there since the beginning of time. Or at least the beginning of the CPUs. People talk about getting rid of nil lik
by kkowalczyk 13y ago
Hoare didn't invent 0-valued pointer. It was there since the beginning of time. Or at least the beginning of the CPUs.
People talk about getting rid of nil like it's actually possible.
It's not.
If you have pointers, they sometimes have to start the life uninitialized (i.e. with a value of 0) hence nil pointers.
As you admit yourself, the proposed solutions don't actually get rid of anything. At best they can force you to handle nil value by wrapping it in some wrapper.
Guess what - if you have that kind of discipline, you can do the same in C++.
Why aren't people doing that?
Because it comes at a cost. There's a cost to the wrapper and when it comes down to it, you still have to write the code to handle the nil pointer whether you're using a wrapper or a raw pointer.
It just doesn't buy you much.
Finally, fixing crashes caused by referencing a null pointer is downright trivial. Spend a few weeks chasing a memory corruption caused by multi-threading and you'll come to a conclusion that fixing a null pointer crashes (which can be done just by looking at the crashing callstack most of the time) is not such a big deal after all.
- JoshTriplett 13y ago> If you have pointers, they sometimes have to start the life uninitialized (i.e. with a value of 0) hence nil pointers. Right, and those pointers must then be declared as potentially null. Or more generally, potentially non-present values of any type need a type like Maybe T. > As you admit yourself, the proposed solutions don't actually get rid of anything. At best they can force you to handle nil value by wrapping it in some wrapper. That's exactly the point: you can tell from a glance at any type whether it can be null or not, and you can only look at the value of something that's guaranteed to not be null; otherwise, you have to handle the null case first. Forcing that to happen at compile time via the type system is far better than dereferencing a null pointer at runtime.
- eru 13y ago> If you have pointers, they sometimes have to start the life uninitialized (i.e. with a value of 0) hence nil pointers. Why do your variables have to start life uninitialized? > Why aren't people doing that? Because it comes at a cost. [...] Not a runtime cost, though. This can be handled purely by the typesystem at compile time.
- jdmichal 13y ago> If you have pointers, they sometimes have to start the life uninitialized (i.e. with a value of 0) hence nil pointers. Maybe at the machine level. But there's nothing that stops a programming language a few levels above machine from requiring that every pointer be initialized with a reference. > As you admit yourself, the proposed solutions don't actually get rid of anything. At best they can force you to handle nil value by wrapping it in some wrapper. The entire point is that a large majority of APIs don't WANT to accept or handle NIL, but have to, because the default is to allow it. And in some languages, such as Java, the only way to extend the type system is with reference types, making it impossible to ever not have to handle NIL. By reversing this decision, it becomes possible to specify both allowance and disallowance of NIL-valued parameters. Why you would ever argue against such expression is beyond me.
- jaekwon 13y ago> Maybe at the machine level. But there's nothing that stops a programming language a few levels above machine from requiring that every pointer be initialized with a reference. What if the pointer is to a large structure (expensive to initialize), and I want a function that returns a pointer, which may fail to initialize? Without a nil, developers would create structures that have a "valid" field. Nil just makes that more convenient, and the way Go does it is pretty good -- you can't cast it the same way you can in C/C++.
- lucian1900 13y agoWith a Maybe/Option type, you are forced to always deal with the possibility of Nothing/None. And even better, you can use monadic bind to chain together several actions on possibly-nullable things and get either a value or a null at the end.
- waps 13y agoAn argument can be made that this merely masks the problem : it is certainly possible for a Haskell function to crash (and therefore not return anything), no matter it's declared type : reallyfun xs = reallyfun xs ++ [ 1 ] Note that this will OOM, not just run infinitely long. There are plenty of ways you can cause this sorts of issues. So you can't trust Haskell functions to always return their declared type either. The million-dollar-question : should your program be ready for this ? (in the case of a database : ideally, yes it should, and it's in fact possible to do just that) That's the problem with abstractions, like the "always-correct-or-null" pointers of java : they're leaky. A type system, unless reduced to pointlessness, can't really be enforced fully. Haskell ignores failure modes, like memory and stack allocation, jumping to other parts of the program, reading the program, ... all of which can in fact fail. Thinking about this gives one new appreciation for try ... except: (catching an unspecified exception). It's not necessarily worse than a pure function. Good luck defending that position to mathematicians though.
- nostrademons 13y agoAs others have pointed out, I'm not talking about nil as the zero-valued pointer at the hardware level. I'm talking about nil as the additional member of every type in the language's typesystem. A typesystem is an abstraction over the hardware, designed to catch programmer errors and facilitate communication among programmers. There's no reason it has to admit every possible value that the hardware can support. And people are applying that sort of discipline - see the NullObject pattern in any OO language, or the @Nullable/@NotNull annotations in Java, or !Object types in Google Closure Compiler. The thing is, they have to apply it manually to every type, because the type system assumes the default is nullable. That makes it an inconvenience, which makes a number of programmers not bother. I'll agree that null pointers are comparatively easy to track down compared to memory corruption caused by multi-threading. Threads are a broken programming model too, and if you want a sane life working with them you'll apply a higher-level abstraction like CSP, producer-consumer queues, SEDA, data-dependency graphs, or transactions on top of them.
- dietrichepp 13y ago> Threads are a broken programming model too More specifically, threads with shared mutable state. If state is never simultaneously shared and mutable then a host of problems disappear.
- nostrademons 13y agoAnd even more specifically - threads with locks. Shared mutable state is okay as long as any state-swapping operations are atomic, eg. with STM or if you build up the state in one thread, switch it with an atomic pointer swap, and don't let other threads mutate the state.
- pcwalton 13y agoThreads with locks are fine as long as the compiler enforces that you take the lock before mutating the data (e.g. Rust). :)
- 13y ago
- derekchiang 13y agoI'm surprised no one has mentioned Rust[1] yet. It's a systems programming language that has managed to get rid of null pointers by carefully controlling the ways in which a a value can be constructed[2]. [1] http://www.rust-lang.org/ http://www.rust-lang.org/ [2] https://github.com/mozilla/rust/wiki/Doc-language-FAQ#how-do-you-get-away-with-no-null-pointers https://github.com/mozilla/rust/wiki/Doc-language-FAQ#how-do...
- Dewie 13y agoAnd IIRC the compiler translates usages of None to null, so any complaint about inefficiency probably does not apply in this case, either.
- dietrichepp 13y agoThis is actually quite common in languages which support ADTs—you stick tags in the pointer to discriminate between subtypes of a union.
- Jare 13y agoI don't think Rust is such a shining example of a solid language in this area, at least not yet. For example: http://stackoverflow.com/a/20704252/626867 http://stackoverflow.com/a/20704252/626867
- Sharlin 13y ago> Guess what - if you have that kind of discipline, you can do the same in C++. Well, ironically, C++ already has non-nullable pointers. They're called references and they work extremely well.
- anon4 13y agoint& toref(int* ptr) { return *ptr; } //snip int& i = toref(0); i = 5; // segfault One thing I kind of appreciate at times about C++ (or at least about certain implementations) is the fact you can call methods on null pointers and you can check in the method if it's called on a null pointer. I.e. class A { public: int foo() { return this == null ? 1 : 2; } }; //.... A* a = 0; std::cout << a->foo(); Which allows you to make classes that work just fine even if you use a null pointer.
- Sharlin 13y ago> int& toref(int* ptr) Yes, it's theoretically possible. The point is, you need to explicitly do evil things to get "null references". The language cannot and is not meant to protect you from yourself. > One thing I kind of appreciate at times about C++ (or at least about certain implementations) Yes, certain implementations indeed. It is formally undefined behavior, so anything at all could happen if you do that; the most straightforward and efficient implementation just happens to "work".
- anon4 13y ago> The point is, you need to explicitly do evil things to get "null references". The language cannot and is not meant to protect you from yourself. No, you don't need to do evil things at all. Any C api that returns a pointer which you then need to pass to a C++ function that takes a reference is a possible source of failure if you forget to check your pointer. It can happen quite easily by mistake. The language cannot in any way guarantee that you won't make a reference from a null pointer. At least taking a reference as an argument does at least alert people that you're not expecting to be passed a null as an argument to your function.