Call it that if you prefer. Maybe we can make a semantic distinction (e.g. one sets the values of self and returns self, the other sets the values in a copy and returns a copy).
The language steering groupâs guidance is that we explicitly would not want to see new DSL-like rules invented to âget rid of $0â or rebind self.
Absolutely agree and the offered solution and constraints miss the point entirely. It is silly that builder functions exist but we canât offer a solution where âselfâ is rebound or the initial lookup is rebound(maybe not self)
MyRect.{
x = 100
$0.x = 100 // same as above
y = 200
self.title = â100â // self isnât MyRect
}
Still, to me, seem best. It feels like a thin syntax to accomplish the goal.
Roughly: a substantial corpus of well-thought out examples where the proposed syntax is clearly better than what we have today, together with an analysis showing why the proposed syntax would not introduce any new ambiguities that cannot be resolved through some mechanism that doesn't impose an undue burden on users of the existing language, and an analysis of performance or complexity-of-implementation for the compiler that might result.
It is better because at the end of that statement âcomponentsâ is a let constant.
You could get a lot of that if you had a way to turn a car into a letâŚlike if âlet components = componentsâ didnât trigger a circular reference error and was just treated as âok thatâs a promise that components is read-only nowâŚâ
Could the "with" feature be a power that we give to tuples? Like a trailing closures on the tuple definition that is provided with mutable members of that tuple?
class MyViewController {
let myButton = (UIButton()) {
$0.translatesSomethingSomething = false
$0.backgroundColor = .purple
}
}
Regarding following issue:
This approach is not deviating from C-style with special unfamiliar operators, because there are none. Trailing closures are an established pattern in Swift and its not very far from what we have today:
class MyViewController {
let myButton = {
$0.translatesSomethingSomething = false
$0.backgroundColor = .purple
return $0
}(UIButton())
}
Only difference is, that the proposed syntax can be applied to all types (not only classes), we can drop the return and we have the name of the object of interest in the front.
Another advantage of building on top of tuples could be that multiple values can be mutated at the same time. Example:
class MyViewController {
let primaryButton, secondaryButton = (UIButton(), UIButton()) {
for btn in [$0, $1] {
btn.translatesSomethingSomething = false
}
$0.backgroundColor = .purple
$1.backgroundColor = .black
}
}
Another potential plus: Since trailing closure on tuples are not supported yet, we may be able to avoid conflict with existing code or other ambiguities for the parser.
There's two problems with that. The first is that single element tuples don't exist. The second is that it's just as magic as .with, but it doesn't have any benefit, aside from shorter typing, over a with free function.
Since it has not already been mentioned in this thread, I want to point out that the swift-protobuf package already includes with as a static member function of its Message protocol. As a user of the package, I have found the method to be very handy and pleasant.
The static function is possible because Message also has a required no-argument init(). Having with as an instance method instead would (IMO) be just as satisfactory.
As the person who implemented this, in retrospect I regret the choice of with as the name of this API. There's no real reason for it to be a named static method nor a benefit to it; an init(_: (inout Self) -> Void) that mutates a default-initialized value would a better choice. And if there had been a general solution to the problem in this thread, we wouldn't have introduced either one, so while it happens to share the name with I don't know if it's necessarily an argument for or against what's being discussed here.
I would like to point to a âsisterâ of with (or however it would be called), namely a conformingTo that works like a filter to one element, i.e. if the closure argument (which consumes the element) is true, it returns the element and else it returns nil.
Questions: Is this feature a good idea, is this list of features somehow âcompleteâ with these two (what else can you think about), and is it a good idea to discuss such a group of features?
Maybe I should have left out the mentioning of tuples. But in the end I think (Something()) { $0 } is not any more magic or confusing than { $0 } (Something()) and the later is already valid syntax. Don't get me wrong, I would rather have a function named with or configure in the standard library than nothing, but if you take my first motivating example and you don't set up just one member, but more than a dozen, then the extra word would become quite redundant.
So, unrelated to the syntax we commit to, I hope we don't just consider the case where you are in the body of a function and you just want to scope the area where you can mutate a variable, but we also deeply think about the case where you set up a bunch of type members and you directly want configure them (Like views, formatters, decoders, etc) to support local reasoning.
Keep in mind, that another feature that is frequently asked for is a 'common initializer' that is or can be invoked from multiple initializer. Enabling developers to configure members in the place where they are defined, could address this need, by making it less likely to become an issue. Having a crisp and less verbose syntax would encourage this.
Last but not least: Applying the 'with' functionality to multiple values is useful in my eyes as well.
I've been gently pushing my Swift projects in the FP direction for a while now, and I really like the increased ease in testing. But frequently working with immutable structs can be quite cumbersome without a feature such as 'with'.
Like many others here have said, you end up writing your own version of this all the time, so it would be great to not only have it in the standard library, but to also have it feel like it fits with the rest of the ecosystem.
Not strictly relevant to the topic, but by "immutable structs" you mean "structs with var properties, that are still technically immutable because they are value types?" And if that's not the case, how does .with help you?
You don't need to "copy and update" in Swift: when using structs, the language does it for you. Enforced value semantics is one of the foundational and most useful features in Swift, that makes it more powerful, in this regard, than many other languages.
And "mutable" structs is precisely where .with shines, because you can do something like this:
struct Person {
var name: String
var age: Int
}
// assuming that a certain `Person` instance already exists
let person = ...
// we can use `.with` to produce a new value in a safe way
let olderPerson = person.with { $0.age += 1 }
If the age property was a let, this wouldn't work, and you're be forced to write something like:
struct Person {
let name: String
let age: Int
}
// assuming that a certain `Person` instance already exists
let person = ...
// `.with` is useless
let olderPerson = Person(
name: person.name,
age: person.age + 1
)
My bad for not even noticing this version of 'with' necessitates mutability. Thank you for pointing that out
That said, I'm still a big +1 on this proposal - however it shapes up. I really like the convenience, and I'm used to using 'with' in F# (albeit without mutation).
For my Swift projects that I pushed in the FP direction, I usually only had a few types that benefited from âwithâ. For those types (always immutable structs), I implemented âwithâ on a type-by-type basis very simply as follows:
struct Person {
let first: String
let last: String
let age: Int
func with(first: String? = nil, last: String? = nil, age: Int? = nil) -> Person {
Person(first: first ?? self.first,
last: last ?? self.last,
age: age ?? self.age)
}
}
Example usage:
let person1 = Person(first: "Alice", last: "Jones", age: 32)
let person2 = person1.with(last: "Smith")
I really like the code completion this affords in Xcode, but I have two questions as a Swift noob:
From a Swift language point of view, is there anything "wrong" with my implementation - form or function?
I havenât yet jumped into Swiftâs attached macros, but is this implementation a good candidate for it (i.e. is it even possible)?