I disagree. It is the job of the language to help you with this problem. Things in Java like the @Nullable annotation, or @NotNull are stupid hacks that exist solely because of null. Much in the same way that the argument can be made that “language design patterns” are flaws in programming languages, any feature in a programming language that makes it easy to do it the wrong way is a flaw.
There are two ways of looking at functions that aren’t pre-annotated as @NotNull or @Nullable. Either you can always assume they’re not null, which means that you’re leaving yourself at risk to NullPointerExceptions, or you can always assume they’re nullable, and always write the boilerplate if (arg == null …). This means that the only sensible thing to do is to either annotate every single function, or set one as the standard, and annotate only the other. However, what if you forget to annotate, and there isn’t a static analysis tool like findbugs to tell you that you incorrectly annotated a function? Then you’re stuck back in a situation where you can end up with surprise NullPointerExceptions.
Semantically, null/Value is not equivalent to Maybe, because the default assumption for Value should be that it is a Value! If you look at a type, and see that it is a List, it is a shock to see that it is instead a null. In languages where you need to explicitly declare that you might be passing a (haskellian) Nothing back, you will never be surprised to see it.
Perhaps it would be more clear to say that Nulls in and of themselves are not gotcha language features, but not forcing a function to signal in some way that it could be passing back a null is a gotcha language feature. One way of dealing with this instead of having a separate Maybe type would be to have a keyword Nullable that you could use to annotate argument types or return types.
Well, I’d say that nullcould be Maybe, but not always. For example, in Ruby, every method returns something, so in this case, for example, you get nil:
def foo
end
Or this one when @correct is falsey
def correct?
true if @correct
end
To me, Maybe is a more explicit ‘result of a computation which may fail’. Neither of these computations are failures.
Hoare is correct in saying that he is incorrect. Language inclusion of null is a mistake. null isn’t a value. If a function is to return a representation of an object, it should return that representation, or it should return an error that causes all further computation to cease. That’s the Maybe. null (by definition, really) serves no valid purpose.
One other language solution to use instead of null is to allow tagged unions. There you can create a sum type where all values returned from a function are meaningful. In a language sketch a friend and I wrote, we stressed not having null in our C-domain language. It is essentially rust without a process or memory model.
Starting with an Error type that a function can return. It is a generic type that can specify the type on success, T, and on error it will return a string (an error message).
The error can be checked via a macro that will expand in-place. This macro will check the subtype of the returned value of the Lexer.begin method. If it is an Error.Error subtype, it will cause the function to return the same error, thus stopping the computation process. This mimics the spirit of Maybe where error values are not the same type as a valid value, and computational progress is halted.
You can mimic similar behavior with the C preprocessor and returning a union struct. The point, however, remains that these languages contain null for no valid reason. All values returned from a function should be meaningful and well-defined so that behaviors are well-defined. In languages where null is a possibility from any function that returns either a pointer or a reference, especially when implementations are a black-box, such functions are immediate suspect for having such a flaw. null therefore yields nothing but a lack of certainty and perhaps spontaneous chaos. :)
The C♯ Maybe type implemented in the example already has a limited precursor in the standard library, the class Nullable. Nullable<T> instances have these properties and methods, which include bool HasValue, T Value, and T GetValueOrDefault. There is even syntactic sugar for Nullable types – String?, with a question mark, is short for the type Nullable<String>.
The Nullable structure supports using only a value type as a nullable type because reference types are nullable by design.
In other words, C♯ only provides the Nullable class to allow you to represent value types such as Int32 as null, because Int32 someNum = null doesn’t work. It thinks reference types such as String don’t need it, since you can already set String someString = null. So it is just an accident that Nullable looks like it’s solving the same problem as Maybe.
[Comment removed by author]
I disagree. It is the job of the language to help you with this problem. Things in Java like the @Nullable annotation, or @NotNull are stupid hacks that exist solely because of null. Much in the same way that the argument can be made that “language design patterns” are flaws in programming languages, any feature in a programming language that makes it easy to do it the wrong way is a flaw.
There are two ways of looking at functions that aren’t pre-annotated as @NotNull or @Nullable. Either you can always assume they’re not null, which means that you’re leaving yourself at risk to NullPointerExceptions, or you can always assume they’re nullable, and always write the boilerplate if (arg == null …). This means that the only sensible thing to do is to either annotate every single function, or set one as the standard, and annotate only the other. However, what if you forget to annotate, and there isn’t a static analysis tool like findbugs to tell you that you incorrectly annotated a function? Then you’re stuck back in a situation where you can end up with surprise NullPointerExceptions.
Semantically, null/Value is not equivalent to Maybe, because the default assumption for Value should be that it is a Value! If you look at a type, and see that it is a List, it is a shock to see that it is instead a null. In languages where you need to explicitly declare that you might be passing a (haskellian) Nothing back, you will never be surprised to see it.
Perhaps it would be more clear to say that Nulls in and of themselves are not gotcha language features, but not forcing a function to signal in some way that it could be passing back a null is a gotcha language feature. One way of dealing with this instead of having a separate Maybe type would be to have a keyword Nullable that you could use to annotate argument types or return types.
Well, I’d say that
nullcould beMaybe, but not always. For example, in Ruby, every method returns something, so in this case, for example, you getnil:def foo endOr this one when @correct is falsey
def correct? true if @correct endTo me,
Maybeis a more explicit ‘result of a computation which may fail’. Neither of these computations are failures.[Comment removed by author]
Ugh, lobsters stripped out the whitespace, my first example is your second example.
Hoare is correct in saying that he is incorrect. Language inclusion of null is a mistake. null isn’t a value. If a function is to return a representation of an object, it should return that representation, or it should return an error that causes all further computation to cease. That’s the Maybe. null (by definition, really) serves no valid purpose.
One other language solution to use instead of null is to allow tagged unions. There you can create a sum type where all values returned from a function are meaningful. In a language sketch a friend and I wrote, we stressed not having null in our C-domain language. It is essentially rust without a process or memory model.
Starting with an Error type that a function can return. It is a generic type that can specify the type on success, T, and on error it will return a string (an error message).
A function can return such an Error.
The error can be checked via a macro that will expand in-place. This macro will check the subtype of the returned value of the Lexer.begin method. If it is an Error.Error subtype, it will cause the function to return the same error, thus stopping the computation process. This mimics the spirit of Maybe where error values are not the same type as a valid value, and computational progress is halted.
You can mimic similar behavior with the C preprocessor and returning a union struct. The point, however, remains that these languages contain null for no valid reason. All values returned from a function should be meaningful and well-defined so that behaviors are well-defined. In languages where null is a possibility from any function that returns either a pointer or a reference, especially when implementations are a black-box, such functions are immediate suspect for having such a flaw. null therefore yields nothing but a lack of certainty and perhaps spontaneous chaos. :)
The C♯
Maybetype implemented in the example already has a limited precursor in the standard library, the classNullable.Nullable<T>instances have these properties and methods, which includebool HasValue,T Value, andT GetValueOrDefault. There is even syntactic sugar forNullabletypes –String?, with a question mark, is short for the typeNullable<String>.However,
Nullableis not a full substitute forMaybe. The docs for “Nullable Generic Structure” explain:In other words, C♯ only provides the
Nullableclass to allow you to represent value types such asInt32asnull, becauseInt32 someNum = nulldoesn’t work. It thinks reference types such asStringdon’t need it, since you can already setString someString = null. So it is just an accident thatNullablelooks like it’s solving the same problem asMaybe.