Hacker News new | ask | show | jobs
by The_rationalist 2075 days ago
Name some swift features that add too much complexity / conflict with existing features. Then your reasoning could become more than a mere heuristic
3 comments

I think for a lot of us, the addition of view builders and function builders that ignored the process of adding features to the language just so the SwiftUI team could use less punctutation was a jumping the shark moment for Swift. On top of that SwiftUI has been a disaster compared to Apple's existing UI toolkits because it's less featured than UIKit, less reliable and unlike UIKit, there's no new hardware forcing its adoption.

None of this is new per se; weird Apple specific edges like @UIApplicationMain and @NSManaged have always polluted the language. But, it's hard to buy in on "Swift world domination" when server-side Swift is stillborn, Rust is more appealing as a better bare metal language, Catalyst and SwiftUI are weaker than AppKit and UIKit, the last time Swift got a useful language feature was guard let in Swift 2.

So far I've not found SwiftUI usable for my work at my (large well known) employer. I like some of the concepts, but it's insufficient to actually use for our apps. Instead my team built a component system on top of UIKit that gives us the benefits of easier to build UI, without the "holy" and incomplete design of SwiftUI. Having Swift has allowed this work to be easy to construct and extremely type safe, and gives us the benefits of being able to do more work with fewer people, which was sort of the goal of SwiftUI, but in a more compatible way.
I totally agree with your point about SwiftUI: that also was a moment where I started looking to other languages in terms of what I should invest my energy in for the next few years due to the problems in governance with the language.

On the point of "swift world domination", I still think this is largely a PR problem. I think Swift's identity as "compiled python" could make it a very good fit for many use cases where scripting languages are currently being used, due to it's clean, high level syntax and powerful type system. It still suffers a chicken-and-egg problem with respect to broader tooling and library support since it's viewed overwhelmingly as a language for making iOS apps.

"Swift world domination" is from Chris Lattner, Swift's author. He proposed that Swift could simultaneously be better at text manipulation than Perl, data science than Python, high-performance compute than C, more expressive than Ruby, etc... The result is a language ideal for no one and pushed heavily by Apple.

Swift as a "compiled python" loses to Go, and that not even Go's design goal (though it is a fairly common use of it).

> The result is a language ideal for no one and pushed heavily by Apple.

Does it have to be "ideal" to be worthwhile? I think it fits a similar use-case to something like C# but it's a lot nicer to work with. I don't understand why Swift shouldn't be perfectly usable for data science, or writing server-less functions for example: it would be a perfectly suitable stand-in for Python or Go in these cases, but with a much nicer type system in my opinion thanks to the first-class optional handling.

> Swift as a "compiled python" loses to Go, and that not even Go's design goal (though it is a fairly common use of it).

I totally disagree. Go is a jail. It's optimized for big teams with a lot of turnover, where you want to prevent complexity in the code-base by choosing a language with very limited facilities for abstraction.

Swift on the other hand has a really flexible syntax, which allows you to hide a lot of complexity behind simple interfaces. In that way, it can be a lot more like Python, where the code you're writing is very much at the domain level, and you don't have to think about the implementation details very often. There are many things you can express in swift which are no expressible in go.

YMMV but I found SwiftUI pretty good for a framework that only has two years.

It runs circles around storyboards when it comes to collaboration and traceability. Disregarding bugginess, the previews are a lot better than what was possible with IBDesignables.

The integration with UIKit is actually quite well made, and for most part not needed.

> there's no new hardware forcing its adoption.

While there is no explicit new hardware, there are several popular features that are only doable using SwiftUI: home screen widgets and complications. It will also become the language in which to write universal iOS/macOS programs.

> when server-side Swift is stillborn, Rust is more appealing as a better bare metal language

I agree with this, at least from Apple's part most effort is clearly done on "application level" and usefulness of Swift outside there seems more exploratory at the moment. Personally I'd like them to start adding higher, rather than lower level functionality, and steer towards scripting.

> I agree with this, at least from Apple's part most effort is clearly done on "application level" and usefulness of Swift outside there seems more exploratory at the moment. Personally I'd like them to start adding higher, rather than lower level functionality, and steer towards scripting.

