[HN Gopher] The Brilliance of "nil" in Objective-C ___________________________________________________________________ The Brilliance of "nil" in Objective-C Author : ingve Score : 58 points Date : 2022-04-29 07:14 UTC (1 days ago) (HTM) web link (collindonnell.com) (TXT) w3m dump (collindonnell.com) | albertzeyer wrote: | But this is potentially dangerous. E.g.: | dict.setObjectForKey(@"b", @"a"); | assert(dict.containsObjectForKey[@"a"]); | | A bit tangential related to the article: | | In Python, I often found that it is helpful to also have a | separate "None"-like type despite None, which I called | NotSpecified. Often in functions you have sth like: | def func(opt: int = None): if opt is None: | opt = get_default_opt() ... | | This works fine when you now that opt should not be None here. | However, if None is allowed for opt and causes some other | specific behavior, I now do: def func(opt: | Optional[int] = NotSpecified): if opt is | NotSpecified: opt = get_default_opt() | ... | | This is also useful for dicts: | my_dict.get("foo", NotSpecified) | drewcoo wrote: | Very verbose, but at least it's not Java. | astrange wrote: | Verbose symbol names aren't a problem unless you need your | text editor window narrow. I find the problems are verbosity | in # of symbols and punctuation, which Java also had. | grahamlee wrote: | This nil-swallowing behaviour works really well with the "out | error parameter" pattern of Cocoa methods. In short, methods that | can fail have a signature like: | | - (id)thingThatSometimesWorks:(NSError *)error; | | You can chain these to your heart's content. The one that fails | sets the error and returns nil; following messages are sent to | nil which does nothing and a particular characteristic of doing | nothing is that it doesn't overwrite the error that was set. So | you still get to see the error when the workflow finishes. | | As an objective-C programmer, I also have a tool in my toolbox | which gives NSNull the same message-swallowing behaviour as nil. | | @implementation NSNull(GJLNilReceiver) | | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector | { return [super methodSignatureForSelector:aSelector] ?: | [NSMethodSignature signatureWithObjCTypes:"@@:"]; } | | - (void)forwardInvocation:(NSInvocation *)anInvocation { | [anInvocation invokeWithTarget:nil]; } | | @end | arcticbull wrote: | I mean this with love, this extension to NSNull gives me a | brain aneurism. If I had this in my codebase I wouldn't be able | to trust _anything_ in a collection. | [deleted] | arcticbull wrote: | As someone who started writing Objective-C on macOS in 2003, the | whole nil messaging syntax is pure hell compared to any modern | language that disavows nulls entirely (like Rust, and to a large | extent Swift). | | You can literally never trust anything - ever - is not nil | because `nonnull` is just a hint, and messaging nil will never | crash. It'll just return 0 (or {0} or 0.0 depending on context), | which is sometimes valid - and sometimes a horrible failure mode. | Failing silently like that is the worst option. | | As such you end up having a ton of boilerplate all over the place | checking the validity of input parameters to methods and | initializers for conditions which shouldn't be representable in | the first place - even when the method signature warrants that | it's _not_ representable, because it still won 't crash on ya. | | Let's say you have a chain of property accessors, a.b.c.d, the | where d is a float. They're all supposed to be nonnull. You get | 0.0. Is that valid? Shrugasaurus rex. Time for the ol' | NSParameterAssert(a.b.c != nil). | | [edit] Also, canonically, there's 5+1 'null' values: nil, Nil, | NULL, -[NSNull null] and 0 - plus nullptr in Objective-C++. | terhechte wrote: | The author seems to not know Swift very well, because this | _behaviour_ was ported over to Swift in a very nice, type safe | manner: | | In Swift, you can call methods on Optional values, and they | evaluate to null or the actual value. So the authors example: | | if (dict.containsObjectForKey[@"a"]) { ... } | | Would look like this: | | if dict?.contains("a") { ... } | | The `?` tells Swift to only call `contains` if `dict` is not | null. The return value of this call would then be another | optional. This second point is the improvement over Objective-C. | There, you would not know whether the return value of | `dict.containsObjectForKey` is a potential null value. This, in | turn, would crash the app if you'd try to use this return value | to, say index into an array or perform math on it. In Swift, the | compiler yells at you to remind you that this value can be null. | | So, really, Swift has the same benefits, but better. | happytoexplain wrote: | Note that it would actually be (also changing to an array to | make your abbreviation of the author's example make more | sense): | | `if arr?.contains("a") == true { ... }` | | or | | `if arr?.contains("a") ?? false { ... }` | | or | | `if (arr ?? []).contains("a") { ... }` | | or | | `if let arr= arr, arr.contains("a") { ... }` | | or | | `if arr!= nil, arr!.contains("a") { ... }` | | Optional<Bool> will not automatically unwrap where Bool is | expected (since the compiler would have to assume nil is | falsey, which is the kind of mushy shenanigans Swift explicitly | denies). | mojuba wrote: | Yes, but Objective-C is even more permissive than that. For | example, [object method] | | where method is supposed to return an int, will return 0 if | object is nil. This is of course a double-edged sword: it's too | easy to make a mistake in your logic, but also you can use it | to your advantage if you know what you are doing. | | The advantage really being just writing slightly less, i.e. | eliminating extra if's. All this, however, didn't save | Objective-C from being over all a very verbose language. At | least from my experience of rewriting from ObjC to Swift, the | latter can make your code half the size of the original and as | an added bonus will make it so much safer too. | | This is why Objective-C is objectively and hopelessly dead now. | terhechte wrote: | double fraction = 1.0 / [object method]; | | huzzah :) | Cloudef wrote: | In swift you do mymethod?.call(...) Where method can be nil | and it has the same effect. Similarly object?.something has | the same effect. Use exclamation mark instead of question | mark, to make swift runtime panic if the object or method is | nil. Obj-c is objectively worse in every way and easy way to | improve bugs in your code that go unnoticed for long time. | | Another cool thing is that you can use ?? At end of | expression that can return nil to decide what to do if nil is | returned (for example return something else). So swift | actually reduces the amount of ifs required. | mojuba wrote: | > In swift you do mymethod?.call(...) Where method can be | nil and it has the same effect. | | No, it doesn't. (You probably meant object?.mymethod()). If | mymethod returns int, then in Swift the result of this | expression is strictly Optional<Int>, whereas in | Objective-C it's Int defaulting to zero if object is nil. | The implications in terms of safety are significant. | Cloudef wrote: | I did mention object?.mymethod as well, method?.call is | needed if you want to call on nullable method itself in a | safe way. | setr wrote: | DefaultZero = Object?.mymethod() ?? 0; | | Optional-null + null-coalescing is strictly more flexible | than obj-c's behavior, while more sanely defined, with | very minimal syntax | threatripper wrote: | In Kotlin you can have a similar behavior but it's explicit which | I like more. | | var a : T; // cannot be null | | var b : T?; // can be null | | a.something; // OK | | b.something; // not possible because b could be null | | b!!.something; // assert that b is not null, then do the stuff | | b?.something; // if b is null, return null, else do the stuff | | So, each time you see the "?." you know to take extra care with | that statement. On the other hand it's very short and easy to | write chains of function calls that could return null which then | falls through. | HNHatesUsers wrote: | Cloudef wrote: | Not sure if it's some runtime mode in kotlin but b?.something | always threw panic for me when writing kotlin plugins for | flutter, so I ended up having to guard the expressions with ifs | anyways... | happytoexplain wrote: | Note that this is all exactly how Swift works (though it uses | one exclamation mark instead of two). The author is doing an | extremely limited comparison, and simply doesn't mention these | features. | moth-fuzz wrote: | My only contention with Optional<T> types as opposed to | NULL/nil/null is that in practice they're almost useless. An | Optional<T&> is a pointer, plain and simple. If NULL is one of | the expected values, it should be handled as expected. This leads | to a vast simplification of a number of algorithms and data | structures and no need for | `.unwrap().unwrap().excpect("unreachable")` in the cases you | can't do anything anyway. I've rarely seen cases where null | values were possible at the algorithmic level, and expected, and | _not_ handled, because, lazy programmers I guess? Honestly all I | can say for that particular case is that if you write half an | algorithm, expect it to fail half the time... | | On the other hand, if NULL is _not_ an expected value, then how | often do you _really_ find the logic error at the same spot that | the panic occurred? Not very often in my experience. We have | callstacks for a reason. You usually find the | NullPointerException at the bottom of the stacktrace or the | None.panic() after 100-something _other_ function calls in | significantly complex codebases. No, if a function expects a | value 100% of the time, and it was passed a NULL value, that 's | the _caller 's problem_. And Option<T> won't help you if the | user-facing calling code is still passing None or std::nullopt | for whatever misguided or erroneous reason they may be doing so. | | Certainly I agree there're benefits to encoding this kind of | behaviour into the type system, but I don't agree that it's | ultimately a solution to the '4 billion dollar mistake', since | mistakes ultimately _still happen_. You just swap NULL for None | and you still have a bug in your logic, just no longer a bug in | the types. It 's kicking the can. | b3orn wrote: | In C a pointer is a version of the optional type, implementing | an explicit version of it is therefore pointless. In other | languages you could enforce that a pointer can't be null or | even forbid it to be uninitialized therefore you need to have | an optional type for the cases where you want to allow null, so | if you get a pointer you can trust it a little more. | nicky0 wrote: | As an ObjC programmer, I concur. | yakshaving_jgt wrote: | Could there be a way to eliminate the problem of null values | floating through your system? | | Maybe. | happytoexplain wrote: | I see what you did there. And Swift, the very language the | author is comparing to, _has this functionality_. | brundolf wrote: | Optional-chaining in other languages gives you the same | ergonomics without the funky semantics | happytoexplain wrote: | There is this weird disconnect going on in this article and | therefore the HN comments. The author's first sentence is, "I | don't code much in Objective-C these days but one thing I miss | about it compared to Swift is how nil works." He's not just | saying "this part of Obj-C's nil is good", he's saying, "this | part is good _even compared to Swift_ ", which makes no sense | because his "before" example in the before-and-after is _Obj-C, | and not Swift_. The HN comments then proceed to enumerate the | advantages of modern handlings of null, _of which Swift is a | prime example_. The author 's lack of clarity is understandably | causing this. | potatolicious wrote: | > _" This is probably the least-Swift thing ever, but, if you're | used to coding in this style, it's fantastic."_ | | As someone who has been building on Objective-C for >10 years | now... it's horrible. Sure, there's less compiler-yelling-at-you- | itis, but the code sample the author gives is almost certainly | the _single biggest source of bugs_ in Objective-C codebases. | | The inclusion of Optionals in Swift is specifically to, in one | fell swoop, eliminate the single largest source of bugs in Obj-C | codebases. | | I get the appeal of getting the compiler to be less strict and | yell at you less for just hacking around - but something you use | to write production code _needs_ be very liberal about error | detection and yelling at you. | | Not to mention the same lack of verbosity is supported in Swift | _with_ all of the safeties of optionals: | | `if dict?.keys.contains("a")` | | Which is both semantically and typographically terser, and makes | the intention of the code clear (i.e., "dict" is expected to | sometimes be nil) | rob74 wrote: | I'm not fluent in either Objective-C or Swift, maybe that's the | reason why the brilliance of having four types of "null"/"nil" | escapes me? Ok, it probably works well if you're used to it, but | it feels more like a kludge than anything else... | Someone wrote: | FTA: the concept of "nothing" in Objective-C is kind of a mess, | since there's four different versions of it | | The article is about nil, not about that. | | And yes, part of it is a kludge. | | I think that's only NSNull, and that, technically, isn't a | language issue, but a library one. As | https://nshipster.com/nil/ (which explains all of this) says: | | _NSNull is used throughout Foundation and other frameworks to | skirt around the limitations of collections like NSArray and | NSDictionary not being able to contain nil values. You can | think of NSNull as effectively boxing the NULL or nil value so | that it can be used in collections._ | | I think a better implementation would use a private singleton | _NSNull_ inside the container as a sentinel, so that users of | the class could store _nil_ values. | dspillett wrote: | _> the concept of "nothing" in Objective-C is kind of a mess, | since there's four different versions of it_ | | Sounds like Visual Basic of old. We used to refer to the four | constants of the apocalypse: null, nothing, missing, and | empty. | throwaway4good wrote: | It tells the tragic story of a language designer fighting to | avoid null while building on/interfacing with an existing | platform that has null. | zaphirplane wrote: | Kotlin kind of got away with it, in the sense that I (massive | statically) don't hear people complain about null on the Java | interoperability calls | throwaway4good wrote: | I think Kotlin took a more pragmatic approach - basically | introducing ? and ! to signify that something could be null | or something isn't null even though the compiler cannot see | it. | DonHopkins wrote: | "My favorite is always the billion dollar mistake of having | null in the language. And since JavaScript has both null and | undefined, it's the two billion dollar mistake." -Anders | Hejlsberg | | "It is by far the most problematic part of language design. | And it's a single value that -- ha ha ha ha -- that if only | that wasn't there, imagine all the problems we wouldn't have, | right? If type systems were designed that way. And some type | systems are, and some type systems are getting there, but | boy, trying to retrofit that on top of a type system that has | null in the first place is quite an undertaking." -Anders | Hejlsberg | b3morales wrote: | `NSArray` is not `nil` terminated; the argument lists to some of | its variadic initializers are, but the array itself simply does | not contain `nil`. | | The literal syntax `@[ ... ]` gets transformed into an | initializer call, which _throws an exception_ if it is passed a | `nil` value. And I can 't remember, but I would expect the | compiler to at least warn, if not error, if you put a literal | `nil` in there. | arcticbull wrote: | They're just referring to -[NSArray arrayWithObjects:...] which | takes a va_list. Same with -[NSDictionary | dictionaryWithObjectsAndKeys:...] | | These both long predate the array and dictionary literal | syntax. | teakettle42 wrote: | It's only "brilliant" if you think data corrupting bugs that fail | to run expected code and silently spread "nil" throughout your | application's data model are brilliant. | | If something can be nil, handle the case. There's nothing | brilliant about objective-C's nil kludge. | larsrc wrote: | I notice that all the comments that agree with this one point | out specific dangers, while those disagreeing basically say | "Eh, bugs happen". | | Yes, you need to know what you're doing in any language, the | difference is whether those things are due to the language or | the problem you're actually trying to solve. The more things in | the language you have to keep in mind, the less of your domain | problems you have room for. | nicky0 wrote: | Like any language feature, you have to know what you are doing. | teakettle42 wrote: | It's a foot-gun, and a significant source of bugs in just | about all software of any complexity written in ObjC. | nicky0 wrote: | Writing code is a significant source of bugs in just about | all software of any complexity. | yakshaving_jgt wrote: | This might help you. | | http://www.paulgraham.com/avg.html | teakettle42 wrote: | Good news, then; languages with an Optional<T> type don't | require you to write _more_ potentially buggy code to | check for (or forget to check for) nil. | nicky0 wrote: | Meh, different languages give you different tools, | different degrees of nannying vs freedom. Some may give | you a foot gun, but at least you have a gun and you | aren't required to point it at your foot. | teakettle42 wrote: | I prefer languages that make it easy for me to focus on | solving novel problems, rather than wasting my mental | energy just trying to not blow my foot off with a rusty | musket. | pilif wrote: | Bugs exist. No question. | | What does bother me is where you detect the bug. | | Is it at compile-time? | | Is it at run-time through a crash? | | or is it months later after you notice that many of your | users seem to have January 1st 1970 as their birthday | with no way of recovering the lost data. | | But hey - at least stuff kept running. Right? | nicky0 wrote: | If you write bad code then your code will be bad. | pdpi wrote: | That's a pretty unhelpful attitude. | | This year has already seen CVE-2022-25636 (heap out-of- | bounds write), CVE-2022-27666 (buffer overflow), and | CVE-2922-0847 (uninitialised memory). Three | vulnerabilities in the Linux kernel, in pretty important | (and therefore, presumably, closely scrutinised) bits of | the code base. And this is the Linux kernel, arguably the | most important open source project in the world, worked | on by some of the most skilled developers out there. | | Everybody writes bad code, and everybody misses bad code | on review, even in the really important bits, even when | they're well-known bug classes. Criticising a language | for leaving these foot guns lying about is perfectly | reasonable, and it's important we talk about avoiding | those languages wherever possible. | threatripper wrote: | Programming languages are like shepherds. They offer a | compromise between the freedom to do stuff efficiently and | the danger of unwanted behavior. You cannot maximize both at | the same time. You can, however, minimize both by making bad | design decisions. Footguns are more and more considered a | fault in the language design and less the responsibility of | the programmer. | smitty1e wrote: | Sometimes the serious training wheels on the system make | sense, especially when navigating the learning curve. | | Other times, the need to have direct hardware access from | the BIOS on up cannot be avoided. | | The bugaboo is the quest for the One True System that is | all things to all people. | | Emacs is as close as we get to that Nirvana. | afc wrote: | Well said. This feels terrible: just silently ignore logical | errors (where some code expects but doesn't receive a fully | constructed object) and ... keep going. | | I'd rather take a crash (that way I'll know soon that I have a | bug and it'll be easy to fix) or, even better, a compiler/type | error ("code is expecting a fully constructed object but | doesn't always receive one"). | | In C++ I recently started using a NonNull<> templated type | (with specializations for std::unique_ptr<> and | std::shared_ptr<>): | https://github.com/alefore/edge/blob/af1192a70646f662539bfe6... | | It's slightly verbose, but has allowed me to delete a ton of | CHECK(x != nullptr) statements (since the type will already | carry the information that x can't be null) and this use of | types has helped me detect a few mismatches (where e.g. the | consumer had unnecessary complexity to deal with null but the | caller always emitted fully constructed types, or the, much | worse, symmetric case). | grenoire wrote: | After starting using Option<T>, no implementation of null is | 'brilliant' any more. Not that any one of them ever was. | [deleted] | melling wrote: | NSArray *array = @[@"a", @"b", nil, @"c"]; // array = [@"a", | @"b"] | | I sort of prefer Swift: | | let array = ["a","b"] | happytoexplain wrote: | And the Swift version is even automatically typed as containing | Strings using generics! | astrange wrote: | Though this isn't precisely typed: let dict = | ["a":[1],"b":["c"]] print(type(of:dict["a"])) | = Optional<Array<Any>> | drewcoo wrote: | ObjC is similar to other things of the time. There used to be | wonderful chaining nil/null check semantics. And duck-typed | languages only made that better. | | if foo || bar || baz || you_do_not_even_know_what_comes_next | break | | or: | | retval = foo || bar || baz || you_do_not_even_know_what_comes | return retval | | &c | | I don't understand why it's notable except that it's not really | JS or Pythonic . . . | jamespwilliams wrote: | > Mostly used to recodesent null | | Is this line the victim of an overeager s/pre/code/g I wonder... | cassepipe wrote: | Oh thanks my mind just hopped over it without even trying. It's | nice to see it was no knowledge gap but an well known word | Valodim wrote: | Haha, indeed. I can't think of an explanation for this one | though: | | > switdhing to Swift "feels like a Jedi who's trying to switdh | from a lightsaber to a blaster," | adrusi wrote: | I think it's because the author thought the tags you use in | tables are <tr> and <tc> (table-row and table-column) and | then did a s/tc/td/g. | kzrdude wrote: | I guess today the author will learn about word boundaries | in regexp, then. | CraigJPerry wrote: | Option type was my favourite solution to this until i was | introduced to nil punning. I still haven't found a more pragmatic | approach. | | Some languages allow you to say "this can never be null", and in | general, i find that really helpful and ergonomic but there's | cases where it's not practical and you have to permit null and at | that point, you've sunk the boat. | | Strict non-nullable with Option types has been moved to 2nd place | solution since i experienced nil punning. | | Treat nil as a value, like in this article's example of the array | being terminated by nil. Don't make it an exception to compare | against nil. But don't ascribe any other meaning to the value - | that means no matter the context, nil can semantically fit your | use case. | | It's better than option primarily because it means a lot less | code. You delete all "is this value present?" type code from your | codebase because encountering nil in your happy path is rendered | harmless. | | Very nice. | HNHatesUsers wrote: | happytoexplain wrote: | >Don't make it an exception to compare against nil. But don't | ascribe any other meaning to the value | | If comparing against nil does not fail, you are ascribing the | value of "false" to it. If it works in every use case, you are | ascribing an arbitrary value to it in every use case. | divingdragon wrote: | I would rather have my program hard crash on unexpected null | than to have it silently ignore potential logic errors. I have | never used a language with nil punning, but to me Option<T> | still feels to be the best approach. | CraigJPerry wrote: | It's a small example and won't claim it's life changing but | if you get a spare hour some time, i'd definitely vote for | broadening experience horizons and playing with an example. | | Specifically to your point about the hard crash, I'm | interpreting this as meaning "i want the programmer to be | told they're mistaken", if I've got that right, you'll be | pleasantly surprised. | happytoexplain wrote: | >encountering nil in your happy path is rendered harmless. | | Only if by "harmless", you mean "creates a bug later rather | than a crash now", as compared to traditional null handling. | But Optional has neither problem. | | >It's better than option primarily because it means a lot less | code. You delete all "is this value present?" type code from | your codebase | | "It's better because it's less code" is a pretty big red flag | if that's the only advantage. Especially when "less code" means | `foo.bar` instead of `foo?.bar`, or `if foo.bar` instead of `if | foo?.bar == true` (and even in the second example, if you're | touching `foo` multiple times, you can unwrap it one time with | `if let` instead). | feanaro wrote: | This sounds like an invitation for logic bugs and corruption. | | > It's better than option primarily because it means a lot less | code. | | Option doesn't need any extra code for checking the presence of | a value when it's done right. In Rust, you just place a ? sigil | at the end of the option expression. In Haskell, you just use | the do syntax where val <- optional will assign the value to | `val` if `optional` is some value rather that none, and it will | exit the do block otherwise. | CraigJPerry wrote: | >> Option doesn't need any extra code | | Ahh, those are both logic errors in your code. You do need | the extra code to fix your errors here: | | >> In Rust, you just place a ? sigil at the end | | That changes the behaviour to bubble up that there was no | value, the caller has been delegated responsibility and | further processing has stopped at this point. | | Instead to accurately implement the required behaviour and | replicate the nil punning logic, you'd need ? to behave in | such a way that it inserts a suitable "empty" value for | whatever type the option is guarding. So if this was an | optional collection for example, the ? would need to figure | out what type of collection then how to get an empty one to | provide here (and you'd probably want that to be a singleton | immutable collection, otherwise you'd be risking introducing | logic bugs). | | In practice, you'd instead write code to provide that empty | collection at the call site. At this point you've rendered | the Option wrapper pointless though. | | It's the exact same story for your Haskell example. | | You've given a different behaviour which is of course, a | logic bug. | feanaro wrote: | > to behave in such a way that it inserts a suitable | "empty" value for whatever type the option is guarding. | | Some types have no suitable "empty" value. It is dangerous | to assume you can always find such a value. | | In essence, when you're expecting that such a value exists, | you're implicitly relying on your type being a suitable | monoid. That's fine when you're expecting this and when it | is actually a monoid, but not all types are. | | > In practice, you'd instead write code to provide that | empty collection at the call site. | | Not all types can be modelled as collections. This is the | same problem as above. | | What I'd actually do is either: | | 1. Fail the parent operation by again using `?`, _or_ 2. | Provide a suitable default /"empty" type, if it is possible | to continue. | | It's very important that such a decision is explicit on a | case-by-case basis, and this is what `?` allows you to | easily do without much fluff or noise. | | > At this point you've rendered the Option wrapper | pointless though. | | How was it useless? It allowed us to _safely_ write a | procedure as if the value existed and otherwise cheaply | signal failure to the caller. The caller can then repeat | the same process. | | What's important is that the procedure stands on its own | and is safe out of the box. It clearly says that it can | fail (by returning an option) and we know exactly when it | has failed and when it has not. | | > You've given a different behaviour which is of course, a | logic bug. | | I don't see where the bug is? | nybble41 wrote: | > Some types have no suitable "empty" value. It is | dangerous to assume you can always find such a value. | | Indeed. In Rust you have Option::unwrap_or_default() for | this, which is only available if the underlying type | implements the Default trait. It's unfortunately more | verbose than `?` but it gives the behavior the GP wants, | without making unsafe assumptions like 0 always being a | safe default for any integer result. | CraigJPerry wrote: | >> I don't see where the bug is? | | That appears to be a willful decision on your part i | think :-) A'dieu and all the best | Dylan16807 wrote: | You are acting wrong here, fyi. | feanaro wrote: | I was trying to be deliberately obtuse, it was a good | faith question. I guess I just misunderstood what you | were trying to say then. | | But no hard feelings and all the best to you too. | feanaro wrote: | *wasn't :D Yes, yes, cue the Freudian jokes. | rocqua wrote: | Most 'optional' types have a 'map' function that allows you to | apply a function to it, if it isn't None. That seems like a | decent middle ground, though maybe less useful when writing a | few short functions. | sillysaurusx wrote: | I found myself using nil in C++ everywhere. | typedef std::optional maybe; using nil = std::nullopt; | | Not quite the right definition, but I'm on an iPad. | | When you combine those with an "either" function which returns | either a maybe<T> else a default T, you end up with a nice way of | having default args in C++. Everything defaults to nil, then you | set it to either(foo, defaultFoo()) in the body of the function. | | Of course, C++ already has what it calls default arguments. But | lisp has corrupted me. The defaults can't be a function call, and | the values can't refer to other arguments, so they feel useless. | | Heckin C++. I feel like programmers 80 years from now will still | be yearning for an escape, whereas C++100 will still be the | dominant systems programming language. Golang was a nice attempt, | and so is Rust, but empirically the evidence is that (at least in | AI) you can't escape C++. The best thing to do is to just dive | in. | pjmlp wrote: | Not only in AI, anything GPGPU related (just look how many ISO | C++ folks are on the payroll of the big three), and all major | language and GUI runtimes. | dataflow wrote: | using nil = std::nullopt; | | I don't think that compiles? The right hand side is a value, | not a type. | sillysaurusx wrote: | constexpr auto nil = std::nullopt. I was on an iPad. :) | [deleted] | happytoexplain wrote: | This would be cool as a comparison to traditional null handling | (i.e. crashing). But the comparison to Swift is ridiculous. This | begs for overlooked nulls to silently create logic bugs. | | Also, the author admits there are two kinds of null in Swift, as | compared to four in Obj-C. Is the second kind he's referring to | `NSNull`? Doesn't that only exist to bridge with Obj-C APIs that | haven't been updated yet? That's pretty misleading, if so. | astrange wrote: | NSNull is used to store nulls in NSDictionaries when you really | want them to, since they don't allow storing `nil`. An example | being decoding JSON, which does allow it. | happytoexplain wrote: | I was thinking that counted as one of the "Obj-C APIs that | haven't been updated yet", but I guess it's not actually | going away since it's qualitatively different from Swift's | Dictionary (being a reference type instead of a value type). | SemanticStrengh wrote: | people should really try non-nullable types in typescript and | kotlin, in addition to smart-casting. Then they would realize | optionals are niche and cumbersome. | planb wrote: | if (dict != nil && dict.containsObjectForKey[@"a"]) { ... } | Can become this: if (dict.containsObjectForKey[@"a"]) { | ... } This is probably the least-Swift thing ever, but, | if you're used to coding in this style, it's fantastic | | Why is this better than a language that can guarantee that dict | is not null? | throwaway4good wrote: | Null is simple, straight-forward solution to the problem of | missing values. | happytoexplain wrote: | Simple and straightforward at write-time. A nightmare at run- | time, modification-time, and debug-time. | jon889 wrote: | I don't see how it's that different to Swift? All that would be | needed is a ? after `dict` and a direct comparison to true. | | dict.containsObjectForKey[@"a"] | | Becomes roughly | | dict?.containsObjectForKey("a") == true ___________________________________________________________________ (page generated 2022-04-30 23:01 UTC)