Propagating Optionals


(Trans) #1

As I've been learning Swift recently, one aspect of the language
jumped out at me with a "code smell". Namely, the way Optionals are
handled. For starters, just consider how long this chapter is:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
That's pretty beefy for something on the surface is pretty simple.

More concretely, consider the example given:

    class Person {
        var residence: Residence?
    }

    class Residence {
        var numberOfRooms = 1
    }

    let john = Person()

    let roomCount = john.residence.numberOfRooms

    // error: value of optional type 'Residence?' not unwrapped; did
you mean to use '!' or '?'?

As general rule of thumb, whenever I get an error and the system tells
me what I probably meant, that is a pretty good sign the system isn't
doing all it can for me and making me jump through an unnecessary
hoop.

Basically "john.residence.numberOfRooms" is a completely wasted
expression -- it's meaningless. You have to put a `?` or `!` in there
to get anything useful. I can't see any good reason for that.
"john.residence.numberOfRooms" could just behave one way or the other,
either as if the `?` were there, or the `!`. And of the two, the
obvious choice is `?` because... I already told the program it the was
optional in "var residence: Residence?". I said it was optional, and
yep I meant that. (Reminds me of the old retort "did I stutter?")
Thus, if I try to assign it to something else it too should be
optional. If I want it to be otherwise I'd add the `!`.

Making this change would just simplify a whole mess of code and about
half that chapter would all but vanish.

In addition, seeing that `!` acts a short-circuit to error, it would
be nice to have something equivalent for fallback value. We can't use
`??` b/c it doesn't chain, though maybe it could be made to? And I'd
rather not reuse `?.` here (for reasons I can explain later). Maybe
`:` is a good choice? In any case, exact syntax aside,

    let homelessShelter = Residence()
    let roomCount = john.residence:homelessShelter.numberOfRooms

Now, roomCount will not be optional, because there is a guaranteed value.

I think simplifying Optionals this way would be a good fit for Swift,
making this part of the language a whole lot cleaner and clearer.


(William Sumner) #2

As I've been learning Swift recently, one aspect of the language
jumped out at me with a "code smell". Namely, the way Optionals are
handled. For starters, just consider how long this chapter is:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
That's pretty beefy for something on the surface is pretty simple.

More concretely, consider the example given:

  class Person {
      var residence: Residence?
  }

  class Residence {
      var numberOfRooms = 1
  }

  let john = Person()

  let roomCount = john.residence.numberOfRooms

  // error: value of optional type 'Residence?' not unwrapped; did
you mean to use '!' or '?'?

As general rule of thumb, whenever I get an error and the system tells
me what I probably meant, that is a pretty good sign the system isn't
doing all it can for me and making me jump through an unnecessary
hoop.

Basically "john.residence.numberOfRooms" is a completely wasted
expression -- it's meaningless. You have to put a `?` or `!` in there
to get anything useful. I can't see any good reason for that.
"john.residence.numberOfRooms" could just behave one way or the other,
either as if the `?` were there, or the `!`. And of the two, the
obvious choice is `?` because... I already told the program it the was
optional in "var residence: Residence?". I said it was optional, and
yep I meant that. (Reminds me of the old retort "did I stutter?")
Thus, if I try to assign it to something else it too should be
optional. If I want it to be otherwise I'd add the `!`.

Making this change would just simplify a whole mess of code and about
half that chapter would all but vanish.

You have to be explicit about how to handle nullability because you’re attempting to access the property of an optional property.

In addition, seeing that `!` acts a short-circuit to error, it would
be nice to have something equivalent for fallback value. We can't use
`??` b/c it doesn't chain, though maybe it could be made to? And I'd
rather not reuse `?.` here (for reasons I can explain later). Maybe
`:` is a good choice? In any case, exact syntax aside,

  let homelessShelter = Residence()
  let roomCount = john.residence:homelessShelter.numberOfRooms