This here makes sense. I can see Swift becoming more focused on the application-level side at the moment and in the future becoming a way of writing cross-platform GUI apps in Swift, more likely if it is with Qt5 with direct C++ interop with a SwiftUI-like DSL.

As a Rust user, the server-side use-case for Rust is far more developed unlike Swift and would much rather see Rust pushing for low-level development as well.

However, I've seen many attempts for a cross-platform GUI library for Rust for a long time and it has been proven to be difficult in terms of adoption, implementation and maintenance. It would take an enormous amount of effort to cover all three of these difficulties from scratch and using bindgen bindings to wrap and maintain it and I'm afraid for them it would be more trouble than it is worth. But who knows?

I just don't see a mature cross-platform GUI library coming from the Rust ecosystem anytime soon.

While I don't think adding C++ interoperability is one of the'mashup of features', one example of a feature that adds complexity and conflicts is the function builders (result builders)[0].

It doesn't interact well with other language features (e.x. if let wasn't supported for a long time), it isn't really extensible (transforms are specified by the language), and yet it adds a bunch of complexity in both the language, implementation and reading code.

It seems clear, at least to me, that this is something that happens if you try to make a feature 'generic' without a lot of time and design consideration, due to Apple's internal politics on SwiftUI. I like SwiftUI, and some features that support it are great (like custom attributes), but I firmly think that they should have taked the time to produce a better design.

[0]: https://github.com/apple/swift-evolution/blob/main/proposals...

Swift is a crescendo of special cases stopping just short of the general; the result is complexity in the semantics, complexity in the behaviour (i.e. bugs), and complexity in use (i.e. workarounds).

https://www.quora.com/Which-features-overcomplicate-Swift-Wh...

That was 2015, when Swift was far simpler than today.

Heck, they added almost all of Smalltalk syntax recently to address one special case of a special case.

https://blog.metaobject.com/2020/06/the-curious-case-of-swif...

And the method syntax that this is a special case of a special case of is many pages of spec.

And of course there's initialization, which was 14 pages of the Swift spec at the time, with crazy overlaps and gotchas. Unsurprisingly and predictably (as in: I predicted it), it wasn't enough, or rather far too specific, so for SwiftUI they had to create an entirely new way of creating/initializing: function builders.

https://blog.metaobject.com/2020/04/swift-initialization-swi...

I find it funny when people complain about Smalltalk syntax in Swift, since in my experience keyword arguments and trailing closures are two of the features which make Swift clear and nice to work with.
It would have been nice and simple if Swift had used Smalltalk syntax from the start. It's quite another thing to reject it at first, and then also bolt it on later alongside the non-Smalltalk syntax.
It’s wise for a language to be parsimonious with syntactic constructs at first, and then gradually introduce concepts which seem like they should obviously be there.

Multiple trailing closures fit a common use case and seem like a natural extension of the language.

> wise for a language to be parsimonious with syntactic constructs at first

Absolutely.

But Swift wasn't parsimonious at first. Not even close. It unwisely rejected the parsimonious case and chose tremendous complexity.

Keywords? Or parens? Both! And we'll throw in a bunch of other stuff as well. And the weird case of having two keywords, one with a colon and one without, because we decided to make the keywords the names of the parameters. Sometimes.

And then afterwards they noticed that the parsimonious choice that they had initially rejected in favor of their much more complex choice was needed in addition to all that initial complexity.

Have you worked with Swift at all? Because the features you're complaining about aren't really difficult to understand in practice, and they're a major part of what makes Swift really clear and nice to work with.

> Keywords? Or parens? Both!

Why should they be mutually exclusive?

Swift's named parameters are great! For instance they allow for default parameters, which is something I really miss in Rust for instance where I have to use the builder pattern everywhere.

> And the weird case of having two keywords, one with a colon and one without, because we decided to make the keywords the names of the parameters. Sometimes.

Swift's rules for parameter naming are very simple, consistent, and easy to understand. Given the signature:

    func foo(first second: Type)
`first` is the name used outside of the function, or the keyword for this argument, and `second` is the name used inside the function, or the parameter name. If the two names are the same, the second name can simply be elided. So for instance this signature:

    func foo(x: Int)
Is just syntactic sugar for this:

    func foo(x x: Int)
