So this essentially means there can never be a NULL object assigned to anything... how is this possible? is it avoided by throwing exceptions instead of returning NULL?
So this essentially means there can never be a NULL object assigned to anything... how is this possible? is it avoided by throwing exceptions instead of returning NULL?
In practice, it's useful to be able to answer "I'm sorry, Dave, I can't do that". Some languages do that by silently and implicitly allowing this special value "null" in a return type. Some languages allow you to throw an exception (again without ever having declared that this is something you might do). But some languages take one of those approaches and make it explicit: in the two cases, you have the "option"/"maybe" type, or checked exceptions. Either of these explicit methods let a function declare that it may fail, and moreover the language forces you to handle the possibility of failure in any function which may fail.
The "reasonability" comes from the fact that the language forces you to be explicit about the possibility of failure, rather than implicitly allowing all functions to fail.
On the other hand I unironicly write goto:s and use globals as much as a please (think GNU Bison/Flex) so who I am to judge.
The difference is, because you’re now receiving a different type, you are forced to deal with the case where there’s no value.
Rather than your function returning a Customer that could sometimes be null—and if it’s only null infrequently, you’re much more likely to forget to handle it—it instead returns Optional<Customer>. To operate on that Customer object, you are forced to unwrap the Optional by checking whether the underlying value is there.
How well you handle the None case is up to you, but the compiler won’t let you forget (generally anyway; for example Swift allows you to force-unwrap optionals with a crash on the None case, but eh).
For example, a division function could return (1) 'None' for 'div(20, 0)' because division-by-zero is undefined, and return (2) 'Some 10' for 'div(20, 2)' because 20/2 is defined and is equal to '10'.
Just t | None
You can have your null case, and avoid the null-pointer exception, default keyword, chained ? null-check hellscape in which we currently reside.
new Customer()
should return something valid? I think the FP way would be to disallow constructor overloads with no arguments, as really, there's no reasonable thing to be done here. If you don't have all the necessary information to initialize a customer yet, you don't get to initialize one, and you pass around a partially applied constructor function instead.ie, The argument is that (in pseudocode):
Customer(name = "Jimmy", address = null)
is not really a customer if address is a necessary piece of information, so we should distinguish at the type level by passing around a function that takes the address and returns the correctly initialized Customer like so \addr -> Customer(name = "Jimmy", address = addr)
Admittedly, this prevents you from accessing name until you provide an address, but I don't think there are many valid use cases where you want to access data before the Customer value is corrrectly initialized. I'd consider this a code smell to be refactored.If you really needed to do that you could just define a partial customer with no address field and pass are that around.
I believe Crystal doesn't let anything be null either. It's quite interesting.
No, it means that you can't leave bits off yet claim to have a valid object, either the object is fully initialised or it's illegal. This is mostly a concern in older languages like C where you can reserve space for a struct then not fill in anything.
In some more recent languages (Java, Go, C# historically though that's changing) everything is zeroed by default so you avoid the "random garbage" situation, but it means you need to assume anything can be zero even if that's not intentional, and depending on the specifics you might "double write" each location even when that's not actually necessary.
In the more functional slate of languages, you simply can't let any members off. There might be a concept of "default value" but that tends to be opt-in, the baseline is that if you define a type you must fill in every member, explicitly, before the compiler will accept it.
Initialising items to null is orthogonal.
Now since the rest is predicated on a misunderstanding I've a version of each for "incomplete initialisation" and a version for "null".
> how is [forcing complete initialisation] possible?
The compiler just requires that every member of the structure be specified.
> how is [things never being NULL] possible?
Don't make NULL an implicit member of every type.
> is [incomplete initialisation] avoided by throwing exceptions instead of returning NULL?
it's avoided by the compiler refusing to compile your code if you leave off members.
> is [things never being null] avoided by throwing exceptions instead of returning NULL?
It's avoided by NULL being a member of a completely different type than anything else, whether that type is a special case or not e.g. in Rust "null" is one member of the Option "wrapper type" so to indicate that, say, a user is optional you write `Option<User>`, then that can either be `None` (no user, ~ null) or `Some(user)`.
If you just type something as `User` then it's necessarily a valid user and nothing else, because there is no null value you could put into it, you'd get a type error.
* Rust playground which fails to compile because one of the struct members was left off: https://play.rust-lang.org/?version=stable&mode=debug&editio...
* Rust playground which fails to compile because trying to put a null (None) in a non-nullable field: https://play.rust-lang.org/?version=stable&mode=debug&editio...
* Version of the previous with an optional (nullable) member: https://play.rust-lang.org/?version=stable&mode=debug&editio...
* Version with opt-in default (through Rust's Default trait, the default derivation of the Default trait will recursively default each field, which usually means some sort of zeroing): https://play.rust-lang.org/?version=stable&mode=debug&editio...