[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)