It's super simple and something which every swift developer can pick up within minutes of starting their first tutorial. I don't really understand why you seem to getting so agitated over this non-issue.
> It unwisely rejected the parsimonious case and chose tremendous complexity.

Swift's keyword syntax isn't complex at all.

> Keywords? Or parens? Both!

I know you like Smalltalk, but keywords and parens are used together in a lot of languages (e.g. Python) so it's a tested idea.

> And the weird case of having two keywords, one with a colon and one without, because we decided to make the keywords the names of the parameters. Sometimes.

You don't have two keywords, you have an optional variable name. It's intuitive at the first time you see it - you're phrasing makes it seem much more complex than it really is.

I think you're trying to imply that Swift isn't an elegant language with it's syntax fitting in a postcard, but that doesn't mean it's 'tremendous complexity'. This whole comment is a bit too harsh to Swift, calling the legitimate differences between Swift and Smalltalk as 'complexity'.

It just shows an ability to learn. There is nothing wrong with that.
Sure, morally speaking, I completely agree. It might mean the language ends up more awkward and complicated than it had to, though.
I am not familiar with the history of keywords in Swift. But I like both using keywords for function arguments, and to be able to leave them out. What other design would you propose?
I don't think that adopting a syntax that looks similar to Smalltalk means it has 'adopted Smalltalk syntax'. I like Smalltalk as a language, I do think that Swift would have been better if it's runtime model was more similar to Objective-C and Smalltalk, but it's not fair to say Swift 'added Smalltalk syntax to address a special case'.

I do agree in general that Swift can benefit from a reduction of special cases, but my personal feeling is that Swift is getting more consistent every version (at least in the features that already existed). YMMV, of course.

> I don't think that adopting a syntax that looks similar to Smalltalk means it has 'adopted Smalltalk syntax'.

How so? That is literally what they did. The trailing closure syntax is Smalltalk keyword syntax, and so Smalltalk keyword syntax is used for that one special case. Which exists in addition to the non-keyword trailing closure syntax and the non-trailing (regular argument) closure syntax.

> but it's not fair to say Swift 'added Smalltalk syntax to address a special case'.

Can you explain how it is "unfair" when that is exactly what happened? Smalltalk syntax is really compact, there just isn't that much there.

Well, the syntax is same - the ideas are different. Trailing closure syntax is basically 'you can remove the parens if there are trailing closures'. Which, if you apply that to multiple closures, you get the syntax that also looks like Smalltalk. Which is understandable, since Swift keyword calling syntax was always 'keyword followed with a colon' since Swift 1, which was inspired by Objective-C, which was inspired by Smalltalk. But then that's not adding Smalltalk syntax in a later stage, it was baked into the language since day 1. That multiple trailing closure syntax - that syntax can come out from anyone knowing Swift without any Smalltalk knowledge, it's a logical next step. So it feels a bit weird to call it 'adopting Smalltalk syntax' since I don't think Smalltalk influenced that syntax any more than other parts of the language.
> Well, the syntax is same

Exactly. Which is why I wrote "they adopted the syntax", not "they adopted the ideas". It would have been really wonderful had they adopted the ideas, and it would also have made their marketing slogan "Objective-C without the C" actually truthful.

> the ideas are different

Yes, and that's the problem.

> Trailing closure syntax is basically 'you can remove the parens if there are trailing closures'

Not really, no. The parens are still all there, you just move one (original trailing closure syntax) or more (new keyword trailing closure syntax) of the parameters outside the parens.

Which is horribly inconsistent just by itself. I mean, the point of the parens is that they delimit the arguments. If you have parens, then the arguments go inside the parens. Except for this one...did I say one, I meant these two special cases where the arguments do not go inside the parens.

If you are going to have some of the arguments be outside the parens, why not just go all in and have all of them "outside" by just getting rid of the parens and be (a) consistent and (b) dramatically simpler and (c) readable?

> which was inspired by Objective-C, which was inspired by Smalltalk.

> I don't think Smalltalk influenced that syntax any more ...

Which is it?

Anyway, the point of the article wasn't that they consciously decided to adopt Smalltalk syntax. The point is that they bungled this so badly that they ended up adopting the very Smalltalk syntax that they initially rejected, and the reason they ended up adopting it was that they couldn't make things work reasonably any other way. Except that had they done this from the beginning, they would have not run into any of these problems.