Why so many "@" expressions?

I don't use Swift on a daily basis, because it's effectively an Apple-only language.
So when I recently returned to it I found two new problems.

  1. My code which ran just fine after being compiled with the Swift 5 compiler doesn't run correctly when I built with the 5.9-dev compiler. The latter produces runtime bugs that didn't exist before.

  2. Looking at some sample code, I see more At expressions than before, for instance at-frozen, at-State, at-stateobject, and so on. Is there a complete list anywhere? What are each of these for? The documentation (to the extent that it exists) is vague. Don't these expressions make the code less readable?

2 Likes

To be perfectly honest, I'm getting @ fatigue as well :smile:

2 Likes

Some "@ expressions" are built-into the compiler, like @available, @propertyWrapper, @dynamicMemberLookup. I don't know of any centralized lists of these, but it would be great if someone could share one.

Most others are provided by libraries like SwiftUI, in the form of Property Wrappers

Don't these expressions make the code less readable?

On the contrary, they vastly increase readability compared to the hand-rolled alternative.

Property wrappers let you extract reusable behaviour that's automatically applied to the getting or setting of values (like handler methods in JS or Descriptors in Python).

For example, everytime you write to a property marked @Published, your ObservedObject will automatically emit a objectWillChange event. If the @Published property wrapper didn't exist, you would have less @ signs in your code, but you would also need to do something like objectWillChangePublisher.send() on every property write. This is laborious, introduces crazy amounts of repetition and chances to forget to do it. Before you know it, you'll see questions on the forums looking like:

Asker: "Why did my view not update when I change this property?"
Answerer: "Because you forgot to objectWillChangePublisher.send()."
Asker: "Why can't the framework just automatically call that for me?"

And thus, property wrappers arise.

PS: but yeah, there’s a lot of @s now. I wish the built in ones used a different sigil

16 Likes

in my opinion, many things that currently use an @, like @usableFromInline should have been keywords or rolled into other attributes. and some things that do not use an @, like CodingKeys, should be using an @. everyone had a different opinion on those things, so we had to settle on something.

but the reason why we have so many attributes is because historically, it was politically easier to add a new attribute to the language, than it was to add a new keyword to the language. you will hear many rationalizations why ___ should be an attribute, but they all stem from the perception that keywords are more "heavyweight" than attributes.

12 Likes

I'd get rid of these three @'s:

@discardableResult func foo() -> Int
func bar(v: @escaping () -> Void)
func baz(v: @autoclosure () -> Bool)

in favour of:

func foo() -> discardable Int
func bar(v: escaping () -> Void)
func baz(v: lazy Bool)

What's left? @available, @convention(c), @objc – maybe they could use a different sigil indeed to leave @ solely for property wrappers (or vice versa).

9 Likes

Seriously is there a comprehensive documentation for all the swift built-in attributes and even those that starts with an underscore :thinking:

3 Likes
4 Likes

Then there’ll be a forum post asking why there’s so many keywords.

I think the reality of the situation (and any in the industry) is harsh. If someone doesn’t keep up, they’ll get left behind. Someone wants to build an app, they need to take the time to understand the concepts they needed. But this doesn’t mean there couldn’t be less effort required to do so.

9 Likes

Thanks. However what about ones like __shared and __owned

“guaranteed” is spelled __shared in source. i do not know why.

2 Likes

I guess 5.9 is unstable at this point. Do you have those errors in 5.7? If you show those errors we may recommend a fix.

3 Likes

That can only be true if the reader immediately knows what they mean, but actually they are confusing. For instance:

  • at-state or at-stateobject seems redundant because every var property is state by definition.
  • at-frozen is confusing because there is already "let" (constants) and static, which have similar meanings. Maybe frozen refers to a princess or something?
  • at-published sounds like it is document related. Maybe "subscribable" would be more specific?
3 Likes

They aren't printed errors, they're erroneous program behaviors.

I tried to build Swift 5.7 (checking out with --scheme release/5.7) but it failed with some errors.
Maybe I can try 5.5 next.

@frozen means fixed-layout, and it only applies to standard library types. In application code, it's the default for all structs and enums.

I totally agree hand rolled alternatives would be worse. When I was saying I'm getting @ fatigue I was more nodding at @MainActor, the custom property wrappers and result builders. So many libraries out there now have some attribute they introduce and end user code eventually gets littered with @something on top of the baked in ats. It's all a hodge podge of attributes now and more are coming. Not helping is the different colouring in Xcode. some are red some are purple, autocompletion many times breaks for attributes or doesn't exist

If you program for the Apple world you're bound to have some @objcs here and there too even if you hoped the old hag was dead and buried :joy:

While Swift always strived for readability I feel that in some parts it has become not unreadable but very noisy for lack of a better description and attributes are a major part of that

Plus if you have any dark thoughts of making your code performant you have to add even more obscure attributes. It baffles me actually, and it's probably worth its own thread, how (ultra) performance sensitive swift is not documented apart from the GitHub readmes that other people have already posted here. And those "docs" always come with almost a do-not-use-this, do-not-use-this-yet, are-you-sure warning.

I agree and agree that some are so key that they are essentially keywords, like @inlinable @escaping @discardableResult etc. My bad here too because even though I've been crawling these forums since their inception without participating or even having an account for quite a few years, I never voiced my opinion when I disagreed and maybe the balance would have leaned in favour of keywords. But I've seen all the discussions in all the pitches and reviews and indeed adding keywords was very heavily scrutinised, many times justifiably so considering the new-ness of the language.

But speaking of attributes, is there one that I can use to not have to add this gem in all my NS/UIWhatever subclasses?

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

would that be @_hasMissingDesignatedInitializers ?
Edit: Turns out that's not it

1 Like

True, but they (the property wrapper ones, at least) are option-clickable entities that you can lookup. It's no different from any other function or class.

Now consider the alternative:

An absolute cluster– of code like objectWillUpdate.send(), SwiftUI.allocateState(for: self), SwiftUI.state(for: self)[key] = value, etc. That would be less readable (lower signal-to-noise) ratio, and patterns would be implicit rather than explicit, extracted and named.

It's no different from any other kind of function, really. When you extract a function and give it a name, you do make code more opaque in one particular sense of the word. If callers don't know what the function does, it's less immediately clear than before. But of course, the hope is that the simplification you get from not having all your functions inlined, having them have names, and documentation, outweighs the indirection they add.

11 Likes

I wonder if we could drop @ sign from property wrappers?

struct MyView: View {
    State private var value = 1

Next week's top new thread: "Why so many capitalized keywords"?

15 Likes

That's even more confusing. Are they talking about a US state like Kentucky? Or is State a verb, as in "I hereby state that..."?

In the past, Objective-C's designers and Apple pointed out the obvious that longer expressions are clearer and more specific. Calling a variable (which is already state) a State sounds redundant, and it doesn't tell the context.

As Uncle Bob once said, you can tell software quality by counting the number of WTF's that a newcomer emits per unit of time.
If a newcomer looks at something and says WTF too often, then it's crap.

Classic Programming Mistake #1: Using ambiguous, vague, nonspecific names for things that leave out context.