4 ms·
> A few years ago that seemed to start to change. From my perspective, some of the features added to the language to support SwiftUI - specifically property wra
by ragnese 5y ago
> A few years ago that seemed to start to change. From my perspective, some of the features added to the language to support SwiftUI - specifically property wrappers and function builders - very much felt rushed and forced into the language based on external deadlines.
Yep. If anyone had doubts that the language was no longer the one Lattner designed, SwiftUI should've put the nail in that coffin.
Swift is an imperative, statement-oriented, language. In fact, I get kind of frustrated when writing Swift after spending some time with Rust: I just love writing `let x = if foo { 1 } else { 2 }` or `let x = match foo { ... }`, and it's ugly as hell to try to assign a variable from a switch statement in Swift, etc. BUT, that's okay- I'm almost sure that zero programming languages were written with my opinion in mind, and Swift is Swift.
But, SwiftUI is declarative, which just doesn't work with literally the entire rest of the language. So they added result builders and these weird, magical, annotations just so we can have a UI DSL.
Error handling is now inconsistent and annoying, too. The original approach was to use this quasi-checked-exception syntax where you mark a function as `throws`, and all callers are forced to handle the possibility of failure. The difference between this and Java's checked exceptions is that the specific error type is not part of the signature and therefore the caller only knows that they might get an Error, but not what specific type of Error.
They even have a mechanism for higher-order functions to indicate that they don't throw any of their own errors, but will re-throw errors thrown by function arguments. Clever.
Okay, fine. Pros and cons to that approach, some like it, some don't, etc, whatever. Except then they realized that this approach falls flat in some scenarios (async/promises), so we really just need to go back to returning error values. So they stabilized a Result type.
So, now we need to figure out when we're writing a function if we want to return a Result or make it throw. And, even more annoyingly, Result has a specifically-typed error variant! It's actually a Result<T, E>. Which is it, Swift team? Should immediate callers care about specific error types or not?
Just recently, they landed the async and Actors stuff. Async is fine and great, and is consistent with the imperative syntax and semantics of the language, and it even supports `throws`, IIRC. But Actors? What the hell is that? That's totally out of left field for the rest of the language.
I used to really enjoy Swift in the 2.x to 3.y days, but it really seems like it doesn't even know what it wants to be as a language anymore, which is a real shame- it had a real shot to take the wind out of Rust's sails, IMO (The number one "scary" part of Rust is lifetimes and borrow checker. Swift has CoW structs and auto-ref-counted classes, instead, which can be seen as more appropriate for higher level tasks than writing system libs, etc).
- honkdaddy 5y agoI totally agree. I've been writing varying levels of Swift code since the 1.0 days and I truly can't think of a time I've felt constrained by not having the actor keyword. The threading and DispatchQueue APIs are so rich and flexible already, I just don't really see the use case. Maybe someone who's worked more with them can chime in?
- skohan 5y agoI think the motivation for actors is that it's an abstraction which lets you write concurrent code with reasonably strong safety guarantees without adding as much cognitive overhead as a strict system like Rust. GCD is a really nice abstraction for async, but it doesn't protect you from shooting yourself in the foot if you're not careful about what thread is doing what. That said, I'm really not sold on actors. I think CS as a whole hasn't really landed on the right abstraction for concurrency yet, actors seem a bit experimental, and GCD is plenty good enough in most cases.
- ninkendo 5y agoA good critique of libdispatch is here: https://tclementdev.com/posts/what_went_wrong_with_the_libdispatch.html https://tclementdev.com/posts/what_went_wrong_with_the_libdi... I think the main issue with GCD is the potential for deadlocks in serial queues (which it doesn’t really help you with), and the related problem of thread explosion in concurrent queues. Actors make protecting shared mutable state really easy, solving a big chunk of the reason you’d want to use semaphores and serial queues in the first place. If you stick with async/await/Task{}/actors, you’re guaranteed to only have the optimum number of threads that can saturate cores, and won’t have deadlocks. If you use Sendable properly and heed all the warnings, you’re a good way towards the kind of “fearless concurrency” that Rust gives you. It really is pretty great IMO compared to GCD.
- saagarjha 5y agoI’ve been using Swift Concurrency for the last several months and while it has a lot of nice things going for it there’s still a lot of ways you can “hold it wrong” and get incredibly poor performance. If you block a thread in the thread pool you’re going to deadlock. It’s still pretty easy to create data races (possibly with mis-annotated or poorly thought out types). And logical races are absolutely still a thing, and I might even say they are more common because the messaging is that this’ll magically solve all your concurrency problems and it doesn’t do that.