4 ms·
> "4. Objects must always be initialized to a valid state. Not doing so is a compile-time error." So this essentially means there can never be a NULL object as
by gitgud 6y ago
> "4. Objects must always be initialized to a valid state. Not doing so is a compile-time error."
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?
- bollu 6y agoYou use sum types / optional<T> / Maybe / Option<T> / whatever your language calls them. Such a value can have two states: 'None', where it calls no value, and 'Some(t)',where it carries a value 't'. 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'.
- mbogun 6y agoThree is a great article about option tyle in the context of F# here: https://fsharpforfunandprofit.com/posts/the-option-type/ https://fsharpforfunandprofit.com/posts/the-option-type/
- danielinoa 6y agoNot quite. There can be a null value if the object type allows for it, think Optional<T>. What you can't have is an undefined object, as you could in Javascript for example.
- Smaug123 6y agoIf you forget everything you ever learned about programming, and then I ask you for an integer, you're never in a million years going to reply "null"; the existence of "null" is something you had to learn. A language could simply not have a concept of "null". Even an OO language could have no concept of "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.
- rightbyte 6y agoNull is just an invalid pointer target that you can verify easely. It is just abi convenience and removes the need for a flag. 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.
- lmm 6y agoAt ABI level you might want to use all-bits-zero to represent something other than a valid instance of your type, sure; that's how Rust's option type works. But making your language semantics say that that has to be allowed everywhere a valid instance could be passed is a mistake; for most functions it's an error to call that function without passing valid argument values, so it's better if the language has a way of enforcing that rather than forcing you to write it out by hand every time.
- rightbyte 6y agoYe some "always valid kinda a pointer"-type is nice. I wish there was some standardized compiler support for that in C, like MS kinda silly empty macros in function signatures to annotate input and output pointers etc.
- Smaug123 6y agoI don't really view that as being relevant. F# compiles `Option.None` down to a null under the covers; what matters is that the type system is null-aware.
- nullsense 6y ago>So this essentially means there can never be a NULL object assigned to anything... how is this possible? I believe Crystal doesn't let anything be null either. It's quite interesting.
- tremon 6y agoNot only that, it implies that there must be a valid null-state for every type, which is also a bad idea.
- jackcviers3 6y agoNo, it implies that there must be a generic two-valued datatype to wrap every state: 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.
- sullyj3 6y agoI'm not entirely sure what you're getting at here. Are you saying the author is arguing a call like 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.
- learc83 6y ago> 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.
- lmm 6y agoNo it doesn't; the whole point is to avoid every type having a null state. You only define null states for those types for which it's semantically valid. For most types, you just don't have a null state and don't allow them to be constructed in an invalid state.
- deergomoo 6y agoYou can still have “the absence of a value”, but it’s codified in the type system. Most languages with this feature implement an Optional or Maybe wrapper type, that is either Some<T> or None (i.e. no value). 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).
- dudul 6y agoYou can represent the possible absence of value with your type system. You can use a Maybe/Option type for example.
- masklinn 6y ago> So this essentially means there can never be a NULL object assigned to anything… 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&edition=2018&gist=560e1ea6c9b767ec9ca6a5f913b955aa 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&edition=2018&gist=4dede06ad070f65e525dfd16c423223f 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&edition=2018&gist=a741aab6d7000320c1c5fe736fbb2ef3 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&edition=2018&gist=ba0aa527cf597e0abbc250dbabb4b3c3 https://play.rust-lang.org/?version=stable&mode=debug&editio...