Pre-pitch: remove the implicit initialization of Optional variables

It's hard for me to express since I lack the precise terminology required, but optional nil values are unique in that they apply equally to all optionals, everywhere. That is, they have the same meaning for all instances: .none. The same is not true of 0 or [] or [:]. Each instance of those types may mean something different for the empty value, or the empty value may be invalid altogether. In that way nil is a valid default value where the other instances aren't.

Also, nil defaults are likely an order or even orders of magnitude more common than other defaulted values, especially when interacting with Obj-C APIs. In that way the burden of having to explicitly initialize them is far greater than Ints or Arrays. So even if you don't consider the type-based default value argument to be valid, it's still a matter of the sheer scope of the change you're talking about here.

2 Likes

I understand what you mean, but I don't think it's true. in let x: String? = nil the nil does not mean the same thing as in let x: Int? = nil.

Optional<String>.none is not the same as Optional<Int>.none, in pretty much exactly the same way an empty String array is not the same as an empty Int array.

If you are talking more conceptually then sure, different nils have sort of the same meaning, but that is also true of different empty arrays.

1 Like

Exactly. nil being a magical placeholder for Optional<T>.none is convenient but also obscure. And hiding it altogether as an implicit default is even more damaging to the mental model. Hence this thread.

Not really, no. Just because Swift's type system requires explicitly typing optional values doesn't mean Optional<Int>.none and Optional<String>.none can't be considered the same things. In Haskell I believe they'd both just be the Nothing type instead. Whereas empty arrays have a variety of different internal behavior depending on the type your using.

Obscure? How so? While people have pointed out the edges to Swift's implicit nil model in this thread, I haven't heard any actual harm or even damage that is unique to this feature vs. other implicit Swift behaviors (where they can be surprising or implicitly change behavior). The model seems quite clear to me; declarations using the ? form will default to nil when used as a property or local variable. The other behaviors seem to fall clearly out of those two behaviors. This mostly seems to be a documentation or edge definition issue.

1 Like

This thread exposes a few of the damages. Here's another one:

Those don't seem to be damages, unless you consider every misunderstanding about language behavior damaging. In which case they come and go as the user gets more familiar with the language.

Edit: And I guess my ultimate point is that those edge cases can be fixed without removing the feature from the language.

I mean yeah, you can "consider" anything to be anything, but in the language, they are different instances of different types. And if you want to talk conceptually, it's hard to argue that nil is fundamentally different from [], that is just subjective.

What differences would that be?

By the way, even if optionals would be different from arrays etc, I don't think that's an argument. They are still a type, with instances and values, it can be var or let, you can extend it etc etc. So it should still behave the same w.r.t. implicit initialisation as other types.

I've been working with Swift professionally for 5+ years. I'm very familiar with it, and I mistook this behavior for a compiler bug just last year. It is a problem beyond unfamiliarity.

8 Likes

How do you distinguish between simple unfamiliarity and a broken feature then? I've been working in Swift quite a while as well, yet I forget the edges of the generics system, initializer inheritance, or other rules fairly regularly. That doesn't mean the features are broken. It may mean the compiler could be improved, documentation could be added, or that I'm tired at some particular moment. So I think we need more justification for language changes than "sometimes people forget this rule".

I think the difference is that implicit initialisation of variables is an extremely basic feature, whereas obscure generics behaviour isn't.

You will notice on day 1 that you do need to initialise all variables, but it took me until 2020 I think to notice it wasn't the case for optionals, generalising what you observe is the most natural thing in the world, so we should lean into it. That doesn't really apply to the obscure corners of generics.

5 Likes

Implicit nil initialization for T? is a special case, which should be treated specially. Pursuing pure consistency will incur inconvenient burden which bring =nil ... almost everywhere. It's worthless in reducing one special case in exchange for much more boilerplate code to write out.

The same can be said about any feature that isn't immediately obvious. People say it about Swift's shorthand closure syntax or type inference rules on a regular basis. That doesn't mean they should be removed. Generally it means we could improve documentation, compiler diagnostics, or the edges of the feature. Removal is usually reserved for actively harmful features, and I don't believe this feature meets that standard.

In any case, there doesn't seem to be much more to discuss here. If we get a full proposal out of this I'll write up a full review then.

1 Like

Sorry but this part simply isn't true, you can't compare it. Whether or not variables are implicitly initialised is completely fundamental to how a language works. Trailing closures aren't even in the top 10 of fundamental features, nice as they are.

