struct Foo {
let value: String
init(_ value: String) {
value = value // Cannot assign to value: 'value' is a 'let' constant
self.value = value. // but it can't be anything but this, so why not just allow the above?
}
}
FWIW, if Foo.value
is a (settable) compute property, it could reasonably be interpreted as self.value = self.value
as well.
I don't understand what you mean:
struct Foo {
let value: String
// is this "settable" computed var?
var bar: Int {
get { _bar }
set { _bar = newValue }
}
var _bar: Int
init(_ value: String, _ bar: Int) {
// can we have implicit self here? so instead of this
self.value = value
self.bar = bar
// can be this:
value = value
bar = bar
// like if-let:
// if let value = value
}
}
After Foo
is initialized, you can do self.bar = self.bar
. Regardless, IMO, the rule you're proposing is strangely circumstantial. I have a hard time deciding whether it is a good sugar/inference or not.
Imho that would be quite a lot of new complexity for a small gain.
A clever way to assign all members automatically could make a difference, but just getting around typing "self."?
If you really don't like the rules, you can also choose different names for either the member of the parameter, so that there is no ambiguity anymore.
I can appreciate the desire to not have the weird redundancy that languages often have with Class init functions. That said, value = value
reads as a mistake to me.
What I'd love is a way to say "If this init declaration mentions its property symbols in the parameter list, just set them."
Maybe @prop?
Class Foo {
let bar: Int
let fum: Int
init(_ bar: @prop, fumbled: Int ) {
self.fum = fumbled * 3
}
}
Such that var x = Foo(2,4)
yields x.bar == 2
and x.fum == 12
So many @ attributes for parameters already: @escaping, @someResultBuilder, @somePropertyWrapper (are there more?). Not sure about yet another one...
I feel allowing this:
value = value
is just another case of implicit self
. We can have this in my example init
or any member method:
var value = value // implicit self on 2nd value
Isn't what I want the same, just implicit self
apply to the first value
?
The right randside is implicit the member variable because at this point there is no other local declaration of value (note that a parameter is a declaration in the function body local scope), but if you do something like this
class Val {
var value: Int = 0
init() {
let value = value // OK same as let value = self.value
value = value // error: cannot assign to value: 'value' is a 'let' constant
}
}
Because basically if you think of this in top-bottom order of declarations now at the point value = value
there is a local variable value already declared which for the rules of the language takes "precedence" (note that precedence may not be the formal term here but it can get the idea across) meaning that every reference to value from this point on is choose to be the local declaration unless explicit specified with self.value
. So at the end the same behavior is applied to parameter which are also local declaration in the scope of the function body. So the point is basically the compiler can infer implicit self for a member unless this member is ambiguous with another declaration in the local scope of function/initializer because if there is a local decl it will always be look-up first and reference be resolved as local variable which is a general rule of the language as far as my understanding goes :)
So basically, for value = value
is not that the compiler can't infer implicit self, is the left randside will be always resolved as the local parameter declaration and this will be basically assignment to itself in case this value was an inout parameter but since all non-inout parameters are let variable for the context of the function body, we get the cannot assign to value: 'value' is a 'let' constant
...
No. There's nothing to indicate that this is a particular case, it just looks like a bog standard assignment.
var foo = foo and for that matter let foo = foo makes it explicit that a variable is being created.
self.value = value makes it clear that two different variables are involved.
Implicit actions are fine, in my mind, only when the situation is unambiguous.
This boilerplate is annoying, but removing self.
, IMO, makes code less readable at the expense of marginal improvement in writing convenience.
Iād be more interested in compiler-generated memberwise initializers for public structs (and maybe even classes).
+1
What is this and how does it work? So this is different from synthesized init? The reason you have to hand code init because you need to do special processing. There is no way it can be just "compiler-generated".
here's my take on it. talking only about those cases when custom code is not needed, so it's only about renaming / reordering parameters and obvious compiler generated boilerplate implementation, for both structs and classes.
struct / class T {
let x: Int
let y: Double
let z: String
let w: Bool
// examples:
init(x, renamed y, reordered w, _ z)
// usage: T(x: 1, renamed: 1.0, reordered: true, "z")
init(_ x, _ y, _ z, _ w)
// usage: T(1, 1.0, "z", true)
// this is the default memberwise initialiser we know and love
init(x, y, z, w)
// usage: T(x: 1, y: 1.0, z: "z", w: true)
}
i also find it non ideal that my custom initialiser prevents the default memberwise initialiser, in order to preserve default initialiser i have to declare my initialiser in an extension which is often distracting.
I see: full control over parameter labels for synthesized init.
yep, and you can have more than one.
i also like @Hacksaw's idea above (and just realised that mine is very similar). maybe combine the two ideas?
init(_ x, _ y, w, string z) // body is not needed here
init(_ x, y, reordered w, z: String) { // body is required here for z
self.z = "custom" + z
}
// usage: T(1, y: 1.0, reordered: true, z: "z")
init(x, y) // error, z/w are left uninitialised (unless they have default values in type or unless body is provided)
init(x = 1, y, _ w, z = "default") // default values also allowed here
Oh wow, that would be fantastic! I really wanted this! This solved this need very elegantly!
Same thing. I'm talking about cases where synthesized init would work (no special processing), but it is not synthesized because it is a public struct or class. I'm suggesting that rather than trying to improve the self.foo = foo
line itself, to reduce the sheer volume of such lines.
Cases when you need to apply special processing to a few fields, but the rest are copied as is, can be mitigated by breaking down your type into smaller ones, so that special processing of selected fields is encapsulated into smaller types, and on the top level synthesized init would work.
I cannot think of any good reasons to have parameters of the initializer to be named or ordered differently from the properties. IMO, that would only introduce unneeded inconsistency. If there happen to be such reasons, they probably deserve a comment in code. In such case, I would prefer to have hand-written initializer, just to have sufficient space for a high-quality comment.
I agree it would be handy to turn default values of properties into default values of initialiser arguments. Not sure why it is not the case yet.
In the summary, I'm thinking of something like this:
public struct Foo {
public var bar: String
public var baz: Int = 42
// memberwise - contextual keyword
// compiler synthesises init(bar: String, baz: Int = 42)
public init memberwise
}

In the summary, I'm thinking of something like this:
This already works:
struct Foo {
var bar: String
var baz: Int = 42
}
Foo(bar: "moo")
Foo(bar: "cow", baz: 17)
Synthesized init
for public
structs seems to be orthogonal to the default value for parameters feature.
"I cannot think of any good reasons to have parameters of the initializer to be named or ordered differently from the properties."
Consider a case where a class has two () -> T properties, where one might be quite short, like a function name, but the other might be a multi-line closure, and you can't predict which. It sure would be nice to have more than one init ordering.

Consider a case where a class has two () -> T properties
I would think such cases are rare enough that it's not too much a burden on developers to handle it themselves by defining their own init
. You can even have one version simply forward to the other, effectively turning it into a one-liner.