I'm probably very biased, like I mentioned, Swift was my first language
I absolutely didn't learn everything with just Swift. I learned ownership with Rust, functional programming with Haskell, and Zig was very helpful for figuring out low level code — I think every language had something valuable, I guess C++ just wasn't very intuitive for me personally.
Thank you for the link, I understand how protocols work and that some is just sugar, their motivation, that this is a limitation of protocol implementation, etc. My point is that some and generic types are not better for readability, comparing to existential any.
protocol Service1 {}
protocol Service2 {}
protocol Service3 {}
class MyClass {
private let service1: Service1
private let service2: Service2
private let service3: Service3
init(service1: Service1, service2: Service2, service3: Service3) { ... }
}
This is a piece of code from my project. It doesn't matter whether you rewrite it with generic types or existential types, it anyway will be more verbose than the original version:
class MyClass<S1: Service1, S2: Service2, S3: Service3> {
private let service1: S1
private let service2: S2
private let service3: S3
init(service1: S1, service2: S2, service3: S3) { ... }
}
let view: MyClass // Cool, now this is not valid, I need to MyClass<...., ................, ......................> everywhere I use it
or
class MyClass {
private let service1: any Service1
private let service2: any Service2
private let service3: any Service3
init(service1: any Service1, service2: any Service2, service3: any Service3) { ... }
}
Moreover, using generic types here is now what I want. If I wanted efficiency, I would not use protocols here, simply concrete types to get direct dispatch and potentially inlining, this is faster than some and <S1: Service1> (correct me if I'm wrong). The only possibility to drop all these unnecessary verbosity is using base classes, which again limits flexibility... So no matter what I choose, anyway code becomes less compact and less readable than before.
although i haven't had time to do a full reinvestigation, my gut feeling is many of the suggestions in that article could reasonably be considered "workarounds for quirks of the Swift 5.5 compiler" today. it is also quite tailored to what Swift PNG does which is do a lot of signal processing on large buffers of data, which is atypical of most applications.
in general, i've found following these mantras to be more productive than spending a lot of time squeezing the last few cycles out of a ManagedBuffer:
use the right file format
use the right DB schema
run the code in the right place
deploy the application to a server in the right geographical location
but that is hard to codify into instructions for a beginning engineer to follow.
I would say it is more C part of it that helps to get your head around some low-level ideas that might be hidden or unclear, now of course there is Rust, for instance, that can do maybe even better job. C++ is indeed introduces a lot of noise (especially in latest editions) that are confusing to the newcomers of the language, earlier editions actually were much less confusing at the beginning (don’t think parallel with the Swift here can be true).
Swift will devirtualize generic functions when it has enough information to do so. That’s Swift’s “have your cake and eat it too” approach to generics.
Flexible is not necessarily what you want. Any is flexible, it can literally be anything, but is that going to be correct? If your types are not constrained enough then you can just pass nonsense data somewhere and who knows what will happen. Types aren't just for efficiency, they exist to enforce as much correctness as possible statically. It's far better to get a compile error when code inherently doesn't account for all possibilities than some runtime exception.
If you can replace existentials with generics then they weren't dynamic anyway and so weren't doing anything useful — and If you can then still replace those generics with concrete types and it leads to less code rather than reducing duplicate code, which generics exist to do, they probably had no business being generic anyway
Comparing your examples by "verbosity" is not correct, they express something completely different.
For instance private let service1: any Service1 does nothing, you're erasing type information with no benefit as you can't reassign a let.
I recall that as well, though I don't think it's exactly the same thing. Objective-C/Cocoa was a pretty well-established/proven environment by that time, having gone through a decade (and a half, in case of Obj-C) of development and some level of standardization with the OPENSTEP initiative. So you're talking about two well-established paradigms with an almost equally-long history, and also mixing in OS-level issues, such as separate address spaces/processes, which was also long-established in the context of UNIX and kind of a no-brainer for most people (perhaps with the exception of old-school MacOS Toolbox programmers—I'll take your word for it). Again, perhaps I'm just getting too old (49 going on 50), but it doesn't seem like the same thing in my mind. From the outset, Swift always kind of seemed to me like just another programming language at a time when that was pretty much the last thing the world needed.
I have no insider knowledge on this, and not to disparage anyone, but had Chris Lattner had not been working for Apple at the time, I highly doubt that they would've bet the company by adopting his personal hobby project as their new language of choice. I feel like Apple's direction was likely swayed in a major way by two people who subsequently left the company (the other being Dave Abrahams, who is also known for having a major influence on modern C++).
I also recall from that time that it seemed like everyone not coming from the NeXT camp was adamant that Objective-C was too odd and not viable as a language for commercial software, and that Java would be a much better choice. To the point that Apple acquiesced and pursued the strategy of supporting Java-Cocoa interoperability for several years, until everyone realized that Objective-C was actually a pretty nice language, and the memory management/safety concerns relative to Java were largely overblown. In the subsequent decade and a half, perhaps largely driven by the popularity of the iPhone, tons of newcomers realized that Objective-C was a decent language and came up to speed quickly. I feel like Swift is just the new Java in a lot of ways, perhaps created because Apple couldn't control Java (and Apple likes to control its own destiny).
I first approached Obj-C myself circa 1998 and found it to be very interesting and fun to learn. I had no real knowledge of Apple or NeXT programming at the time, but I had become very interested in OPENSTEP/Rhapsody and the idea of a UNIX system with a decent GUI after learning of the Apple-NeXT acquisition in '96. I was coming from a background of having learned a little BASIC by typing in games on the PC XT in the 80's, learning FORTRAN 90 as part of my intro to Engineering classes in college, then teaching myself C and LPC (which evolved into Pike, another undervalued language IMO) in order to build MUDs in my free time, and later learning Scheme (one of the most "fun" languages I've used, for whatever reason), C++, and MIPS assembly in my Computer Science classes after changing majors. One thing that I liked about Objective-C was that it was a "small" language like C prior to C11 (small core syntax and runtime), and even Cocoa and the C standard library were pretty small compared to what we're faced with today in languages like modern C++ and Swift. I later taught myself Smalltalk after falling in love with the Obj-C messaging syntax, and wished that Smalltalk had won the OO wars. I had even built a working Cocoa-Smalltalk bridge at one point so I could write macOS apps in Smalltalk. The named-parameter scheme adopted by Swift (and others) is a really ugly compromise, and one of things that just never sticks in my mind, what with the optional label, and with the Pascal-style declaration syntax (which one is which—the label or the parameter name? I always have to go look it up after spending any considerable time away from the language).
As I vaguely recall, even a few years before @Chris_Lattner3 himself thinks he started noodling on Swift (2010, IIRC) there was a real appetite for a new language within Developer Tools at Apple (dunno about the rest of Apple - I think for most of them it was more a desire for "a faster horse" than a car, because they had their own worries and programming language was only a small part of that… I was actually part of Developer Tools, though Performance Tools specifically, and regarding programming languages mostly what I wanted was a better Objective-C compiler… kinda like how now I mostly want a better Swift compiler ).
…well, and for the idea of putting GC in Objective-C to go away.
Remember that circa 2005 through 2010 there was real work done to evolve Objective-C. I'm sure there were many reasons, though the only one I specifically recall was essentially a perceived PR problem - that Objective-C was considered "weird" and "old" by the world outside Apple's direct influence (even despite the clear success of the iPhone app ecosystem).
In any case, once it became sufficiently probable that a replacement would be more viable & effective, something like Swift was kind of inevitable. Irrespective of whether Chris Lattner was there, or any other specific individual, I doubt we'd have ended up with something radically different. And it was inevitable that something would supersede Objective-C, for better or worse. The "peer pressure" along would have seen to it.
That was very complicated. Not just technical considerations but politics.
And product design.
Keep in mind that Java back then was - though as old as Swift is now - still pretty clumsy compared to Java today (which ain't no masterpiece, either). I don't actually recall memory usage being talked about much - honestly, I don't know anyone that took the Java stuff seriously, within the Apple sphere - but just think about how bad the JVM still is in that respect, and now take three quarters of the RAM out of your Macs (along with the SSD - remember when swapping meant something? ).
I'm very glad that Swift ended up being Apple's path, rather than Java, at least if those were the limits of our choices. For all the warts - and Swift genuinely has some, like any language, as this thread has covered - Swift is really better than Java. As in, I'd be using Windows right now instead of Macs because I'd far prefer C# to Java.
Well, Kotlin is the new Java, really. A little hindered by the limits of the JVM and Java compatibility - but then arguably so is Swift, with its Objective-C compatibility.
It is interesting to muse about what would have happened if Google had adopted Swift instead of Kotlin [for Android], as was rumoured. Swift might really have become the new Java, in the sense of being pervasive. Still might, I guess.
I still think every and any programmer benefits from learning at least one Lisp-family language. I too learnt Scheme, and though I'd never do "real" work in it, it really expands your programming mind.
In fairness, every language has things like that - or if not in the language itself, in the essential libraries, which is effectively the same thing.
Heck, Unix is full of things like that. ln, for example, is existential proof of the quantum uncertainty principle, because only once you invoke it does the waveform collapse (and tell you that you got its two arguments in the wrong order). It's the software equivalent of USB-A plugs.
I think it's not so much the "small" that's important as being well-composed, and therefore intuitive.
In that respect Python is actually a close relative to Objective-C. Maybe Go, too, to a lesser degree. Those languages are more like triangles than the trapeziums of C++/Swift/etc - a relatively tiny core language and/or library on which everything else is recursively built, rather than a broad set of parallel, disjoint features.
There also weren't a lot of holes in Objective-C, just like there aren't in Python. Take for-loops, for example. Anything that's iterable can be for-looped in Python, including meta-level stuff and type information. Likewise in Objective-C, for the most part. In contrast, Swift only very recently and only just barely gained the ability to for-loop over variadic generics (and you still can't loop over tuples).
Other examples including argument splatting and argument handling in general - very early Swift had ideas about argument lists being tuples (or arrays, I forget) but abandoned that. Which might have had good reasons - maybe it was even the right decision - but it's nonetheless unfortunate that we now have these unintuitive disconnects between core language features.
There's a lot of things in Swift which don't play together well, if at all. That makes it unintuitive and seemingly inconsistent. I think that's really what people are hitting upon when they talk about the language being complicated.
It reminds me with one of the 'golden rules' of making video games - if it looks like you can do something, you should be able to do it. A lot of Swift doesn't follow that rule (which manifests most often & obviously as people seeking help or whinging about compiler errors, but bad compiler diagnostics is more a symptom or side-effect, than the cause).
Good point, thank you. However, I guess this is not about "it work as fast as direct dispatch", but more about "it may work as fast as direct dispatch in some cases, but usually works slower", and using concrete types is a more stable approach to gain efficiency in terms of calling something.
I wish there were reserved optimization cycles to improve compiler stability and compilation speed. I've spent too much time in slow compilation and restarting Xcode to get language server back on track.
I like using extension to split type implementation, but it does not compose with inheritance.
No, this is what I want. I want flexibility like "it should be anything that has the interface described by Service1", no matter it's a struct, class, enum, or protocol. Not Any, not "it should be anything conforming to Service1 protocol", not "it should be that class named to Service1". I want to write code that says "something having this public interface", in other words, something that can respond to all the methods declared by type Service1, nothing more. How to achieve this?
Yes, that's the problem with any in Swift. Instead of focusing on correctness, as you say (and so do I), I'm meant to choose between "slow bad any" and "faster some", and write boilerplate to always remind myself, that "here is slow type erasure any, don't forget it". And that's what confuses me in new versions, Swift 4, for instance, allowed me to not include information about type efficiency in my method signature and focus on correctness.
Saying "verbosity", I mean that they express to someone who will read this code additional useless information, noise that they don't need to understand the logic. I know that they express different things. I just say that I want to not express any of these different things, and Swift 4 allowed me that.
Maybe I'm wrong, really. You seem to have enough details about my example to choose what to use in this class. What you suggest to use in the example to achieve needed correctness and at the same time to keep code simple and compact?
It's not information about type efficiency, it has everything to do with correctness, which is what I was saying. any can be changed at runtime to an arbitrary different conforming type, some can only be changed to the same concrete type.
In your very abstract example where you're putting it in an immutable value it's only inefficient, but that is not always the case.
protocol P {}
struct A: P {}
struct B: P {}
var x: any P = A()
x = B() // Correct because x can be anything conforming to P *at runtime*
var y: some P = A()
y = B() // Error, when we assigned A to y it inferred `some P` to be A at compile time
With any the type can change from under you and if you make assumptions that it does not in your code, it might break. I only mentioned it because you seem to be treating these interchangeably or like some performance optimization, which is not what they are. Swift didn't allow you not to express this before, it just defaulted to any.
It sounds like you're trying to use protocols for object oriented code, and while Swift can do that, most of its features are designed with more functional code in mind. Protocols are more Haskell typeclasses than interfaces.
protocol Service1 {}
protocol Service2 {}
protocol Service3 {}
class MyClass {
private let service1: Service1
private let service2: Service2
private let service3: Service3
init(service1: Service1, service2: Service2, service3: Service3) { ... }
}
This is the example. To migrate to Swift 6, I would need to rewrite this piece of code somehow. Either add any everywhere I mention Service1, Service2, Service3, or make them generic types of MyClass, or turn these protocols into base classes and inherit implementations from them. As far as I know, there are no other variants.
My point was that any of these variants makes code less readable (more boilerplate code comparing to the previous version) and more verbose because of adding information about real implementation or adding unnecessary type constraints (in case of generics, MyClass users don't need to know these types to work with an instance of MyClass, but they will have to). And that I personally don't like that earlier, with Swift 4, I could focus only on business logic, but now I need to mix it with implementation details when I don't want/don't see a reason to do that.
This won’t be required in Swift 6 by default. But that’s not main question, since Service and any Service here is the same thing, just second one is explicit. And if there are any performance implications you are concerned using existential, it is fine to use any (especially if alternatives are unavailable or fit less). It is good to prefer generic/some (since generics itself has a viral tendency) if possible, but still if that is not a critical part, you it won’t have performance implications.
Yes, that's right. I want to be able to write abstract, high-level code in OOP style since Swift supports it. Swift 4 allowed me to write such code, Swift 6 breaks this ability for some considerations, which I subjectively deem as an example of inconsistency. And this is why I don't like where Swift moves.
I don't think Swift ever truly was a good object oriented language, from the very beginning it was focused on value semantics and making generics accessible.
There was a really well written wwdc 2015 session 408 titled "protocol oriented programming" about the design of the Swift standard library and writing abstractions without adding classes, but Apple since removed it from the website. You can probably still find it if you search for it on the internet and scroll a little.
Swift is not verbose and can infer generics most of the time when written in an idiomatic way. I can't tell you what that would look like in your case from your example as it's too simple.
If you just prefer a different style of programming it makes sense that you'd find the direction unappealing.