i think you are exaggerating... here's my list of top fundamental features of a language (YMMV of course):

  • assert(x == x) // shalt not fail (can fail in some languages including swift in a few cases)
  • assert((x == y) == (y == x)) // ditto
  • array.append(x); assert(array.contains(x)) // ditto
  • ...
    in the grand scheme of things var c: T? = nil vs var c: T? would be closer to the bottom of the list, so let's not start a full scale "endian" war about it.

a few examples of other swift inconsistencies (despite being inconsistencies somehow everyone is happy about them!):

  • true / false (instead of .true / .false)
  • nil (when we already have .none)
  • "let v = false" but not "func foo(v = false)"

In the case (and I don't agree, so much of swift is simply 'magic') that the argument can be made both ways (as is the case with most things), then let those who like to not type = nil not have to do it, and those who do like it do it. This discussion feels too pedantic.

  • Auto closures are magic.
  • ResultBuilders nothing but magic.
  • Trailing closure are magic.
  • Decodable is pure magic.
  • Enums with type conformances are magic (they assign cases to a value that you definitely don't need to provide).
  • Type inference is magic.
  • Implicit returns are magic (though they help align auto closures and normal parameters)

These were all created for the explicit purpose of not having to provide a value or a type, or having to type (), or build whole functions for decoding or encoding. They were made to elide implementations and code that would otherwise have to be provided.

There is VERY clear evidence that we would propose to remove requiring = nil to the language should it not have been there from the début. This is the entire spirit of Swift (much to my chagrin, because I absolutely hate the broken magic of ResultBuilders). We like removing things if it's not necessary and doesn't cause much harm.

I think not requiring = nil is harmless and I fully believe it aligns with the spirit of Swift.

1 Like

This example demonstrates a much larger burden when teaching new programmers than anything related eliding = nil or optionals in general. Shorthand for for arrays and dictionaries are also more esoteric and confusing.

As a point of fact, most of the things that you list aren't "magic," which isn't a synonym for syntactic sugar, and it's worth pointing out that result builders especially are anything but magic. They are as unmagical as they come.

We use "magic" as a term of art for a feature that's implemented in a way that can't be expressed in the language itself, most often in distinction to something that could just as well be implemented in a third-party library.

It has always been a goal to make things less magical whenever possible. For example, even basic types like Int are ordinary structs, and even basic operators like + are ordinary operator functions--all defined in Swift and even shadowable by the end user. (Obviously, at some point, the Swift implementation needs to make use of compiler primitives; we are discussing facets of the language as exposed to the user.)

Each result builder is defined entirely in Swift, and the way in which the result is built up can be understood entirely in terms of the rules of the language. There are no result builders vended by the standard library yet; however, you can refer to the ongoing discussion on string pattern matching for a possible future inclusion. If such a result builder were to be included in the standard library, it would be doing nothing that an end user's own result builder couldn't do. For this reason, result builders are not magical at all.

The ? operator, by contrast, is as magical as they come. You cannot define a postfix ? operator to shadow the standard library's. You cannot inspect the declaration of that function, because there is none. If you do not also understand C++, you can't even inspect how Swift implements the behaviors it endows the ? operator even if you find the implementation.

Along the same lines, you can create a type that conforms to ExpressibleByNilLiteral (not magical), but there is no amount of cleverness by which you can make a local variable or member of that type implicitly initialized by default to nil (magical).

This is absolutely not the case, but I see that you're going to simply reassert this without evidence and without engaging with my earlier reasoning as to why, so :man_shrugging:

14 Likes

My apologies on using the word 'magic'. I really should have said syntactic sugar.

Well, I understand your points. It's just my taste and your taste.
If this pitch became actual proposal and accepted, I will be affected and I'm against with that so I just expressed my opinions.

Seeing through that perspective is your personal choice, and it doesn't make sense to me to see it through that.
That said, however, yes I do understand that point.

If that's misleading, every implicit optional declarations written in docs are, too.

Yes, I agree that its meaning will be clearer, but whether its noisy is dependent on individuals.
To me, personally, it's noisy.


I mean, I don't see a concrete point to make this kind of choice forced by at compiler level.
Why not use SwiftLint or contribute to swift-format and let users have choice?
Does it really worth narrowing users choice?

Or in the first place, "NO" post shouldn't have been here yet as this is not yet a Pitch nor Proposal?
In that case sorry for disturbing your discussions and I'll be just quiet and wait.

as explained above i am quite against this pitch itself, but if this is the only approachable way to fix the other issue (that according to feedback i'm getting is "inseparable"), i'm ready to accept this one, as the "unwanted default values in memberwise initializers" is a bigger evil in my eyes than this pitch.

how are we going to proceed with this?

or is it going to end the same unfortunate fate as the other attempts?

is the core team discussion in order to resolve this once and for all?

1 Like