Consistency is good because it means fewer things to remember, and that code is less likely to break when one piece is swapped out for another. It's not circular at all. I don't think consistency is the most important goal for designing something, but it's a goal.
Anyway, the case where this matters most is stored properties, not locals, but I wouldn't want to change the behavior for stored properties and not locals, because that makes the special case even smaller.
Yes, but mostly no.
It's semantically incorrect. We don't actually have a truthful mechanism for properties that cannot be initialized during initialization.*
final class ViewController: UIViewController {
@IBOutlet private unowned var label: UILabel! = uninitializedImplicitlyUnwrappedOptional()
/// The "value" of an implicitly unwrapped `Optional` that has not yet been assigned a real value.
///
/// This is technically `.none` / `nil`, but it should never be used directly.
/// If it is *ever* valid for getting an `Optional`'s value to result in `nil`,
/// then it should not be implicitly unwrapped.
func uninitializedImplicitlyUnwrappedOptional<Wrapped>() -> Wrapped! {
nil
}
just recently we introduced implicit returns from single-expression closures... now the new comers have to remember that this is good:
{
"hello"
}
and this is not
{
print("inside closure")
"hello"
}
if we want to be consistent we need to either always require return or never, right? i wouldn't mind the latter.
implicit returns from single-expression closures optimisation breaks consistency and reduces visual clutter by removing some noise characters. var v: Int? is exactly the same thing.
if we do consistency in one place but not another - we are ... inconsistent.
@jrose isnāt saying that consistency is the most important goal ā in fact, he says that it isnāt the most important goal. Rather, heās saying that we should be consistent unless thereās a good reason to be inconsistent (unless Iāve misunderstood what he said). It would be theoretically nice if Swift were a totally consistent language, but in practice it would be verbose, take a long time to compile, and/or cause bugs.
Implicit returns for single-expression functions helps make code more readable by removing useless return statements. Furthermore, single-expression closures were already inconsistent with multiple-expression closures before this feature was implemented since type inference only applies to single-expression closures, not multiple-expression closures. And I think the rule is simple enough that its complexity cost is justified.
yep, that's how i understood him as well. looks like the decision whether to prefer "more noise plus more consistency" vs "less noise plus less consistency" is taken on a case by case basis.
I think every exception should have a rationale. I don't think there is a good one for implicitly initialized Optionals.
By the way, Iām not sure how practical this would actually be, but Iād love some form of āmulti-stageā initialization that allows you to call instance functions to set properties. I suspect thatās infeasible, since youād need some way of confirming at compile-time that the relevant instance methods donāt depend on uninitialized properties, but I can dream.
Implicitly-unwrapped Optionals are meant to solve this problem, of course, but they remain dangerous (if not unsafe in the sense of undefined behavior) forever.
the other radical way of solving this inconsistency is to introduce some DefaultValueInitializable (?) protocol. Optionals would confirm to it, so could user types.
:-( I didnāt realize thereād been an attempt so recently. Still, it seems like itās got pretty broad support, so it may still be worth me writing out the full pitch.
EDIT: And I replied to the most recent one too, ha.
These issues are not separate at all; they heavily overlap. When you write var x: Int?, the compiler implicitly adds the "variable initialization expression" nil as if you had written var x: Int? = nil, hence "implicit initialisation of T?".
MyStruct.s is only initialized once: var s: S = S(1). In MyStruct.init(s:), you're merely modifying the value: self.s = s. And you can do that whenever you want, including in the initializer, because MyStruct.s is a mutable var property.
This behavior is very straightforward to me. I don't understand why you think this is a bug.
Definitely. I donāt personally think of Jun 2019 as very ārecentā. A lot has happened in between. The community has matured, views have changed. And most importantly, the window is briefly open for this change now that Swift 6 is on the horizon.
The consistency is that computed var and closures always behaved like this, and I think it's quite natural. And the compiler will help you if you don't think so...
@jrose as the topic's author you can include a poll panel in the initial post like here. it won't automatically lead to anything material but at least you'd immediately know what people think about it, and maybe that would make this change successful compared to the previous attempts. make sure you make it clear that this change is supposed to happen in all cases (structs/enums, classes, local variables, implicitly unwrapped optionals).
ps. it would still be a -1 from me for the reasons i stated above.
pps. i feel the majority of the votes would be positive, perhaps 80/20
ppps. i would be more happy to lose on this one should the majority decide this is the way to go and should there be enough motivation to make the actual fix.
These forums are far from representative of the Swift community or any subset of it, and while the presence or absence of a rough consensus can certainly send strong signals, decisions aren't taken on votes. The purpose of pitching ideas is to solicit good arguments for or against a design and to refine ideas iteratively.
yep, that's what i meant; 80/20 positive vote would send a strong signal; nothing less, nothing more.
somewhat related. this:
struct S: Decodable {
var x: Int
var y: Optional<Int>
}
let data = "{\"x\": 1}".data(using: .utf8)!
let x = try! JSONSerialization.jsonObject(with: data, options: [])
must fail, as y is not marked as having default value. it currently doesn't fail in any form (including short-hand Int?). the current behaviour doesn't allow you to opt-opt of this "no key ==> nil" behaviour. the fixed version would make it more flexible and allow to differentiate:
"{ x:1}"
var: y: Int? ===> ERROR *** not the current behaviour
var y: Int? = nil ===> y=nil
var y: Int? = 3 ===> y= Optional(3) *** not the current behaviour
"{ x:1, y: null}"
var: y: Int? ===> y=nil
var y: Int? = nil ===> y=nil
var y: Int? = 3 ===> y=null
"{ x:1, y: 2}"
var: y: Int? ===> y=Optional(2)
var y: Int? = nil ===> y=Optional(2)
var y: Int? = 3 ===> y= Optional(2)
How often do people use the full form Optional<T> to avoid implicit initialization? I guess, not often enough to give up a convenient syntactic sugar. Initialization with nil is the most common case and it is good to suppress ? = nil noise. Also, as mentioned above, this behavior is consistent with property wrappers that can have an implicit initializer.
Yes, nil is a very natural starting value for an optional, but that is not a valid reason since there are lots of other types with natural default values, like empty arrays, 0 for all numeric types etc.
This is very inconsistent, and the potential behaviour of property wrappers doesn't really change that, they are not basic types and part of the point of having them is to add special behaviour like that.
This is a great question ā it has come up in a few discussions of this (pre)pitch or similar ones.
To be honest I had no idea this was a thing and the fact that it exists seems like an oversight / bug to me rather than something that was intentionally designed as such.
To me its existence brings up many more questions than problems it may solve; to me it's another reason to remove the special behaviour of Type?.
That doesn't answer the question directly but I'd be curious what others think.