Now, roomCount will not be optional, because there is a guaranteed value.

I think simplifying Optionals this way would be a good fit for Swift,
making this part of the language a whole lot cleaner and clearer.

You can accomplish this using parenthesis:

let roomCount = (john.residence ?? homelessShelter).numberOfRooms

Preston

···

On Sep 25, 2016, at 2:19 PM, Trans via swift-evolution <swift-evolution@swift.org> wrote:


(Brent Royal-Gordon) #3

Not true; it’s an attempt to access the numberOfRooms property on Optional<Residence>. That property doesn’t exist, but it could be added using an extension.

(And Optional does have several members—most notably map, flatMap, and unsafelyUnwrapped.)

···

On Sep 25, 2016, at 4:19 PM, Trans via swift-evolution swift-evolution@swift.org wrote:

Basically “john.residence.numberOfRooms” is a completely wasted
expression – it’s meaningless.


Brent Royal-Gordon
Architechies


(Haravikk) #4

While I understand where you're coming from, I think the problem is that whichever version we specified as a guess would be wrong some of the time anyway, which is why it's better to just force the developer to think about it and put the correct symbol(s) for their use case.

For example, if we assumed a behaviour of !, then the user is going to get runtime errors that might not always occur (say the residence is nil 5% of the time, that's potentially only a 5% chance of triggering the error during testing). If we assume ? as the correct behaviour then the developer could end up with an error much further down their code depending upon when and how they use the result of the statement, and looking back at the code it won't be as obvious where that optional came from (especially if you meant to put an exclamation mark but didn't, so are assuming a non-optional value).

It's just one of those cases where I think it's better to force the developer to be explicit, rather than try to guess what they meant to do.

···

On 25 Sep 2016, at 21:19, Trans via swift-evolution <swift-evolution@swift.org> wrote:

"john.residence.numberOfRooms" could just behave one way or the other


(TJ Usiyan) #5

As I've been learning Swift recently, one aspect of the language
jumped out at me with a "code smell". Namely, the way Optionals are
handled. For starters, just consider how long this chapter is:
https://developer.apple.com/library/content/documentation/
Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
That's pretty beefy for something on the surface is pretty simple.

Handling 'nothing' is actually complicated. I am not sure that there is a
reasonable way to make it less complicated. There are quite a few tools to
deal with "this might not contain anything" and yes, that can be daunting,
but each tool pulls its weight.

More concretely, consider the example given:

    class Person {
        var residence: Residence?
    }

    class Residence {
        var numberOfRooms = 1
    }

    let john = Person()

    let roomCount = john.residence.numberOfRooms

    // error: value of optional type 'Residence?' not unwrapped; did
you mean to use '!' or '?'?

As general rule of thumb, whenever I get an error and the system tells
me what I probably meant, that is a pretty good sign the system isn't
doing all it can for me and making me jump through an unnecessary
hoop.

If you click the fixit, the compiler will insert a `!` for you. If you
typed that using autocomplete, it would have put a `?` in for you. I don't
see understand what else it can do for you. A choice has to be made about
how to handle "The left portion of this might be nil" and an implicit
choice is bad.

Basically "john.residence.numberOfRooms" is a completely wasted
expression -- it's meaningless. You have to put a `?` or `!` in there
to get anything useful. I can't see any good reason for that.
"john.residence.numberOfRooms" could just behave one way or the other,
either as if the `?` were there, or the `!`. And of the two, the
obvious choice is `?` because... I already told the program it the was
optional in "var residence: Residence?". I said it was optional, and
yep I meant that. (Reminds me of the old retort "did I stutter?")

You meant 'this might be nil'. You did not say what to do when it is and
you usually cannot always say if finding nothing is recoverable even for
the same property.

Thus, if I try to assign it to something else it too should be
optional. If I want it to be otherwise I'd add the `!`.

This seems like a step backward. `?` calls out everyplace that there is
uncertainty about `nothing`. Going back to hidden nil messaging isn't a
good move. I know what it feels like to unexpectedly message nil. I can
debug it just fine. I know that the skill can be acquired and yet, I think
that swift is better for not requiring that skill of new programmers.

Making this change would just simplify a whole mess of code and about
half that chapter would all but vanish.

It would not simplify the code at all. It would complicate reading it.

In addition, seeing that `!` acts a short-circuit to error, it would
be nice to have something equivalent for fallback value. We can't use
`??` b/c it doesn't chain, though maybe it could be made to? And I'd
rather not reuse `?.` here (for reasons I can explain later). Maybe
`:` is a good choice? In any case, exact syntax aside,

    let homelessShelter = Residence()
    let roomCount = john.residence:homelessShelter.numberOfRooms

Now, roomCount will not be optional, because there is a guaranteed value.

I think simplifying Optionals this way would be a good fit for Swift,
making this part of the language a whole lot cleaner and clearer.

I disagree, as the rest of my reply has likely indicated. Some things a
fine being implicit (the positive sign on integers, for instance).
Optionality is not one of those things.

···

On Sun, Sep 25, 2016 at 4:19 PM, Trans via swift-evolution < swift-evolution@swift.org> wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jeremy Pereira) #6

In this case, the compiler hasn’t told you what you probably mean. It’s given you two different meanings to choose from and hasn’t actually excluded other ways to fix the issue e.g. if let or guard let or even an extension to Optional.

As Haravikk and Brent have said, assuming either of these as the default is dangerous.

Assuming ! as the default, to my mind negates the point of introducing optionals at all, we might as well go back to null references. The whole point is to eliminate the “no value” problem at compile time, not run time.

Assuming ? as the default is more insidious. The application may carry on and may persist state that is incorrect.

I think the current behaviour is correct.

···

On 26 Sep 2016, at 00:26, William Sumner via swift-evolution <swift-evolution@swift.org> wrote:

let roomCount = john.residence.numberOfRooms

// error: value of optional type 'Residence?' not unwrapped; did
you mean to use '!' or '?'?

As general rule of thumb, whenever I get an error and the system tells
me what I probably meant, that is a pretty good sign the system isn't
doing all it can for me and making me jump through an unnecessary
hoop.


(Trans) #7

Just discovered that none of my replies made it to the mailing list.
Doesn't it bug others that the default reply is to the author and not
the list?

···

---------- Forwarded message ----------
From: Trans <transfire@gmail.com>
Date: Sun, Sep 25, 2016 at 9:36 PM
Subject: Re: [swift-evolution] Propagating Optionals
To: William Sumner <prestonsumner@me.com>

On Sun, Sep 25, 2016 at 7:26 PM, William Sumner <prestonsumner@me.com> wrote:

You can accomplish this using parenthesis:

let roomCount = (john.residence ?? homelessShelter).numberOfRooms

Yea. Don't know why that escaped me. Though it does start to look long
in the tooth if there is more than one.

    let x = ((foo.bar ?? dbar).baz ?? dbaz).zee

vs

    let x = foo.bar:dbar.baz:dbaz.zee

But then again maybe that is too concise.

--
People with courage and character always seem sinister to the rest.
--Hermann Hesse

Trans <transfire@gmail.com>
7r4n5.com http://7r4n5.com


(Trans) #8

"john.residence.numberOfRooms" could just behave one way or the other

While I understand where you're coming from, I think the problem is that whichever version we specified as a guess would be wrong some of the time anyway, which is why it's better to just force the developer to think about it and put the correct symbol(s) for their use case.

For example, if we assumed a behaviour of !, then the user is going to get runtime errors that might not always occur (say the residence is nil 5% of the time, that's potentially only a 5% chance of triggering the error during testing). If we assume ? as the correct behaviour then the developer could end up with an error much further down their code depending upon when and how they use the result of the statement, and looking back at the code it won't be as obvious where that optional came from (especially if you meant to put an exclamation mark but didn't, so are assuming a non-optional value).

If it is already declared an optional so it is already to be expected.
Any ease of tracking down bugs this way is relatively minor -- akin to
the advantages of using Hungarian notation
(https://en.wikipedia.org/wiki/Hungarian_notation), and few suggest we
use that anymore. Plus, if you have more than one in the chain of
calls you are still going to have figure out which is the culprit. And
it gets worse, b/c what if you decide later that it should not be
optional (or vice-versa)? Then you have work your way though all your
code removing the `?`. THAT REALLY SUCKS.

It's just one of those cases where I think it's better to force the developer to be explicit, rather than try to guess what they meant to do.

I think being explicit once is enough -- declaring the optional.
Having to be explicit on every use is just annoying. It would be like
having to declare the type on every use.

···

On Mon, Sep 26, 2016 at 4:08 AM, Haravikk <swift-evolution@haravikk.me> wrote:

On 25 Sep 2016, at 21:19, Trans via swift-evolution <swift-evolution@swift.org> wrote:

--
People with courage and character always seem sinister to the rest.
--Hermann Hesse

Trans <transfire@gmail.com>
7r4n5.com http://7r4n5.com


(Trans) #9

Oh Haskell, what have you wrought :wink:

Good point. Pretty sure my proposal translates into making flatMap the
default dispatch behavior for Optionals. I think map might well be a
YAGNI. I've tried a number of examples, and in every case where map
was used flatMap worked ... and is probably what is intended anyway.
map can return double optionals and error (and if you want the error
you can explicitly unwrap). Of course there has to be way around the
default flatMap behavior, probably the simplest is to just make an
exception for any public member of Optional. (This sort of thing is
not uncommon in Ruby actually, when method_missing is used). I'd much
rather have to be aware of a few uncommon corner cases calling methods
on Optional than to have to explicitly cater to every Optional
throughout my code.

···

On Sun, Sep 25, 2016 at 9:22 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

Not true; it's an attempt to access the `numberOfRooms` property on
`Optional<Residence>`. That property doesn't exist, but it could be added
using an extension.

(And `Optional` does have several members—most notably `map`, `flatMap`, and
`unsafelyUnwrapped`.)

--
People with courage and character always seem sinister to the rest.
--Hermann Hesse

Trans <transfire@gmail.com>
7r4n5.com http://7r4n5.com


(Kevin Nattinger) #10

Just discovered that none of my replies made it to the mailing list.
Doesn't it bug others that the default reply is to the author and not
the list?

From: Trans <transfire@gmail.com>
Date: Sun, Sep 25, 2016 at 9:36 PM
Subject: Re: [swift-evolution] Propagating Optionals
To: William Sumner <prestonsumner@me.com>

You can accomplish this using parenthesis:

let roomCount = (john.residence ?? homelessShelter).numberOfRooms

Yea. Don't know why that escaped me. Though it does start to look long
in the tooth if there is more than one.

   let x = ((foo.bar ?? dbar).baz ?? dbaz).zee

vs

   let x = foo.bar:dbar.baz:dbaz.zee

I’m not sure what I’d expect a raw `:` operator to do, but whatever it is I’d expect it to have a lower priority than `.`, making your statement equal to

    let x = (foo.bar) : (dbar.baz) : (dbaz.zee)

Member access (`.`) is a *very* high precedence operator in every language I can think of, and I think it would be quite confusing to slip something in above it.

···

On Oct 7, 2016, at 2:56 PM, Trans via swift-evolution <swift-evolution@swift.org> wrote:
---------- Forwarded message ----------
On Sun, Sep 25, 2016 at 7:26 PM, William Sumner <prestonsumner@me.com> wrote:

But then again maybe that is too concise.

--
People with courage and character always seem sinister to the rest.
--Hermann Hesse

Trans <transfire@gmail.com>
7r4n5.com http://7r4n5.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution