As is tradition, I'm going to take the discussion one level up and talk about the language as a whole, although my issue is related to this syntactical change. The problem Swift has right now is that the review process is marred by huge conflicts-of-interests. The people reviewing proposals are very closely affiliated with, or sometimes the same people, that proposed it and want it implemented. And that's not to say they aren't smart, reasonable people; quite the opposite: it's just that they have their own interests when they're adding features to the language, and they ignore the community when doing so.
When initial SwiftUI changes came through, they were "proposed" by Apple engineers even though they already shipped in Xcode. Sure, they got a bit of feedback and change, but everyone largely knew they were going in regardless and they did. When callAsFunction came in (which I actually think is a useful feature, just spelled horribly) it was obvious that it was being pushed heavily by the Swift for TensorFlow people. And who reviewed it? Chris Lattner, who approved it even though nobody really liked the name at all. And with this case we see a fairly dubious benefit being pushed forward by the team at Apple, and everyone knows it's going to be for some sort of SwiftUI DSL thing, except they can't really tell us what it is and currently it provides a new the way to call a bunch of functions that is unergonomic and extremely special-purposed.
When people say that Swift is adding "useless features" they don't really think that Swift should stop evolving; it's just that they're frustrated that stupid things like these keep getting added and that the Swift Evolution community as a whole is unable to stop them from being merged in or modified substantially.
The multiple trailing closures case is also frustrating because they said "we're totally open to future evolution of this"...and then ruled out every proposed alternative with the "Principles" post.
I'd take it up a few more levels and ask about the elephant in the room: in what significant way is this language better than C#?
This outrage over closures reminds me of tabs vs spaces - it's one level above utter lunacy. Who gives a shit what the syntax is? Didn't we use Objective-C not long ago? Programming languages are a tool for me, not an artistic expression of text on a screen.
What has this language allowed me to build that I couldn't in Objective-C? Let's use empirical evidence - why do we have electron apps for something as basic as a chat app (Slack) instead of a native app?
We're going backwards in software tools as far as I can tell. Everything is more complicated and fragmented and Swift is a part of this mess, not a solution.
Sorry, carry on with expressing opinions about syntax and pointing out that those who pay for the juke box, get to select the songs and are hypocrites when they pretend otherwise and press 'Billie Jean' when most people wanted 'Thriller'.
> in what significant way is this language better than C#?
- direct compilation to native code with static analysis and optimization
- automatic reference counting (more predictable/consistent performance vs. garbage collection)
- good support for native iOS and macOS app development
- ranges
(But on an unrelated note, I have to say it's disappointing to me to find any language in 2020 (Java...) that doesn't support named and default parameters.)
I'm confused by your post. You first say the syntax doesn't matter, languages are just a tool, yet you say that Swift is part of the problem because everything is more complicated.
The increasingly complex syntax of Swift IS the problem.
We have furniture and some furniture uses screws to hold it together.
A furniture manufacturer decides to create a new line of furniture and introduces a new type of screw that requires you use a new screwdriver.
This screwdriver and screws are magical, they can magically alter themselves over time in small ways - they can change color, the handle can become rubberized or change texture, etc.
There's a whole group of people working on deciding how the screwdriver and screws magically alter themselves and which rubber material is best, which color is most pleasing, etc. They're so wrapped up in it, they never stop to ask, wait, why the fuck did this new line of furniture even require new screws and screwdrivers? When I compare the old line of furniture and the new, I can't tell the difference! It's the same shitty furniture!
We have multiple furniture manufacturers, why didn't we just agree to use the same screws and screwdrivers for all of them?
I don't get it - the people who are so passionate about screwdriver handles of one single manufacturer, to me, are a bunch of lunatics.
I dunno, but I find real assemble-it-yourself furniture to pretty much fit that description anyway.
I wanted some replacement shelf supports for a bookcase, and it turned out there are ones that are specified in metric dimensions and ones that are not, and they are like half a mm different, and so the only ones that the local big box store carried were just enough off they didn't work. Even with a hammer. And my drill wouldn't fit in the corner. So I ended up taking the appropriate drill bit and enlarging the holes by twisting it in my fingers, which hurt a bit but shockingly worked as it was a truly tiny bit of additional clearance needed and soft particle board.
It really was annoying in a rather similar manner as programming can be. The fasteners and tools to make cheap furniture should be standardized.
Electron is an economic choice, not a choice of engineering excellence. Make desktop apps that work on all major desktop OSes (actual write once, run everywhere) and also works as a web app with one kind of very ubiquitous engineer on one code base. Web apps are also very popular from a customer adoption standpoint.
Also Objective-C scares people away. Swift being a nicer Obj-C syntax alone has consequences of it's own. And pretty much nobody in silicon valley / startups / big tech other than microsoft uses C#. C# is anti-recruiting indicator in most engineer's minds.
I also think nullability typing in swift & all major libraries having proper nullability typing brings major benefits, I'm not sure if C# has that.
Sun Microsystems cursed the programming world with Java. And its hideous object oriented model.
I never thought Java would die. Then Oracle saved us all, and bought Sun. And they proceeded to burn down bridges, and destroy all the goodwill that Sun built up with Java.
Now, I can finally see it. The Sun is finally going to set on Java.
> The already weird-ish addition of Smalltalk/Objective-C keywords inside the f(x) syntax: f(arg:x)
I'm a huge fan of named arguments in Swift. It's one of the things I miss most when working with languages which don't have it.
For instance, I do a lot of work with computer graphics, and in that case you end up with a lot of functions with a lot of arguments for things like configuration. With named arguments, code becomes almost self-documenting. Without them, I just end up using comments in their place, but of course then I don't have the compiler making sure this is correct.
It's a totally optional language feature, and adds a lot of value in some cases.
Maybe it's shallow of me, but named/keyword arguments are now at the top of my list of things I want in a programming language.
For all the attention given to static typing and unit testing for increasing developer confidence, I think named arguments belong up there as well. Makes reading code so much easier, especially the way they are implemented and encouraged in Swift.
As someone stuck doing PHP for my day job, they could buy an awful lot of goodwill from me by implementing something like this in PHP (well, that, and separating arrays from dictionaries, but that seems far less likely to happen).
I like named/keyword arguments too but I think one needs to be careful not to overdo it, especially in statically typed languages. I think it's sort of a last resort when the call syntax can't be disambiguated by other means.
As an example imagine a search function that returns the position of a character `c` in a string `str` that looks like that:
int search(String str, char c, bool case_sensitive, bool from_end)
I'd say that I would avoid this signature in general, because those two `bool`s make for an awkward call syntax:
search("foo", 'f', true, false)
That's pretty opaque, even if you know what the two options are it's easy to forget the order. Now you might say "well see, that's where keyword parameters shine!" and you'd be right, it does help a lot:
But I'd argue that while this is good enough if you're coding in a dynamically typed language, it's actually sub-obtimal if you're coding in Rust for instance. Here you can do something even better: give these two options different types, this way not only it makes the code more explicit, it also means that it gets enforced by the compiler:
Now that's not to say that I don't want or like keyword arguments, I just think that it's one of these situations where "when you have a hammer, everything looks like a nail". I sometimes miss keyword arguments in Rust, but it's the exception rather than the rule for me.
I would question that your rust example is better than the named argument example. I think it's situational at best, and a workaround at worst.
For instance, even in the example you gave, it could be argued that the SearchCase type is really just a boolean which is being introduced to do the job of an argument label. If this enum is only used in this function call, that's going to add noise to the docs with fairly tenuous justification.
And this only works in the case of arguments which can be easily converted to enums. For instance if I have a signature which looks like this:
void search_buffer(int match, int start, int end, int stride)
It's going to be very difficult to look at the call site and infer what the meanings of these arguments are.
>For instance, even in the example you gave, it could be argued that the SearchCase type is really just a boolean which is being introduced to do the job of an argument label. If this enum is only used in this function call, that's going to add noise to the docs with fairly tenuous justification.
I don't really agree with this, I think in general it's a good idea to type your parameters as tightly as it's reasonable to do. I don't think that introducing a one-time enum is particularly unreasonable. I don't really get the argument for reducing noise in the docs, typically nowadays API documentations are hyperlinked document, not a big PDF, you either look the symbol up or you don't.
An argument in favour of using types here would be in the situation where the "case_sensitive" parameter is not a literal in the code but something that's being passed as argument from an other function or stored in an object. The names could be slightly different, the conventions could change from one part of the code to an other etc... With dedicated types you can be reasonably certain that if the types match then it's probably safe to pass to the function, with human-level semantics that may require more caution.
Of course my example is so trivial that it could be argued to be over-engineered, but consider a function that takes angles as floating point numbers vs. a dedicated "Angle" type for instance, with the float you have to make sure that the underlying representation is correct (degrees? radians? turns?) whereas a dedicated type would typically have well-defined semantics.
>And this only works in the case of arguments which can be easily converted to enums. For instance if I have a signature which looks like this:
void search_buffer(int match, int start, int end, int stride)
I agree completely with you here. Keyword parameters definitely have their use cases. I just think that they should be used parsimoniously and, IMO, can be a code smell if used too often, especially in languages with a decent type system.
You’re forgetting another important use case of keyword/named arguments: default values. In Rust either every call site needs to specify the default values, or you use a None for the default which is not at all readable.
Also, if Rust were to support named arguments (even better if with default values), you can use explicit types for your bools all the same. It’s not an either or situation.
Of course in the case of Rust this would mean potentially one type per function being added to your implementation. That’s a lot of boilerplate for a problem which is solved really well by defaultable named parameters
I agree it seems like a lot of boilerplate, but from what little I've played around with Rust, it seems they've already adopted strategies similar to this for instantiating objects with the builder pattern[1], but the nuts and bolts are abstracted away so the interface is slightly different.
If I understand it correctly, the equivalent builder pattern in Rust (which is essentially using methods to build a struct used to instantiate the object) would be:
let search = Search::new("foo","f")
.option_case_sensitive(true)
.option_from_end(false)
.build();
Where "new" returns a builder object/struct with passed in and default params, those options methods are optional and can change those defaults, and the build method actually builders the search object from the search builder struct with all the params.
I'm not sure how well this style works with simple functions, unless there's a macro or crate that can do most of this work for you from a a spec.
> Here you can do something even better: give these two options different types, this way not only it makes the code more explicit, it also means that it gets enforced by the compiler
Bool is also enforced by the compiler; adding ad-hoc types for things that have only two values is generally not something you should do without good reason. By the way, constructs like these are fairly common in Swift:
enum SearchType {
case literal
case regex
case extendedRegex
}
extension String {
func substring(matching needle: String, interpretedAs type: SearchType = .literal) -> Substring
}
In the absence of keyword arguments as a language feature, boolean arguments should almost always be avoided, and replaced by enums where possible. Boolean arguments are almost always inscrutable at the call site.
I still think there are wider use cases for named arguments. For example, even disambiguating between which argument is the "string" and which is the "substring to be found", or the classic "x, y, width, height" list in graphics programming.
But your point is well taken that, like many tools, you shouldn't abandon one set of good practices for another. There are certainly cases where a builder pattern or enum values are a better fit, which could be obscured as an option because keyword arguments "sorta kinda but don't really" address the same thing.
I see what you mean, but if that's the problem I'd argue that the weakness is that Rust lacks a shorthand to declare simple enums, not necessarily that it lacks keyword parameters. #[derive] soup aside I don't think too bad at the moment though, and I'm not really sure how you could simplify that without introducing too much magic into the language.
After all, even with keyword parameters there'll always be plenty of opportunity for one-off enums, if only for situations where more than two choices are possible. Take a typical "seek" function for instance, who can usually be performed relative to the current location, from the start or from the end.
That's why Swift does support unnamed parameters as well, and they can interweave with named ones. If the type of the argument is explicit enough, you can make the parameter name at call site optional.
Nitpick (I think this is just a miswording rather than a misunderstanding): it's not optional. You can have unlabelled args, but that's enforced too: the label must be included or omitted at the call site according to the function declaration.
I'm not sure that's entirely true. If a large proportion of people use a more advanced development tool, it sort of becomes indispensable, because people design the software to be edited with the tool, such that it becomes difficult to edit it without the tool.
For example if, today, I checked in code like performRequest(request, response, null, null, null) you probably wouldn't consider that good code, as it's not clear what the nulls mean there. Most people today would try to not write code like that. But if everyone was using an editor which displayed the parameter names, then it'd be fine. People would write that code, check it in, and so on. Which would mean that those people without the tool would have to confront such code.
I have long envisioned a language where these two things were tightly combined. The rough ideas are:
- All arguments are keyword arguments.
- Each function implicitly defines a struct type equivalent to its arguments.
I think if you combined this with some nice structural typing you can get a lot of the argument forwarding/builders and similar that people enjoy in dynamic languages without much/any overhead. Rough example:
A somewhat silly example but I think you can see the point. Of course you could also add language-level builder syntax to these structs and have something really nice.
I think the key here is that once you make the arguments a static type you can add loads of convenience like builders very easily.
This makes me wonder if there is some sort of relationship between currying and row polymorphism; that could be pretty sweet to capture succinctly in a language
Builders are nice when you actually need to build something with tons of optional arguments. The vast majority of cases where named arguments shine are function/method calls where builders just don’t make sense. Also, named arguments complement/augment the builder pattern.
You can actually have type safe buildrs in rust where trying to call the method that consumes the builder fails to compile if not all the arguments are set correctly.
I think technically it's affine types. In this particular case it comes from really nice generics - you start out with a struct that's generic over all fields - one generic bound per field. And that let's you implement methods depending on what fields have been set. So you could even make a builder with mutually exclusive setters. But you'd probably just want to use an enum in that case for the mutually exclusive options.
I still don't quite understand the generics magic required in order to not generate exponential amounts of code for it to allow random order of arguments, but I won't complain.
That capability would have come in handy on Java/Lombok.
> exponential amounts of code for it to allow random order of arguments
The key is that it only generates code for types that actually get used. So if you always call the orders in the same order, it won't generate tons of code. That said, you could actually tweak the type-safe builder to enforce a certain order for calling the methods on the builder.
Also, it's not the order of arguments, it's the order of method calls :)
Named arguments are great but should be used with care as they also create a reverse dependency. It prevents you from ever renaming the arguments in the future.
Accompanying saagarjha's point, you can freely rename the internal argument names, just not the external labels. Which is basically the same as changing the argument names in say C.
In the case of a library that would make this a breaking change you could add the new key rather than changing it, and support both while flagging old key usage with a deprecation warning.
Why would it? When you change the name of nth argument from foo to bar, compile will fail and then you just need to do a global rename. Is there any scenario where this would not be feasible?
The caller can't do a global rename if they don't own the source of the function -- because it's in a library. Which is what parent meant by "reverse dependency": the library's interface is constrained by the fact that it has users.
Surely if the library interface changes the users can change their own code? That's why we have versioning of libraries - so that users of the library can take intelligently new versions into use, and especially so they are aware if (and when) the interface changes.
It's really weird to me that this is (currently) the top-voted comment on this article. This little quip is not what the article is about nor does it represent the author's views.
Do you have any feedback on that article's thesis that the closure syntax could have been handled better?
Every programming language changes as it evolves and matures and people understand how to use it best or its domain changes significantly. Unfortunately, this always means accumulating these syntactic and semantical warts. Personally, I think Swift manages to keep a good balance, and it's a very powerful language, with lots of users and compatibility requirements, so it's much harder for them than for many others.
Alternative is constant redesign of syntax and behavior that just keeps making users unhappy with every release, breaking their code, libraries and obsoleting the docs.
If, to mitigate that, you try to make big changes to the language in big releases, once in a long while, you can easily get into the Python 3, Scala 3, Perl 6 fiasco and that can simply kill the language adoption.
As regards Perl 6, a recent development was to rename it to Raku, which (in hindsight) seems like a perfectly sensible decision. The overloading of the name Perl for a language that proved to be something different from Perl 5 caused trouble both ways. Perl 5 was widely considered to be a version that was to be eventually be replaced, whereas Perl 6 was considered a language that was a close and direct iteration of Perl 5. So we basically got the worst of both worlds. Perl 5 is very much in use today, still being actively maintained and is getting new features regularly, while Raku/Perl 6 is a beautiful new different language with interesting ideas being worked on by many brilliant folks, and the notion of trying to be in any way compatible with Perl 5 seems utterly irrelevant today.
The Python 3 changes aren't big changes.
They are breaking changes however.
I know they aren't big because Perl has had to deal with many of those same type of problems, only it didn't have to break old code to do it.
Perl 6 is more of a research project that took Perl 5 as a leap-off point. As it is is such a large departure from every language, it deserves it's own name. So it is now known as Raku.
I have followed the discussion around this on the Swift forums, and I'm actually of the opinion that the debate around this topic amounts to little more than bikeshedding and the proposed syntax for multiple trailing closures is fine.
I'm a bit perplexed by the author's position that Swift should just be small-talk as though this is obviously better.
As far as the idea of getting rid of parens entirely as the author suggests, I'm not a big fan of this approach. In languages like ruby where parens are not used, I tend to find this makes things a lot less readable. That might be a matter of opinion, but at the very least I think it can be said that Swift's approach to arguments, where only closures can be moved out of the parentheses of a function call is a perfectly consistent and defensible approach. For instance I don't think that this:
foo(someArg: 42) {
// do something
}
Is less clear than this:
foo doSomething: {
do something
}
someArg: 42
In fact I think it's probably the opposite, and the only reason I can imagine to prefer the second example would be out of some appeal to purity/simplicity of the function call syntax over any level of pragmatism.
As far as the whole debate over the inconsistency of the first argument label being removed while the following argument labels are required in multiple-trailing-closure syntax, I just don't think it's that big a deal. I also understand the consistency argument, that it should be handled the same for all arguments (i.e. the author of the function signature should explicitly decide if the label is required or not), and it probably should work that way, but I also understand why it hasn't been proposed that way for the first iteration. It would break a lot of code given how prevalent trailing closures are in Swift.
However there are already proposals in the pipeline to introduce an argument label in the first trailing closure (provided the function author hasn't named it `_`). I suspect in the long term this will eventually make it into the language, and automatic migration tools will make it possible to resolve issues with existing code without much fuss.
So in short I think multiple trailing closures are a good and natural addition to the language which provide real value, and the proposed syntax is absolutely fine.
Yes, the function definition must opt-in to accepting named arguments. To me that’s not a big deal, and if it means the same object/array spread syntax can be used everywhere for the same effect, it’s miles better than introducing new syntax for named arguments.
In my opinion, languages should be simple and small, relating on composition of core syntax features over introducing new syntax for each and every new stylistic fad happens to be popular this year.
> if it means the same object/array spread syntax can be used everywhere for the same effect
It cannot. Because, as you yourself said it: the function must know how to deal with object-as-parameters.
> relating on composition of core syntax features over introducing new syntax
This doesn't work for named arguments. They either exist in a language, and you can use them everywhere, or they don't, and you're left with inconsistently implemented workarounds.
It can - you pass objects to functions declared with named parameters, and you pass lists to functions declared with positional parameters. It is true that the function must be written one way one or the other a. priori (it could theoretically detect, but that’s not ideal).
And what I’m saying is that if they don’t exist in a language and you can instead using other more core things in the language to implement them rather than adding new syntax (complexity) to the language itself, that’s (in my opinion) a better language and the one I’d rather work in.
> It can - you pass objects to functions declared with named parameters, and you pass lists to functions declared with positional parameters.
Once again: these are workarounds and there are no "functions declared with named parameters" in Javascript. There are functions that accept objects as a parameter, and they employ additional code to work with those objects.
Example:
function add(x, y) {}
No matter how much you try, you cannot "use the core thing in the language" to make this function accept this
add(x = 1, y = 2)
// or
add({x: 1, y: 2})
without writing additional code inside the function itself.
So if a library gives you `function add(x, y)`, that's what you're stuck with, forever. You cannot pretend that named arguments can be solved by "javascript's core language". It cannot.
It’s really not as complicated as you’re making it out to be. If I’m making a function that has potentially confusing parameters, I make it accept an object. This forces named parameters, and it does so only using core language features.
Yes, there’s no dedicated syntax to allow people to name parameters that weren’t intended to be named. So what?
> It’s really not as complicated as you’re making it out to be.
Of course it's not complicated, it's as simple as I showed.
> If I’m making a function that has potentially confusing parameters, I make it accept an object.
It doesn't really matter what you, a single developer, do, does it?
If I have to work with a third-party library that has "confusing parameters", please enlighten me how I can make its functions "accept an object to force named parameters"?
> there’s no dedicated syntax to allow people to name parameters that weren’t intended to be named. So what?
I've clearly shown what in at least three messages.
The problem is that there is the Smalltalk/Objective-C style keyword syntax or the Pascal/C/C++ etc. function syntax. Both are consistent and complete ways of handling this, though I obviously think the Smalltalk way is better.
But it's either/or. When you jam them together, you get a weird mess of special cases and workarounds.
I would agree that the benefits of labels outweigh the messiness, even for the x.f() + labels case. So if you're coming from the x.f() side of things, I can fully understand and sympathize the view that they are a pure benefit without drawbacks. But once you have keyword messages, you no longer need the x.f() part of the syntax, because the syntax is completely self-sufficient. And then all the extra messiness of integrating the two makes no sense whatsoever.
As an example, the colon and the whitespace are sufficient to separate parameters. So is the comma. So we have...both?
There is also the issue that, please correct me if I got this wrong, the label that's in the signature becomes the name of the parameter inside the function. And as far as I can tell it cannot be changed/mapped. That's a horrible coupling of inside and outside.
Because of this, you actually need labels in your function declaration even for parameters that are unlabeled. Wut? And because of this, you need another special bit of syntax that says "despite the fact that I am now about to give a label, this is unlabeled". It also means that a name that is actually purely internal to the function is part of the signature. Wat?
And sometimes you get partial labels that just kind of sit there. It's really weird when you look at things closely.
I guess an analogy would be like a gasoline engine, a hybrid and a fully electric. Yes, the hybrid seems like an obvious improvement to the pure gasoline engine, if at some cost in complexity. But the pure electric car (let's assume a few more years improvement in battery tech) does away with all that extra complexity.
Hmm not entirely sure that analogy is all that good.
The way Swift does them is the least awkward and most robust I have seen in any language.
> As an example, the colon and the whitespace are sufficient to separate parameters. So is the comma. So we have...both?
Designing a language around what the minimum requirements of a parser is will not get you a good language. Human language is massively redundant, because humans find it more easy to communicate when there is redundancy. Computer code is also communication aimed at humans, and a bit of redundancy does not hurt there, and can often help.
> There is also the issue that, please correct me if I got this wrong, the label that's in the signature becomes the name of the parameter inside the function.
This is incorrect. You can optionally specify both an internal and external name for any argument. Unlabelled arguments are just a special case of this syntax, where you say "external name does not exist, internal name is this".
> There is also the issue that, please correct me if I got this wrong, the label that's in the signature becomes the name of the parameter inside the function. And as far as I can tell it cannot be changed/mapped.
You got that wrong. You can specify a separate internal name like this:
foo(externalName internalName: Type) { ... }
> you actually need labels in your function declaration even for parameters that are unlabeled. Wut? And because of this, you need another special bit of syntax that says "despite the fact that I am now about to give a label, this is unlabeled"
I mean you need to refer to those values inside your function as something and that has to be specified somewhere. How else would you do it?
Also the `_` you're complaining about is just a special case of the syntax I described above. This just means the external parameter name is omitted at the call site. It's quite consistent: if two names are given, one is external and one is internal. If only one is given, that means the external and internal names are the same (which is convenient since this is the common case).
> It also means that a name that is actually purely internal to the function is part of the signature. Wat?
Again, where on earth else would you put it? That's only also half true: the public signature does not include the internal parameter name. For instance I can declare a protocol like this:
protocol P {
func foo(arg: Int)
}
And both of these implementations will conform to it just fine despite the fact that the internal argument names are different:
Ahh, my bad! But only the "cannot be changed/mapped." part, right?
foo(externalName internalName: Type) { ... }
Doesn't this strike you as odd at all? The part that looks like the label is not the label, it's the name of the variable. And then there's this extra bit in front that doesn't look like the label, doesn't really look like anything TBH, that will turn into the label. If present.
I kind of see how they got there, but boy is it inconsistent.
I don’t know it seems fine to me tbh but maybe it’s a matter of taste.
It seems pretty simple to me: everything before the colon is defining the argument name. If you have one label, it means the argument label is the same as the name of the parameter used inside the function. If you have two labels, then the first one is the argument label and the second one is the variable name.
You could also think of it that you’re always specifying the argument label and the variable name, and in the case where they’re identical you have a bit of shorthand to save some typing.
Maybe it seems weird to some people if it’s not familiar, but I got used to it pretty quickly after starting to use Swift, and it felt like a natural extension of the type system and optional handling in terms of the language giving me tools to cross-check things and bake sanity checks into the source code itself.
I have never used a language which allow this, is-it really useful?
I don't think I have ever thought if only the external name was different than the internal name or used an 'alias' variable inside the function to 'hide' the external name..
> There is also the issue that, please correct me if I got this wrong, the label that's in the signature becomes the name of the parameter inside the function. And as far as I can tell it cannot be changed/mapped. That's a horrible coupling of inside and outside.
You can in fact provide names for both, and there at least used to be (I think they might have removed this?) some ludicrous special case on the behavior of the first parameter.
I think an IDE that would show the argument names and types when you mouse over a function would give a lot of the benefits of named arguments without putting the responsibility on the language to support them.
I think named arguments is an example of trying to solve issues at the language level, when the real problem is the information that is hidden from you while programming.
Swift's approach to this is pretty good. Arguments have a fixed order, and whether or not they are named at the call-site is explicitly decided by the author of the function signature.
In practice the order and identity of the arguments is quite clear in Swift.
As someone who kind of jumped into Swift coming from other languages, I still find this kind of stuff confusing.
Is there a good intro to Swift that goes through only what is unique/peculiar about Swift and explains that in a good way to someone that already knows how to program?
I don't know if you tried reading The Swift Programming Language book ( https://docs.swift.org/swift-book/LanguageGuide/TheBasics.ht... ). It's a fairly quick read and has brief explanations with simple examples. You can skip some sections if you have prior programming experience, but if you're confused about Swift's function arguments, you may want to skim even the basics since a few things like control flow (e.g. the switch statement) are unique compared to C/C++/Java/C#.
I don’t think named arguments are as much a problem as function overloading and optional parameters for this.
Without overloading you could define the answer as
g(x) { f(fst:x, snd:y) }
always which would make partial application somewhat ergonomic but makes allowing fst without snd horribly ugly when you have to pass in some spiritual null or _ to.
I think these ideas are fundamentally opposed and so I don’t think there’s a good way to have a nice syntax for both.
Yes, this can be implemented. And languages can trust the developers to not write stuff like f(3, fst:5). This is not a large scale pattern where people need help to refactor.
The one largest obstacle for partial application (at least of the Haskell style) is optional arguments. You just can not have both.
If you want optional arguments and partial application, you need some way to declare that the optional arguments are left blank. Haskell-style syntax has no such way.
But yeah, functors are all about building stuff like that. So there are similar things, just with a different syntax.
I would really hate to be learning Swift now. I started using it around the v2 release and I think it had nice syntax & was relatively small and opinionated.
Now there are so many ways to the same thing it must be a nightmare to teach. Each way of doing the same thing is in its own way nice, but they seem to just be accepting every half-way decent proposal for things they've already solved.
I feel like SwiftUI had brought a number of challenges that have massively complicated the language and you can really feel it when you bump up against things that have been tacked on to make SwiftUI work (which it looks like they are getting around to smoothing out now - multiple trailing closures is very a much a problem compounded by SwiftUI).
It's a shame, I loved this language from v2 -> v4, v5 was a good release but started to muddy the waters and now its just getting continually worse.
> feel like SwiftUI had brought a number of challenges that have massively complicated the language and
I've been a huge fan of Swift for years, and it's still one of the languages I reach for most for personal projects, but I was really put off by the way the rollout was handled for the features supporting SwiftUI, specifically property wrappers and function builders. Property wrappers in particular feel like a half-baked solution to paper over the lack of real meta-programming facilities.
I think it was especially disheartening the way these changes were sprung on the community after being shown at WWDC. It just made the promise of Swift being an open source project built through a community-based process seem like a bit of a farce, and made me a bit more apprehensive about investing in a language which might radically change in direction based on the product needs of Apple.
Swift is trivial compared to the rest of iOS/macOS/iDevice development. CoreData, nasty Cocoa/AppKit APIs, SwiftUI buggyness, all the legacy objc cruft, 99% of resources online being for iOS, not macOS.
Swift idiosyncrasies don't even put a dent in the learning curve imo.
I don't think anything gets this across quite like onboarding experienced devs into a client app that haven't done mobile client dev before. They'll ask you completely reasonable questions about Appkit/UIKit/CoreData like why is NSBox used here and you realize 90% of the time your best answer is just "well, that's just kind of how you learn to do it over time." Very few questions about Swift itself.
apple allowed this problem to happen. it's the same as allowing people to abuse SEO to get on the first page of results. apple told people that there is money in iOS and they went there.
we are the worse.
nobody is teaching you how to write a macOS app. heck, metal has been out for 6 years and there is zero book that will tell you how to set it up on the mac. if that goes on, metal will die because only few will use it. people are like metal is for library and framework and game engine builders. don't use it directly. yeah, and it is competing with and replacing opengl or vulkan. and people are using those directly. and there are bazillions of resources on those. kids are writing shaders in unity.
oh let me give some kudos to the apple programming guides because they deserve it!
i stopped looking for books and watching videos because of the iOS thing mentioned above. everything i needed was on developer.apple.com and i didn't know it. i started with the metal programming guide on my phone a few weeks ago. but that guide made it clear that i needed to understand the ecosystem first. so my detour took me through many others: the cocoa, the event system, the view hierarchy, etc.
these guides have been the highlight of the past few weeks for me.
they are better than most programming documentation out there. and i read them on my phone! i have 3 of them loaded in chrome on my phone right now. i still don't know what makes them so great but i intend to write about that at some point. one thing i figured is that they are light on code sample. reading code on the phone is really the worst.
sorry long comment, but yeah, swift is not the best thing. i am finding great joy in objective-c and what came before because i went through these guides.
I agree with you. When Swift was introduced it was billed as a modern replacement for ObjC that was easier to learn, even a good choice as a language for learning to program. I'm not sure it was ever that, but it sure isn't now. I just got back into a project written in Swift 3 and had to change a bunch of stuff to get it to work in Swift 5. Why? I keep hearing the language is going to be stable "soon" but that doesn't appear to be happening.
Swift appears to be a language for the captive audience of iOS devs, who are able to keep up if that's all they're doing. I don't really see it being adopted anywhere else.
To be fair, the pace of source breaking changes has slowed down considerably. From Swift 1 to Swift 3 there were many breaking changes between versions, and automated migration was very necessary. Between 3 and 4 there were some changes, and between 4 and 5 almost no source breaking changes.
I think it's actually fair for a new language to take a somewhat cavalier approach to backward compatibility. I would rather not be stuck with all the naive mistakes made in the first version of a language just to avoid migration.
> I just got back into a project written in Swift 3 and had to change a bunch of stuff to get it to work in Swift 5. Why? I keep hearing the language is going to be stable "soon" but that doesn't appear to be happening.
Swift 3 was released in 2016. Swift is ABI / source / module stable now, but that doesn't make it retroactively stable with a version released four years ago.
> I keep hearing the language is going to be stable "soon"
I think you may be referring to the promises of ABI stability? They kept promising that "soon" starting around Swift 3 (IIRC) and kicking the can down the road for a while there, but it is stable now on Apple's OSes. But I don't recall seeing promises of source stability back then; they were pretty open about the fact that the language was still evolving.
It's difficult to dip in and out of, you really need to be focussed on it to become productive. It's interesting to compare it to C# which I learnt by opening the IDE and writing code. There was far less to trip you up in C# despite it also being a fairly big language.
>I would really hate to be learning Swift now. I started using it around the v2 release and I think it had nice syntax & was relatively small and opinionated.
But the Swift team / Apple doesn't seems to be aware of it. ( At least a few internal Apple devs completely reject Swift for their project.) That is why I think Chris Lattner trying to make Swift scale from Javascript down to C was wrong from the start. And Swift will be like C# on Windows.
>And that's the frustrating part: this stuff was and is available and well-known. At least if you bother to look and/or ask. But instead, we just choose these things willy-nilly and everybody has to suffer the consequences.
That could be it, or that could be that these days any language that doesn't have a syntax vaguely resembling C on one hand or Python on the other it is considered niche or "weird". You'll also have an endless stream of people commenting (mostly from the Python side) that your language is noisy, because they won't take the time to understand its syntax and it won't read like pseudocode, and that's a bad thing for some reason. But I digress, because that's a pet peeve of mine.
Anyway, my point is that if Swift had Smalltalk-like syntax it would not have been nearly quite as marketable. The situation with Javascript is somewhat similar: under the hood the language is more like a Lisp than C, yet it awkwardly uses C-style syntax (and weird rules for automatic semicolon insertion, because that's more like peusdocode that way, and that's a good thing for some reason).
> Smalltalk-like syntax would not have been nearly quite as marketable
This was one bit I left out: with Objective-C, Apple had firmly established keyword syntax in a Tiobe top 3 language, albeit within square brackets, so there was absolutely no need to do this for "marketing". It truly was snatching defeat from the jaws of victory.
> that your language is noisy
Smalltalk is about as syntactically quiet as you can get.
Was not "marketing" one of the biggest driving factors of moving to Swift, though?
Objective-C ranked highly in usage, but largely because of it being necessary for iOS development.
At the same time, it was common outside of the Apple bubble for devs wanting to make something for iOS to complain about having to learn this "weird language with square brackets" that "only Apple uses", and "why can't we just use something like Java like we can with Android?"
With, of course, zero knowledge of Objective-C's Smalltalk roots, or its purported ethos.
I know it's more complicated than "square brackets bad", with all the C code mixed in, and memory release pools and all, but in general, Apple did still have a marketing problem courting outside developers to build native apps without resorting to "web view" wrappers.
I love Swift, I really do, but I would love something that feels more directly Smalltalk-esque even more. Swift decidedly feels like the goal was to make something that looked familiar enough to the C / Java / Javascript / C-like devs to get them on board, without tossing out some of the core benefits of the Smalltalk ethos. Which, apparently, means using C-like methods with parentheses at first... until you reach special cases where you don't.
Smalltalk is, IMO, a great example of how something can both be very expressive yet very minimal... but that inherently means ditching some very familiar syntax. As soon as you do that, you lose a lot of C-like developers (be it rightfully or wrongfully so).
Yes, this was after the Swift announcement, but the trajectory was in place way before that.
WWDC has been completely sold out for how many years? At first, it sold out in a couple of weeks, then a couple of days, then a couple of hours and finally a couple of minutes or even seconds. Then they started doing the lottery and even that was a while ago.
> but that inherently means ditching some very familiar syntax
Swift has tons of unfamiliar syntax. And you don't have to adopt Smalltalk as-is. Objective-Smalltalk, for example, has class definition syntax and uses curly braces. Those are not big deals. Keyword syntax, on the other hand, is a big deal, as the example of Swift shows.
Because you do want argument labels, and you do want to pass closures. But that just gets messier and messier.
To be clear I wasn't making the "noisy" critique myself, it's just a qualifier I've seen, in my opinion unfairly, applied to the syntax of certain languages in the past.
I think the problem is not whether the language is really noisy or not, it's more whether the language is familiar or not. Nobody complains that "(x + 1) / (y * 4)" is noisy, even though it's more symbols than numbers or letters. Everybody is familiar with mathematical notation.
I think deep down a lot of the "noisy" complaints are really about unfamiliarity, and that was my point: most coders aren't familiar with Smalltalk but most coders are familiar with C-style syntax, so it wins by default in the marketability department.
It's a bit like when people who are first exposed to Lisp say "oh my god all these parens!" or when Python-first coders have to code in C and say "wow, I can't believe I have to put semicolons everywhere". Yet when one is familiar with these languages the parens/semicolon kind of fade away because you see the structure behind the tokens, so to speak. I don't even consciously notice semicolons anymore, and I type them without thinking as naturally as I hit "return" at the end of a line. But if you're just starting it does take a while to get used to them.
The one thought about C++ Lisp completely ruined for me is giving closing braces their own line. Why do all my functions have to end with six newlines?!!??
Language platforms, especially on the front end, are chosen for the customer base you can deliver to for the most part, not because the language is great or horrible unless its egregiously bad.
It seems like every Swift-related discussion here eventually devolves into a debate about the design and evolution of the language. Okay, I'll weigh in.
Comparing Swift with other important languages I have used such as C and SML, I think what's missing in Swift is a philosophy that inspires, motivates, and guides its development. I think Chris Lattner described Swift as a modern, practical language. Those are general characteristics but not the basis for a real philosophy.
Others have described Swift as a "car crash" or "kitchen sink" language and I think those metaphors are apt. Swift seems to be trying to be imperative, functional, OO, and reactive all at the same time, with Obj-C compatibility thrown in for good measure. Complexity and intractability will inevitably follow.
I actually like the kitchen-sink nature of Swift. If I could articulate a philosophy of Swift, it would be that the language prioritizes expressive, ergonomics and safety (in the single threaded case) over everything else.
The fact that you have a kitchen sink of language features lets you carve out the language of your choice and write code according to whichever paradigm suits you as a developer.
In spite of that, it might be a matter of opinion but I generally find Swift code among the most readable given the nature of the type system, and decisions around named arguments and when and how type inference may be applied.
The issue is we don't work alone. There are different people, teams and third-party code we have to deal with. And their preferred subset of a kitchen sink language can vary wildly.
> The fact that you have a kitchen sink of language features lets you carve out the language of your choice and write code according to whichever paradigm suits you as a developer.
Well then the responsibility would be on the team to agree on a subset of the language and a set of design patterns to use. This is a process which happens in every language (with the exception of Go to some extent since it's so small and strict) but with Swift you have a bit more freedom over where you will end up.
With regard to 3rd party code, you're also free to chose libraries where you agree with the interface paradigm they've chosen.
I find this concern to be a bit overblown. In practice I've never really run into a case where somebody is doing something really exotic and inscrutable in Swift, and it's generally easier to understand than, for instance, a really esoteric Scala code base or a Rust project making heavy use of metaprogramming.
- Teams: Less experienced developers will use fancy features trying to be smart. Every subset of the language finds it way on Pull Requests I review.
- People change teams
- People change companies
- Third party code: It's not realistic to filter out libraries based on their use of the language. Who even has time to review that? Not to mention that high quality libraries are scarce enough that if you start to filtering out libraries you often end up with none.
And the code doesn't have to be "exotic or inscrutable" to be a negative. It just needs to be unnecessarily of high cognitive load for it to impact use of the language. I've seen Swift developers squint at Pull Requests trying to gasp what certain part of the code was doing. It ended up being needless sugar and in turn a bit of everyone's time was wasted.
That mindset of "just pick whatever part of the language suits you" is why C++ is what it is.
Agreed. My only experience with someone else's Swift project was discouraging, to say the least. I thought I understood the basic language syntax and semantics, but the source code I reviewed was almost incomprehensible.
The issue is that it's not even a good kitchen sink. Every single one if it's features is paper-thin, harmful or useless.
You don't need `guard` if your compiler disambiguates nils and assigns vars in if statements.
You don't need hundreds of hardcoded If<T1>, If<T1, T2> etc. types to support SwiftUI if your type system is actually designed to handle this. And you have proper expressions. And proper functions.
You don't need an awkward `some` keyword if your type system is designed properly.
You don't need callAsFunction because that's the compiler's job, not the programmer's. And can be replaced with any number of things, including (but not limited to) currying.
> You don't need `guard` if your compiler disambiguates nils and assigns vars in if statements.
guard solves exactly one problem, and that's the one where if you have an if-let it makes your code indented. The compiler has support for the things you mentioned.
> You don't need an awkward `some` keyword if your type system is designed properly.
Swift's type system did handle it "properly", the issue was that with the type system you'd have this horrible monstrosity of a nested type when you really only cared about some very minimal erased part of it.
> You don't need callAsFunction because that's the compiler's job, not the programmer's. And can be replaced with any number of things, including (but not limited to) currying.
callAsFunction solves the problem of a closure having associated state that you can query, making them more object-like.
> Swift's type system did handle it "properly", the issue was
> callAsFunction solves the problem of a
From the article :
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).
I tend to disagree with this premise. Swift, like any language, has some questionable features, but in general I find it extremely consistent and easy to work with. I think mostly what people point to as "extra complexity" comes down to right-sized tools for handling nulls in a correct way, and syntactic sugar which makes it possible for write interfaces which make it possible to write code very close to the level of intent.
When I read complaints like this I wonder if the author has spent almost no effort trying to learn the language and just wants to be able to sit down and write Go or C#.
I spent no effort learning C#. I just sat down and started writing it. Even though C# is a larger language than Swift and is getting larger.
I tried to write a small project in Swift and I really have no intention on ever going back to Swift. It's not a designed language. It literally is a kitchen sink of quite poorly thought out features and "we couldn't be bothered to design a solution, let's just throw in this patch".
What specifically gives you that impression? I'm only asking because I find that Swift is more conceptually coherent than most other languages I have worked with.
The impression I get of Swift is that it takes a bit more effort to get over the learning curve since it introduces some novel concepts (null handling, affine types, the protocol system) but mostly it makes a lot of sense and becomes very nice to work with after getting over the hump.
C# seems easy to pick up by comparison because it's basically like a Java dialect and you just need to map the keywords etc.
- guard is consistent with the priority Swift puts on explicit null handling. An optional is never transformed implicitly into a value. I actually think this is a very good thing and makes Swift easy to reason about in practice, even if it occasionally adds a bit of boilerplate.
- variadic generics are a hole in the language, I agree with you completely there.
- as far as the `some` keyword, existential types are also a concept in Rust and for good reason. With a powerful type system like these languages have, it's a distinction which has to be made in some cases. How would a better type system solve this problem?
- How would the compiler replace `callAsFunction`? Also the inclusion of callable types is consistent with the design philosophy of Swift to allow the user to express interfaces in which ever way he sees fit.
Honestly your criticisms read like you're trying to find fault with the language without making the minimum effort to understand why these features might have been designed the way they are.
I have to do front-end iOS work from time to time and it's surprising how bad auto-complete can be in Xcode with Swift even after all these years: still randomly sluggish, randomly stops working inside closures including completely losing syntax highlighting inside the closure, and even Cmd+clicking to see the definition is a hit-or-miss as it sometimes opens up a blank editor and just spins. I would have thought that Swift having a strict type system would have made the IDE experience better, not worse. Don't know if the grammar is too complex or if it is the bridging with ObjC stuff or something else that made it difficult to build a robust solution. Can anyone shed light on this?
My points of reference are rust-analyzer, or just VSCode with Typescript, both very snappy although of course the languages are very different. If anything I'd expect Swift to be closer to Rust experience-wise since they both have pretty strict grammars.
10 year iOS developer here. I don't have any answers for you (not a compiler expert either) but yeah, auto-complete of Swift in Xcode continues to suck in a lot of different ways. It's a shame because autocomplete in Obj-C was so good for so long (I'd put it up there with VSCode/TypeScript with some caveats), and is still pretty great, but seemed to get a little worse itself when Swift showed up.
I think it comes down to swift being a way slower performance language on the compiler side because of some bad early design decisions their compiler engineers will grumble about on twitter occasionally and xcode struggling with that fact. And apple underfunding their developer tools team that creates all of this.
Don’t think this will help you, but maybe provide some comfort in that you are not alone. I work in Swift and XCode everyday and still run into this problem occasionally. XCode still indeed will sometimes (seemingly randomly) lose syntax highlighting. It seems to be, at least sometimes, related to when you try to do something a bit too clever with closures (especially closures inside closures) and marking lots of changes at once, especially across targets. The biggest problem is that when the indexing required for syntax highlighting fails, there is no insight into why it failed and no way to force Xcode to re-index except by restarting Xcode. Building should re-index, but in practice this indexing is not as extensive as restarting Xcode.
I've found that Xcode's performance with autocomplete, highlighting, etc with Swift depends on how you write Swift. Here's how I generally keep Xcode/SourceKit happy:
- Limit interaction between Swift and Obj-C wherever practically possible. Swift headers for Obj-C functions are autogenerated by the compiler, which can act as a bottleneck depending on how much Obj-C is in your project, how often that Obj-C changes, and how often you clean the project.
- Avoid nesting closures deeper than 2 levels (probably good advice anyway).
- Explicitly state types where possible to avoid inference.
- Avoid excessively long chains of optionals and/or casts.
So generally, try to keep code clean and explicit, and when choosing dependencies opt for pure Swift options where viable. Avoid adding new Obj-C code except where needed (e.g. C++ interop) and if possible reduce Obj-C in the project to small, seldom changing bits.
I wanted to answer that Java has had decades and millions of dollars poured into its ecosystem to allow the very good IDE experience that we have nowadays. But then I saw that you listed rust-analyzer and I found it intriguing. I have a mixed mode project (35k of Swift, ~8k of Rust)[1]. Both are completely separate targets (Rust compiles to a static library), but in general I get much better & faster completion in Xcode than I get in VSCode w/ Rust analyzer. Now, don't get me wrong, I love Rust analyzer, it is leaps and bounds better than Racer but the completion experience in Swift is still better for me...
Well with one caveat. It only works well if the file that I'm currently editing is in the selected target (i.e. if I have a file Bottombar.swift and that is in InterfaceKit.framework, and I'm editing it while the selected target is ExtensionKit.framework, then nothing works)
> Java has had decades and millions of dollars poured into its ecosystem to allow the very good IDE experience that we have nowadays
A trillion-dollar company cannot create an IDE on par with JetBrains which has 1200 employees, 250 M revenue, and and has not one, but several IDEs and plugins for several languages (including dynamic languages, including support for multiple versions of a language, including multiple versions of runtimes for a language) that are superior to Apple's one single IDE for two languages.
It's not a "millions into Java" problem. It's an Apple problem.
Swift language evolution seems to be heading in a bad direction. This is another change that adds nothing but extra, obfuscating complexity to the syntax.
Now you’ll have to read and write yet another syntax variant, with slightly scrambled separators and brackets.
I enjoy reading Marcel's Swift contrarianism (even if I don't always entirely agree with it). I work with Swift and ObjC on a daily basis. I remember the entirely appropriate skepticism that we in the Apple ecosystem had for ObjC when it was the main language. And I'm getting more and more tired of that healthy skepticism having turned into pointless bashing, accompanied by tall frosty pitchers of Swift kool-aid.
No language is perfect, and it's important to be cognizant of the tradeoffs and flaws. There's no denying that Swift is a powerful language in many ways. But there's also no denying that the chosen means to that power has costs. The most obvious is a lot of complexity, both essential and non-essential. Which brings us back to the article and the peculiar way multiple trailing closures ended up being integrated.
Like the author, I also wouldn't mind a Smalltalk syntax flavor for the whole Swift language. ;-)
Though having used Python a lot, I've also become something of a fan of braceless, indented syntax, which I find to be clear and conserving of vertical space.
But I do understand the main argument for making it C-like: they wanted to make an easier transition for Apple developers who were already using Objective-C, and others who might be coming from Java/C#/C++/JavaScript/et al.. Also Smalltalk's syntax, while simple, orthogonal and versatile, simply never caught on the way C's did.
Arguably, the new syntax makes the anonymous trailing closure the special case now. Having done a bit of Ruby in the past I can see why it might be reasonable to have both.
I checked Swift out really early on in its lifecycle, and, ignoring all the bugs, what immediately turned me off was this sense of to many features, even then. Not that a language having a lot of feature is bad - I use Rust for heaven's sake! - but there didn't seem to be a consistent purpose or logic behind it. I just got a really bad feeling in my gut about how the language would develop.
Hashmap literals are the right approach to named arguments and functions-as-control-structures IMHO. Ruby does this too IIRC. It's great because it re-uses existing syntax and semantics instead of adding edge cases. You can even add syntax sugar like you just demonstrated.
After spending a lot of time with Scheme (which is both relatively clean, and strong at syntactic extension), but doing iOS SwiftUI work at the moment, some of the Swift sugar is a reminder that Swift is a very different language.
But I'm not above playing with sugar, including Smalltalk-inspired ones. In Racket, as an exercise, I implemented a straightforward way to do Smalltalk-like syntax for procedure calls.
If the head of a list expression is a symbol that ends in a colon, then it was assumed to be the Smalltalk-ish procedure name sugar. A syntax like this:
(foo: bar from: (+ 1 2) to: (funky 3))
would be translated by the reader to this procedure application:
(foo:from:to: bar (+ 1 2) (funky 3))
You'd probably define the procedure in the usual Scheme way, like this:
(define (foo:from:to: source start end)
...)
Of course, I wouldn't use this complicating sugar in real code without a good reason.
The trailing closure syntax is used in Groovy too. It is very useful for creating builders that look almost declarative, but provide access to flow control features in the language.
The closure obsession in Swift is really off putting. The amount of indirection in the language is frustrating and seems to be there due to an FP zealot somewhere in the language design team at Apple.
When initial SwiftUI changes came through, they were "proposed" by Apple engineers even though they already shipped in Xcode. Sure, they got a bit of feedback and change, but everyone largely knew they were going in regardless and they did. When callAsFunction came in (which I actually think is a useful feature, just spelled horribly) it was obvious that it was being pushed heavily by the Swift for TensorFlow people. And who reviewed it? Chris Lattner, who approved it even though nobody really liked the name at all. And with this case we see a fairly dubious benefit being pushed forward by the team at Apple, and everyone knows it's going to be for some sort of SwiftUI DSL thing, except they can't really tell us what it is and currently it provides a new the way to call a bunch of functions that is unergonomic and extremely special-purposed.
When people say that Swift is adding "useless features" they don't really think that Swift should stop evolving; it's just that they're frustrated that stupid things like these keep getting added and that the Swift Evolution community as a whole is unable to stop them from being merged in or modified substantially.