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