[Pitch] Rework the `weak` keyword into a `Weak<T>` type (compiler intrinsic)

It's not very common, but there are times when you want collections of weak references, for example, an EventPublisher object with a event listeners that you don't want to keep the even publisher alive [Weak<EventListener>]. KVO does this, for example. It's common to see people rolling their own:

struct Weak<T: AnyClass> {
   weak var ref: T?
}

I'm sure we can all agree that less special-syntax is better. I'd prefer a compiler-defined Weak<T> struct over a reserved keyword. Thoughts?

2 Likes

How would one weakly capture something without the weak keyword?

1 Like

I'm not sure I like this. What happens to capturing lists like [weak self] and [unowned self]?

1 Like

Less special syntax is mostly better for a programmers who’s already quite familiar with the language, its concepts and rules. But I see Swift as a language with a very smooth «learning curve», where it’s important to observe this balance between being friendly to newcomers and being «right» for experienced users. weak is one of those concepts which are already hard to understand, and wrapping it into a generic struct would not make life easier for most :slight_smile:

As you mentioned, people who need this kind of behavior already making their own, which indicates their understanding of how weak works and why exactly do they need this. So my suggestion is to leave things as it is.

I used this in a few of my projects where I needed my Database to keep references to it's Collections (think tables) and needed the collection to refer to the DB.

var collections: [String: Weak<MongoCollection>]

I don't like it as part of the standard library, however.

I don't see why that couldn't be spelt [Weak(self)], [Unowned(self)].

This could allow for syntactic unification of different kinds of references:

  • IBM had expressed interest in bringing GC for Swift on the server, so you could imagine there being a Managed<T> for references overseen by the GC.
  • Unmanaged<T> already exists for manual memory management.
1 Like

I'm not suggesting to "wrap weak in a generic struct", I'm suggesting that the weak keyword be removed entirely, and provided by the compiler instead as Weak<T>, so that it can be treated as a first class type, like any other.

Good suggestion. Weak is both easier to understand and more powerful.

Well I get your point there but I'm still not convinced for one simple reason: the suggested syntax looks like an own type, but types usually do not carry this type of informations with them.

I think it's worth noting that the main problem where you want a sequence of weak instances could be solved by Property Behaviors. Better yet, the whole weak keyword could be exchanged with a property behavior of it's own, just like lazy is in the proposal.

Well it kind of is. It's a special kind of Optional.

What do you mean?

I agree that a property behavior could be used to replace the current weak keyword, but how could it be used to make a property of type [Weak<T>]? Or a function that returns a Weak<T> (currently, if you return an object, you're forced to return a strong reference, even if you intend for the consumer to be assigning it to a weak reference)

I'm not sure why you'd want to return a weak reference, this is not possible today (except with the mentioned wrapper type above), please correct if I'm wrong.

Well I cannot really tell how property behaviors could solve both issues. That functionality isn't fully fleshed out yet and was deffered from the evolution process, but it would be interesting if @Joe_Groff had an idea.

I had some idea in mind, but it completely escapes me now lol. But in a more general sense, returning a Managed<T> makes sense, so having the compiler machinery for dealing with different kinds of references generically makes sense

I think the ship has sailed on the weak keyword long ago. Removing it now would introduce too much churn in codebases. I also don't see enough harm in the current approach to warrant replacing it with something is essentially the same for most uses.

As for the introduction of a Weak type into the standard library, I would be open to that idea, but I fear it would run into the same wall that Result/Either ran into. Where most people felt that it was better left to individuals to implement it. Which is unfortunate, since it's probably one of the most redefined types in codebases.

3 Likes

Yeah, but a migrator could do it quite effortlessly

I'm fairly sure the Core Team wants to avoid migrations of that amplitude at this stage in Swift's lifetime. Indeed, I think it's safe to say the ship has sailed.

I agree that a migrator could easily find and replace weak, the issue is that it's been around since forever. If this were still the beta Swift, I could maybe see getting rid of weak. (A lot of people don't remember that objects used to be instantiated with a new keyword!)

This is a great idea. This helps solve a problem that I've encountered when trying to implement a Observer/Broadcaster type pattern where I hold on Set of observers. I've had to use a wrapper object that contains a weak var reference to the object receiving the notification to avoid retain cycles as those objects come and go. This would eliminate the need to this intermediate object. Hopefully it gets picked up!

1 Like

Besides the churn issues, I don't think this is actually what we want:

  • When you assign a weak value to a new variable, you don't necessarily want it to remain weak; in fact, it's probably more likely than not that you want it to become strong. So making weak-ness into a generic type will perpetuate the wrong default behavior.
  • Weak variables are already required to be optional, so that's one level of unwrapping. You don't want to introduce a second level, like myWeak.ref, just to remove the weak wrapper. Similarly, you don't want to have to write Weak(whatever) just to assign into a weak variable.

Weakness is best thought of as a property of the variable, not the value, much like whether or not it's lazy.

2 Likes

Assignment will need special treatment from compiler, not unlike the current syntax

The Weak<T> itself will act like an optional, in itself. I'm not proposing that Weak<T> be implemented as I show, I'm merely pointing out that what people currently do to get around the limitations of weak.

Instead, I suggest the compiler will define an intrinsic called Weak<T>, that's treated like an optional that needs unwrapping to produce a (strong) T.

It will also receive special treatment, in that T can be implicitly promoted to Weak<T>, the same way T is implicitly promoted to T? when necessary.

Only until you need a collection of weak references. Then having it be a property attribute is insufficient.