In what languages references are non-nullable by default?
Out of interest, I want to read about languages that are designed like this. Haskell is one, righ开发者_JAVA技巧t?
I am talking about languages that does this, but also has compiler support to catch issues like if it's nullable, then you have to have appropriate cases, handling to compile, etc.
Also is it only a concept that's in functional programming? Does it also exist in some OO languages?
Just to answer the first part of your question, you're right that Haskell doesn't have a special 'null' value that can be of any type.
If you want this behaviour, you have to change the return type of your function. Typically, you use the Maybe type for this, so for example:
safeDiv :: Float -> Float -> Maybe Float
safeDiv a b
| b == 0 = Nothing
| otherwise = Just (a / b)
This says that safeDiv takes two Floats and returns a type Maybe Float
. In the body of the function, I can then return Nothing
if b is zero, otherwise I return Just (a / b)
.
The key point is that your type signature explicitly marks whether your function can return Nothing
or not, and any caller will be forced to handle the two possible cases in some way.
Haskell does, however, have Exceptions which can be thrown and caught. For pure functions it is preferred to return a Maybe value instead of just throwing an error, but even some Prelude (base library) functions are unsafe. For example, head
, which returns the first element of a list, throws an error if the list is empty, instead of returning a value wrapped up in Maybe.
Any of the ML-derived languages (of which Haskell is one) work like this, including SML and F# (although null
can leak into F# from .NET interop). Scala also generally shuns null
but the fact that it sits on top of the JVM makes it less strict about it than some other languages.
You will find that Nullable is a matter of definition.
Most languages can be seen to have nullable pointers but not nullable values. Also, many languages provide a special null/nil-object with which variables are initialized automatically; would you count this as nullable?
On the one hand it is a special value distinct from all the values the variable normally holds (e.g. numbers), and is from that perspective the same as if you work with only pointers to numbers (nullable).
On the other hand this null/nil-object is a real object with a real class (in class-based-OO languages) and will understand many messages you send to it like asString(), so since it is a real object it is not a "null" in the strictest sense right?
Two languages that come to mind which support both the object-oriented paradigm and non-nullable types are (arguably) C++ and (definitely) Spec#.
Spec# is a formal language for API contracts (influenced by JML, AsmL, and Eiffel), which extends C# with constructs for non-null types, preconditions, postconditions, and object invariants.
— from the Spec# homepage on Microsoft Research
The latter is probably not in widespread use (if it's used at all). There you get a non-nullable type by suffixing a type with !
(e.g. object!
or string![]!
).
Concerning C++, I'm sure someone clever will find plenty of arguments why C++ should not be mentioned here; being the complex, delicate language that it is, I'm sure that such reasons exist. My argument for mentioning C++ anyway is that apart from pointers (*
) (which can very well be 0
), C++ also has references (&
), which have to be initialized to something sensible. The main problem with references is, AFAIK, that they cannot be used for everything. There's probably certain situations, e.g. with memory management or smart pointers, where you cannot get completely rid of pointers.
Finally, you could always try to build your own non-nullable wrapper types, e.g. in C#. Probably like many others, Jon Skeet has done so and blogged about it. It's a workable solution, but such a NotNull<T>
type can feel a bit bulky since it's not truly integrated into the language, as with Spec#.
C++ has been mentioned. If I were designing a C++ library from scratch (something like Qt, say), I would in fact use pointers versus references at various points in the API to indicate whether the given parameter or return type can be null. Then seeing "*" and "->" wouldn't primarily be a visual indicator that you're dealing with a 'non-local' object -- it would be a reminder that you're dealing with something which might be null, and therefore you had better check whether it is.
Haskell's Maybe type is very simple but it's one of the best parts of the language. Doing things this way in C++ doesn't get you everything, but it gets you something and I think it would be quite helpful. (To briefly recap: if a function parameter is not allowed to be null, checking whether it is at runtime is a distant second-best option: if someone broke the rules and gave you a null, even if you check for it there's still nothing much reasonable you can do in response other than crashing the program with an assert. It's much, much better for the compiler to enforce that it can't be null, and disallow callers from passing one in in the first place.)
(Obviously, if you have a null pointer, you can still blindly dereference it, pass it in, and then get a crash anyways. But at least the fact that you're being forced to dereference (as opposed to passing in the pointer unchanged) is a strong cue that you should be checking for nullness. So it's better than nothing.)
It's true that Haskell doesn't have a special null value for every type, but if you work with actual pointers, which the FFI allows you to do, you can create null pointers.
精彩评论