[0] Yes, I'm aware of the staffing issues. No need to beat that dead horse again.
[0] Yes, I'm aware of the staffing issues. No need to beat that dead horse again.
- Go aims to be simple enough for many engineers to be productive with little investment.
- Rust aims to be extremely reliable and efficient.
Swift is very complex, and makes many compromises away from reliability or efficiency in favor of application use cases.
Swift may fill an interesting role for a "native C#" that is mostly reliable, mostly efficient, and somewhat productive, but on the other hand, C#, OCaml, Java, Kotlin, and so on already have various answers to that, and they're only becoming better at it.
The only real advantage it has (as far as I can tell) is iOS (and TensorFlow almost as a knock-on of being the only serious language for iOS).
Fair point, Swift will never be as predictable or efficient as Rust (not a negative perse, just different goals). But I disagree on the complexity re: Go. What about Swift gives you the impression of extreme complexity? Go is definitely comparatively simpler but I do not think it is so much so as to put it in a different league.
Also swift as a language doesn't scale well. There are a lot of bottlenecks in the build process that doesn't let you scale simply amongst many cores like you can with C++, Obj-C & C and probably many other languages too. You're also effectively limited to xcode & apple desktops, so you can't go rent out a 100 core build server on AWS for builds like you can for every other platform out there, not that is matters much yet with swift's build scaling issues.
Also stuff like basic debugging often just... dies.
The more I work with a badly scaling language the more I appreciate a design decision like go made with building fast. I hope with generics in go v2 the boilerplate should reduce a lot.
The debugger is still rough, true, but improving. Apple just isn't spending the resources needed to bring lldb up to a great experience in a timely fashion.
Swifts build system can split off enough threads to consume all of your CPU cores, and batch mode did improve things, but it's not actually doing so efficiently. There is a trade off between total compute time used and number of threads used. In compute time consumed, swift is the most efficient when it's single threaded in WMO mode, but this means you can only compile in parallel with separate modules that don't depend on each other. And even then it's not a very fast compiling language itself, multithreading issues notwithstanding
Maybe something has changed recently, but as far as Xcode 12 goes, I haven't noticed much of a difference. I last checked deeply with swift 4.
More details here: https://github.com/apple/swift/blob/master/docs/CompilerPerf...
Combine (Apple's Rx framework) is pretty understandable/usable as well.
When done right, when you actually need the observer pattern, and use it as an event queue I'm sure it's probably amazing though.
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
BUT it doesn't change the fact that Rx is hard to implement correctly and is often misused when you don't need the observer pattern.
Swift supports LSP now, so you can use VS Code if that's your thing.
for i in 1..<10 where i % 2 == 0 {
print(i)
}
But you can immediately understand how it works, because it's consistent with the rest of the language and reads well. for my $i ( 1..(10-1) ) {
next if $i % 2 == 0;
print $i;
}
Here Perl's post-conditional syntax (which works only on a statement and not a block so it's more manageable) combines well with "next" (Perl's version of "continue") to very clearly convey intent without a lot of extra boilerplate. An additional clause is often just an additional line. Comments after each can provide additional context as needed. I think this is a more flexible way to accomplish the same thing, and more readable in all but the simplest of cases. And for those simplest of cases, I would use a grep, which is also useful in other parts of the language: for my $i ( grep { not $_ % 2 } 1..(10-1) ) {
print $i;
}
Any syntax learned that works as a special case to only a single structure is either a case of missed opportunity or a case of extra syntax that needs to be learned which shouldn't be, IMO. switch i {
case 1...100 where i % 2 == 0:
print("\(i) is a small even number")
case ..<0:
print("\(i) is negative")
default:
print("I couldn't care less")
}
Or generic constraints: struct Vector<T> where T: Numeric {
// ...
}
If you so wish, you can always use any of the functional constructs as well, as in for i in (0..<10).filter { $0 % 2 == 0 } {
print(i)
}One of the biggest criticisms that Perl gets is that there's so many different ways to do things, and that's somewhat deserved, so the question is what does "where" offer that "filter" doesn't other than another keyword and concept you have to learn to understand the language? What's the point if it's not really that much more expressive or clear?
> struct Vector<T> where T: Numeric
Is this really the same thing? This seems like a case where the same word is used to mean something else, even if they are loosely conceptually the same.
So, feedback, from someone whose exposure to Swift consists basically of this article but has eaten a metric crapload of many other braced languages over the decades.
What I can tell is that the programmer wanted to print 2,4,6,8. So the intentional readability is there. However when it comes to understanding how it works (i.e. the language mechanics delivering the intuitive reading) I had lexical concerns. I couldn't immediately tell whether the where-clause acts like a guard predicate in the for-statement or an unbraced if-statement i.e. equivalent to
for i in 1..<10 {
next if !(i % 2 == 0)
print(i)
}
or for i in 1..<10 {
if (i % 2 == 0) {
print(i)
}
}
or even (let's hope not) if (i % 2 == 0) {
for i in 1..<10 {
print(i)
}
}
and in a similar vein the lexical scope and binding of i was not clear to me; some languages bind the iterated variable at the level of the statement, others inside the iterated body (which may even be a closure for all I know), and this has implications particularly for the the value of i after the loop, for the binding of i in any function generated inside the loop (example: print could actually talking to an IO object that defers output by prepending lambdas to a chain - I've seen web containers deferring view rendering this way), for name masking, and for the consequences of any flow-control/continuation/exception mechanism that may cause an early loop exit.I do not know Swift's scoping rules and having read through the introductory guide and the flow control chapter I still can't tell you if the loop variable remains visible after the loop, and what it is bound to if so.
No doubt that if I sat down with a REPL or an IDE it'd hopefully be evident by example within a few seconds, but the claim was about dry-reading, not interactive experience.
I'll leave it there with an admonishment against making assumptions/declarations about the obvious-ness of something you're already familiar with.
So I'm not actually calling Swift a major offender, but noting generally that I don't there's ever been a language in which the intuition gap between intention and mechanics was small enough to be irrelevant.
(something at the back of my mind is now whispering "Scheme", but too quietly to be taken seriously)
Is there a case to be made for other behavior? Or an example of a language that intentionally treats it any differently? (Unless you go out of your way to iterate over a variable you’ve declared in an outer scope in C, but then that’s not really a for-in construct.)
for (const i in [1,2,3,4]) {
alert(i);
}
alert("i is now " + i); // ReferenceError
(Not that JS is exactly known for consistent and predictable behavior on edge cases, of course...)Python I don’t have experience with, and it appears I stand corrected on that front! I wonder how intentional it is, and what sort of use case it enables (and if it’s considered good practice to use).
RangeInclusive::new(1, 10) .filter(|x| x % 2 == 0) .for_each(|x|{ println!("{}", x); });
(though you could write it the same way, for i in 1..=-10 { if .. { .. }}
(1...10).filter { $0.isMultiple(of: 2) }.forEach { print($0) }
Do you mean the use of “\(variable)” for string interpolation? What’s wrong with that?
Using backslash has the advantage that there’s only one ‘special’ character in strings, but of course, also has the problem can escape any character with a backsplash, except for opening parentheses.
Which for is for and would work as for for?
I think D has to be this idea (or disease depending on who you ask i.e. Go is based on the idea of only keeping the simple bits) taken to the extreme - you can quite happily write Java in D but also implement a Java in D all the way down to the metal (merrily metaprogramming all the way down)
So can Perl.
Or the internalization of the managerial imperative for cheap labor, take your pick.
Companies prefer a churn of cheaper juniors happened.
Don’t get me wrong, I love swift but I think that for what you’re talking about constraints are necessary because we naturally shift towards more complex ways of expression.
Dart is the closest I've found to optimizing the surface area of a language while maintaining flexibility like ObjC.
If anything, that's bad, because it invites projects to adopt a impoverished subset of the language that avoids those features, in which case they might as well not exist because you aren't allowed to use them.
The Swift runtime is large-ish, but not outrageously so, and is generally statically compiled-in to the binaries, so there's no dependencies on dynamic libraries.
It is, as you say, quite complex, but still an interesting choice IMO.
C can be exposed as Objective C, but C++ has to go through C (potential objective C++) first.
This is really no different than most other languages.
What it does do is provide good auto binding tools however to expose classes and objects bidirectionally to objective C.
But the C++ story is what you’ve described above.
[0] https://developer.apple.com/documentation/swift/imported_c_a...
and
https://developer.apple.com/documentation/swift/imported_c_a...
There are still assorted gaps in C imports -- a key example is forward-declared structs, that _all_ arrive in Swift as a _single shared_ `OpaquePointer` type: https://forums.swift.org/t/opaque-pointers-in-swift/6875
and afair obj-c interrop is only available/supported on apple platforms
Swift seems to be one step removed from that. I guess you would have to write an Objective-C to Objective-C++ bridge to then use from Swift, ugh.
Seamless in my mind is what D has where name mangling, templates, and classes all work on Windows, Linux (possibly MacOS - I don't have one). Does swift have any of that?
If you are targeting multiple platforms this can get tricky because the mangled C++ symbol will (probably) be different for each platform, which can be solved with a good build system
The only way I would do it and trust it, would be if the binding is generated and tested (separately) automatically, but at that stage why not just write one with a proper ABI.
Also, this may work for simple C++ methods, but once you cross over into vtable territory, you're going to be in a world of hurt.
Also as far as interoperability, I just don't see a smooth path forward between Swift and C++. Philosophically, much of the C++ code you write ends up compiling away or building specialized generated code at compile time. Anything written in C++17 and newer will also have a lot of constexpr code that generates immutable results baked into the final output.
I'd say, wrap your C++ in a simple C API and expose that to Swift (same would apply to Go/Rust or any other language honestly).
vtables are always a pain, but I think the Swift team had some cool ideas about vtables and other dynamic stuff across dynamically linked shared objects..
upd: https://gankra.github.io/blah/swift-abi/ this
It is not there yet but it is actually being implemented on a native level. A quick Google gave me this. [0] [1] [2]
The approach is first-party than what you get with Rust's third-party crates and tools like bindgen, cxx, etc. Swift's C-interop approach is built-in and much more seamless and automated than bindgen's toggles and switches and creating them by hand in with cgo in Golang.
[0] https://github.com/apple/swift/blob/master/docs/CppInteroper...
[1] https://github.com/apple/swift/pulls?q=label%3A%22C%2B%2B+In...
[2] https://forums.swift.org/t/manifesto-interoperability-betwee...
The parent comment I replied to said: "...I cannot find any documentation at all relating to C++ interop." and this some kind of "documentation" that is related to 'C++ interop'.
I would say that Ada and Rust probably compete on some things, but given the history of Ada in industry, it's probably only in fields that already use Ada (aerospace and what-not).
And SPARK 2014 will support ownership specifications.
Rust is the less advanced one.
If you don't know, please don't speculate.
I use Ada professionally and I am experimenting with Rust. Rust has great promise and I am interested to see where it will go, especially as an alternative to C++.
>> it's a bit less advanced than Rust at static checks
Ada 2012 has comparable and in many cases more advanced static checks than Rust with the exception of memory management. This is especially true of the Ada type system (https://learn.adacore.com/courses/intro-to-ada/chapters/stro...) and design by contracts (https://learn.adacore.com/courses/intro-to-ada/chapters/cont...).
If you use the SPARK subset of Ada, you can use advanced tools to prove the correctness of your program. See https://learn.adacore.com/courses/intro-to-spark/index.html
Rust's borrow checker approach to memory management is novel and SPARK is acutally adding similar concepts to the next version of SPARK (https://blog.adacore.com/using-pointers-in-spark).
>> a good bit simpler than Rust and Swift but more complex than Go
Ada is a large and fairly complex language because it was designed for hard real-time, safety-critical, embedded systems and it has been in real-world use for 40+ years. I don't think it is that simple, but judging simplicity is subjective: what one person sees as complex, another person might see as simple. You can browse the Ada 2012 Language Reference Manual and see what you think: http://ada-auth.org/standards/12rm/html/RM-TOC.html
>> a little bit old-fashioned in approach (verbose syntax and all that).
This comes from the Ada design philosophy to be explicit in everything and to prefer the use of keywords over symbols.
"Readability is more important than conciseness. Syntactically this shows through the fact that keywords are preferred to symbols, that no keyword is an abbreviation, etc." (See https://learn.adacore.com/courses/intro-to-ada/chapters/intr...)
Long-life programs tend to be read more than they are written. The Ada way is to make programs easier to read rather than faster to write.
>> I would say that Ada and Rust probably compete on some things, but given the history of Ada in industry, it's probably only in fields that already use Ada (aerospace and what-not).
This is largely true. Ada occupies a niche for aerospace and other safety-critical areas, but has not been widely adopted due to the "uncoolness" factor and the cost of most of the available Ada compilers and toolchains.
I think the popularity of Rust has peaked some interest in Ada as well, but I am not sure if it will cause any change it where either are used.
I would like to see Rust to continue to mature and get adopted for wide-spread use with multiple implementations and a language standard. As it currently stands, many aerospace and safety-critical spaces would not be willing / able to adopt Rust without a language standard and certifications. Here's hoping . . .
An alternative syntax could be helpful for Ada to gain more traction.
This may be true, but it doesn't contradict the claim that Ada is old-fashioned in this regard. It was an old fashion in programming languages to prefer words over symbols, and to try to make programming languages look more natural language-like to make them more readable. See Ada, Cobol, Pascal or AppleScript for some examples. This is much less common with newer programming languages, where it's much more common to favor terseness and shortcut syntax. It's debatable whether this is better, but it seems undeniable that Ada-style verbosity is no longer in fashion.
Swift is only complex if you need it, otherwise very easy, with few caveats.
And it's efficient enough for all application / cli etc use cases -- if Go can do it, Swift can do it even better.
It's not Rust in performance and low-levelness, but Rust is the "really complex" one in its semantics and learning curve.
This might be hyperbole, but if not I'd love to see some examples on how Swift can improve on Go's concurrency patterns.
Yes. A feature well designed for this very purpose.
>Also is GCD green?
GCD tasks are not green threads but they're not direct threads either (even though they're used under the hood). They're more lightweight and much faster to create (than direct OS threads).
[0]: https://forums.swift.org/t/on-the-road-to-swift-6/32862/
If you claim something like this, please obviously back up your claims or in the very least, cite a simple example.
Go isn't simpler, there is just a defered cost engineers pay later on when it turns out one cannot just do away with complexity by deeming it irrelevant.
I agree that from a single sample you can’t reach conclusions, but “it was their fault” isn’t a great defense.
Many large projects may look like a mess but their success is because they got the high level architecture right and perhaps had the moat of a large tech behemoth behind them.
VSCode, Tensorflow, Kubernetes, Go, Typescript, React. These are a few that come to mind.
At a high level, they are all fantastic projects and perhaps messy in their own way, but they are absolutely fantastic, hardened proven codebases with ~millions of users.
Kubernetes is a complex distributed system with over 2M loc, not matter what the language you use there going to be some issues.
It's probably one of the largest open source project out there.
For example C++ is extremely powerful in writing generic code: Make it a template and and provide a set of of traits to modify the behavior and you get a set of algorithms you can re-use for anything – at the cost of unreadable code in the impelemntation (if you aren't careful; with a chance of quite precise code on the call side) and of course with the bloated version in the resulting binary (after compiler instantiated and inlined (thus copied) everything; where the programmer typically doesn't have to care (till the binary is too large to handle) all of it)
I assumed it is about features because there are really a lot of features inside Kubernetes which are not available in Swarm or Nomad by default.
https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-...
The monotonic time thing is an especially good example.
Swift just seems quite more complex for average programmer.
If you're jumping straight into a large, mature code-base I can understand how it might seem complex, but it's very possible to write Swift code which is as simple and readable as Go code. The same can probably not be said for C++ or Rust, where there's a certain baseline complexity which cannot be escaped.
Benching languages against eachother is like pitting birds of prey against eachother, its pointless because they will hunt what they hunt.
This language/framework dispute I though was fever back in bad PHP days has not changed. I'm not saying; 'oh you kids dont know shit' I'm saying the language is not the problem, the problem is the problem, the right tool for said problem is the answer.
You find Swift complex and speak about why, you're not wrong with its application, iOS it does very well, therefore its a tool for an iOS job.
If I grew up just learning Swift and nothin else, I would say swift is the best . Plato's cave springs to mind.
We are all Engineers, Developers, Hackers, Designers and or Code Monkeys.. Don't try to pit against, know the right tool for the job, if you cant find it, make it. With people who can.
I though thats how we roll
I think the problems you’d want to use Go for, the problems you’d want to use Rust for, and the problems you’d want to use Swift for are largely non-overlapping (in spite of whatever similarities they do have).
Swift is a statically compiled, C like language, that can be used for system programming. Just like C, C++, Rust, Go, etc. This is not an accident. It was built from the ground up to do that.
The goal with this OSS library is literally making swift easier to use in projects where you'd otherwise reach for exactly those languages. So, whether you like it or not, it's competing (or at least trying to) in that space (i.e. system programming). Actually, Swift was designed to replace Objective C, which of course was the system programming language that Apple standardized on as an alternative to C decades ago. It's designed from the ground up to be a drop in replacement in any project where you'd previously be using that. So, it's a natural fit for any kind of project where you'd otherwise be considering things like Rust, C++, Go, C, etc.
Whether that makes sense or not in your context is of course up for debate and highly subjective.
For what it's worth, [1] [2] [3] [4]
[1] A type system that can't cope with SwiftUI: https://tonsky.me/blog/swiftui/
[2] More syntax to shake a stick at https://twitter.com/dmitriid/status/1276482336486576133
[3] Even more syntax weirdness https://twitter.com/bradfitz/status/1285302091544576000
[4] Standard library between versions: https://twitter.com/dmitriid/status/1201441652507844608 File manipulation functions on String, great design.
The good and bad of Swift is the regular breaking updates. Sure it’s a pain when things change, but it’s also healthy when they improve, and almost every change has been an improvement.
A language as simple as C can slowly grow without breaking existing source code. But it’s also never been able to address its most significant flaws.
I provided 4 different links showing multiple different features in the language I find suboptimal.
> Sure it’s a pain when things change, but it’s also healthy when they improve, and almost every change has been an improvement.
It's a very dubious statement at the very least.
> But it’s also never been able to address its most significant flaws.
Can Swift address its flaws even with its breaking changes? So far its been piling on more and more syntax, and continuously breaking its standard library (whose design choices like writing to files from Strings are very dubious)?
Not just syntax, but the amount of it, and the amount of special cases in it.
> SwiftUI is not part of Swift, so 1 and 2 are not relevant
SwiftUI exposes deficiencies in the "one of the best designed languages" and shows how Swift fails to scale in complex scenarios.
(1) Shows that its design cannot cope with anything complex. You have to write clunky wrappers for `if` and `foreach` (but not for while and for, apparently), because the design of the language didn't have place for expressions. The type system cannot handle complex requirements of the library and can't provide better facilities than Java.
(2) Shows just how bad the design decisions are in the language that they all but force you to write code this way. There's syntax upon syntax with no internal consistency or actual design to figure out how they all operate with each other.
And Swift UI forces more half-baked escape hatches (like the `some` "type", like the lambda changes described in (1)) etc.
I'll just once again expose my favorite piece of code ever:
guard let self = self { return }
instead of if self == nil { return }
This is not an "extremely well designed language". These are badly thrown together pieces of half-baked ideas.(3) Once again shows just how badly it's designed. Or, rather, how badly the parser is designed that it can't reliably figure out what is going on. In a language that has no significant whitespace you have to care about significant whitespace. Great design.
(4) I'm faulting the design of the standard library that is only compounding the problems already present. Many languages managed to figure out standard libraries that don't make you read and write files from a string object, or need 15 lines (different every year) of code to figure out how to write/read a file. This only continues to show that very little thought is going into this project, as they are being pressured for time to deliver "something".
Oh, and for the last point, I suggest you read the article :P
It amazes how people completely ignore everything against some one thing they can potentially defend.
Once again, SwiftUI exposes deficiencies in the language, no matter if you care about it or not. Let's take it from the top, shall we?
- Swift UI says: hey, I need to have a list of values inside my nice little functions, what can yo give me? Swift: I can give you Java:
static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)>
static func buildBlock<C0, C1, C2>(C0, C1, C2) -> TupleView<(C0, C1, C2)>
static func buildBlock<C0, C1, C2, C3>(C0, C1, C2, C3) -> TupleView<(C0, C1, C2, C3)>
static func buildBlock<C0, C1, C2, C3, C4>(C0, C1, C2, C3, C4) -> TupleView<(C0, C1, C2, C3, C4)>
static func buildBlock<C0, C1, C2, C3, C4, C5>(C0, C1, C2, C3, C4, C5) -> TupleView<(C0, C1, C2, C3, C4, C5)>
- SwiftUI: I obviously need need some conditional and list management logic in my nice little lambdas. Swift: well, our "best designed language" can only give you Java. Because our "best designed language" has skipped class on language development of the past 40 years and can't even imagine that conditional statements can be expressions, for example static func buildEither<TrueContent, FalseContent>(first: TrueContent) -> _ConditionalContent<TrueContent, FalseContent>
static func buildEither<TrueContent, FalseContent>(second: FalseContent) -> _ConditionalContent<TrueContent, FalseContent>
static func buildIf<Content>(Content?) -> Content?
> The "some Type" thing is fairly standard type erasure.Once again, the only reason `some` exists is to aid the compiler. Because the compiler in "the best designed language" cannot figure out the difference between protocols and types on its own.
In general, in Swift and you have to constantly manually aid the compiler in everything. Also see `guard` below.
> Your "favorite piece of code" (which, FWIW, is missing an else on the guard) has different semantics than the other snipped you present because it rebinds self to a non-optional type, whereas the second one doesn't.
Yeah, yeah. I've seen the "it's different semantics" argument everywhere. It's not "different semantics". The only reason this exists is because they rushed the language out, and the compiler and the type checker was very limited. The only reason guard exists is to force nullability checks by the compiler. It's an inelegant and clunky clutch to aid the compiler. That is it. "The best designed language" could instead have this:
if x == nil { return }
// the compiler knows that x is now not nil,
// it's no longer an optional type beyond this point
> The parser is largely whitespace agnostic, just like pretty much every other language. Oh, maybe you were going to bring up C++No I'm not going to bring it up, but it's funny how you brought it up.
Actually, the reason I chose to refuse to engage with you about SwiftUI is that Swift doesn’t support ever programming paradigm in existence, nor should it. The fact that SwiftUI is running into issues where it is trying to use language features that don’t exist and then shoehorn them into the language retroactively is not really a great situation. That said, you claiming (multiple times!) that the language had no thought put into it and that it’s the same as Java is just outright trolling/bait at this point. The language has had a huge amount of effort put into it, many of the questionable decisions made earlier have been rolled out; at this point its type system is really at a similar place Rust or Scala’s is.
The lack of conditional statements being expressions (note: the ternary operator does exist) and type narrowing is a conscious choice, not something that the language has to have in order to “be modern” or some sort of evidence that this wasn’t considered. Type narrowing in particular is very common in languages that interface with code that is not well typed (TypeScript with untyped JavaScript, Kotlin with unannotated Optionals and inheritance hierarchies from Java) and Swift has generally had a much better interface with system libraries than that (this being largely controlled by Apple, they can roll out annotations fairly widely). Statement expressions are just a choice that Swift does not choose to have, although as see with function builders perhaps Apple will force it into the language anyways. And type erasure is good not only for the compiler but it’s also a huge benefit for users and API designers: it allows for “class cluster” designs, it keeps users from having to see SomeMonsterGeneric<Wrapper<Type1, Type2, Type3>, OtherGarbage> for no reason.
Of course it shouldn't. But going ahead and saying that "we shouldn't look at SwiftUI" for whatever reason stops most of the discussion about the deficiencies in the language.
Look, even you are saying things like this:
-- start quote --
The lack of variadic generics is an annoying limitation...
The fact that SwiftUI is running into issues... and then shoehorn them into the language retroactively is not really a great situation.
-- end quote --
You are basically repeating my words, but somehow I'm wrong in my assessment.
> that the language had no thought put into it and that it’s the same as Java is just outright trolling/bait at this point.
When you say that "lack of variadic generics is an annoying limitation" and "language features that don’t exist and then shoehorn them into the language retroactively" it's all right. When I say the same things, it's trolling. Got ya.
> The lack of conditional statements being expressions (note: the ternary operator does exist) and type narrowing is a conscious choice, not something that the language has to have in order to “be modern”
That's why SwiftUI has to "retroactively shoehorn" things like `buildEither<TrueContent, FalseContent>` because a "best designed language" doesn't have any facilities to, well, facilitate this.
> although as see with function builders perhaps Apple will force it into the language anyways.
Me: Swift lacks this and that.
You: You are a troll, and you are wrong, and this is a good design decision <literally half a sentence later> it will likely become a part of the language because <a few paragraphs before> there are features that don't exist in the language
> And type erasure is good not only for the compiler but it’s also a huge benefit for users and API designers: it allows for “class cluster” designs, it keeps users from having to see SomeMonsterGeneric<Wrapper<Type1, Type2, Type3>, OtherGarbage> for no reason.
There's literally no reason to have SomeMonsterGeneric<Wrapper<Type1, Type2, Type3>, OtherGarbage>. The only reason `some` exists is, and I repeat myself, because the compiler and the type checker literally can't distinguish between a type and a protocol without the developer babysitting them.
The reason I suspect trolling is that you have repeatedly taken my comments out of context, glommed them with other things that are not related, and then responded to that strawman, plus created what I can only refer to as "bait" because I have to waste my time responding to them when I could be having a much more productive conversation. So tell me, would you rather talk about how Swift's type system is the same as Java, how the compiler is designed by incompetent fools who rush out releases, and how any language that doesn't include the three features you brought up is automatically stupid, or maybe we can discuss this more productively from the viewpoint of "why didn't Swift include these things I like?" or "was SwiftUI poorly designed if it is trying to create new parts of the language out of thin air to support itself?" or "has Swift prioritized the wrong set of features?"
First of all, this is still perfectly valid swift:
if self == nil { return }
But guard is really nice, because it signals to the reader of a code block that if the guard condition can't be met, execution cannot continue past the guard. It does a lot to signal intent which using an if statement for early return does not.It's something I sorely miss in Rust for example.
Since every single language has an if statement, we clearly know what intent an if block signals.
If the if condition is not meant, execution cannot continue. See? Easy.
The only reason guard exists is not to aid the reader, but to aid the compiler to recognise nullability checks. That's about it. It really is a very specific if statement that is given a special treatment. And all comparisons of how great it is compared to an if statement I've seen are hillariously funny in how they willingly lie about ifs. Starting with the actual Swift docs [1]:
> Using a guard statement for requirements improves the readability of your code, compared to doing the same check with an if statement. It lets you write the code that’s typically executed without wrapping it in an else block
Here's how people take it to heart [2]:
> Without using guard, we’d end up with a big pile of code that resembles a pyramid of doom. This doesn’t scale well
Nope, you don't end up with a pyramid of doom, and yes it does scale well, provided you language designers actually design the language.
[1] https://docs.swift.org/swift-book/LanguageGuide/ControlFlow....
Guard has different semantics than if. For instance, I can write:
if self == nil {
... do some cleanup
return
}
Or: if self nil {
... do something else without returning
}
If it's a simple early return, then yes it's pretty easy to tell the intent. But imagine that I have to do some complex cleanup work over many lines before the return. In the case of a guard, I can look at the guard statement and immediately know that this block of code must end the control flow within this function. With an if statement, I have to read and parse the content of the block before I can understand that execution should not progress past this block.This is especially relevant when you're maintaining code which was written by someone else. With an if statement, you might accidentally remove a return statement from an if block which is required for correctness, and you may not find out about it until you run your program (or in the worst case after you release your code, and it causes unexpected bugs in production). With the guard statement, the compiler will enforce this constraint. It's one of many tools which Swift gives you to help you write correct code.
Also, with your example:
if self == nil { return }
I assume you are alluding to languages where self would be implicitly unwrapped in this case, so it can be treated as a concrete instance rather than an optional after this statement. IMO it is a strength of swift that you don't have this magical conversion of an optional to a concrete value which never explicitly written, but is inferred by the compiler. Guard offers clarity and consistency about exactly where the unwrap happens, and it's barely more verbose than a bare if statement. IMO this is a very elegant solution, and in practice it's something I miss in languages which don't have it.Yes, it has. But why? Everyone parrots the same "different semantics" excuse never bothering to ask why.
The only reason `guard` exists is to manually tell the compiler to do a nullability check. And that is it. It's a very small, very specific case wrapped into a separate syntax of its own because the compiler and the type checker are just not good enough.
guard X else {}
is exactly the same as if X is not null {} // pseudo code
but the compiler/type checker are not good enough to know that once null checks are passed in a statement, it's safe to use that value in the code that follows.This leads to horrendous piles of useless code like the one I showed:
guard let self = self else { return }
How can one look at that and say, "yup, that's good design"?> But imagine that I have to do some complex cleanup work over many lines before the return. In the case of a guard, I can look at the guard statement and immediately know
And immediately know almost nothing. Except that it's a very specific `if` case that returns. Which brings us to:
> you might accidentally remove a return statement from an if block which is required for correctness, and you may not find out about it until you run your program (or in the worst case after you release your code, and it causes unexpected bugs in production).
It won't cause issues if the language is actually properly designed. Because the variables you'll use will not checked for nullability in this case. And even Java will warn you with "potentially null value". But Swift can't do that without a `guard` statement. Go figure.
> IMO it is a strength of swift that you don't have this magical conversion of an optional to a concrete value
Yes, you do. You wrap it in a guard statement, assign a variable to itself, and poof, magic, it's suddenly unwrapped.
It's like you didn't even read my comment, I gave a reasoned answer why it's different. It gives a queue to the user that the condition must be met, or else there will be an early return.
The example you gave is also not equivalent - here you have:
guard X else { /* branch A */ }
// Branch B
And here you have: if X is not null {
// Branch B
}
// Branch A
The problem with your argument is that you are taking a subjective preference - that magical unwrapping via if-statements is the best form of unwrapping - and stating it as if it is an objective fact. You haven't actually given any arguments for why guard is an inferior approach beyond basically saying: just look at it, it's bad language design. Why is it bad?> It won't cause issues if the language is actually properly designed. Because the variables you'll use will not checked for nullability in this case. And even Java will warn you with "potentially null value". But Swift can't do that without a `guard` statement.
Again, it's not a question of proper/improper design or a failure of the compiler. The designers of swift made a conscious decision to provide exactly 3 ways to unwrap an optional:
1) the force unwrap `!` operator,
2) the `if let` statement,
3) the `guard let` statement
Just because that is not the exact design you would prefer does not make it a bad design.
You're also ignoring the uses for `guard` beyond null checking. For instance, if I wanted to check that my arguments are within a certain bounds, I could do something like this:
func foo(x: Int) -> Int {
guard x >= 0, x <= 42 else { return 0 }
return 2*x
}
This has nothing to do with "manually tell[ing] the compiler to do a nullability check ... because the compiler and the type checker are just not good enough" - it's about having an explicit language construct to signal that you do not want to continue with the logic of this function if the condition is not met.> Yes, you do. You wrap it in a guard statement, assign a variable to itself, and poof, magic, it's suddenly unwrapped.
These are actually very different things.
In this statement:
if x == nil { ... }
you are taking the conditional (`x == nil`) and giving it this magic side-effect that once it's executed, a new variable of a separate type is created and will replace `x` until the end of execution. Every other conditional simply evaluates to a boolean, so why should this one special case have this side effect where it's also declaring and assigning a new variable?To contrast that with the statement which bothers you so much aesthetically:
guard let self = self else { return }
Here the `let <identifier> = <value>` denotes the fact that you are declaring and assigning a new variable here, which is why the type change can take place. It happens to have the same name as the previous `self`, but every aspect of this is consistent with the rules of the language, and there is no special case being created here.You may not like it, and that's fine, but your personal taste does not reflect an objective truth about language design. Tens of thousands of swift developers around the world are able to understand the motivation for, and benefits of `guard` statements just fine. If you are bizarrely confused or angered by them, it does not mean they are categorically bad.
A guard must return if the condition doesn’t match. There’s no such requirement for if.
I also like that unwrapping is explicit in Swift. ‘if/guard let x = optional’ is the syntax for unwrapping. ‘optional == nil’ is the syntax for checking whether an optional is nil. Why would the latter do any unwrapping? The first time I saw that syntax in Kotlin, I thought that it’s weird to add functionality like this to a normal looking if.
It's ok for you not to like Swift, though. After all, taste is subjective.
Don’t get me wrong, I love Swift. It’s an absolute pleasure to work in and my first language of choice for projects targeting Apple platforms. But I’m very skeptical of its long-term potential outside of that ecosystem, especially with Apple’s move toward custom silicon and relying on hardware-level implementations of what would traditionally reside in software. This approach has worked out well for Apple with the rest of their product line and I’m completely on board from an engineering perspective - but it’s a path that will not result in higher cross-platform adoption.
It's amazing in situations where performance is critical, or where you have constrained resources. But I'd much rather use Swift in other situations.
I prefer the landscape for ML not to be fragmented based on non-essential qualities like the language that is used.
How great is it that researchers publish code in the same language, and everybody can use that code immediately without having to learn language X and/or porting the implementation?
I also think S4TF's approach has been really neat here: there's a really nice python interop so you still have access to the entire body of work and tools in python available, and it basically feels like writing native swift code.
I primarily write C++ and Python and never feel like this.
But in general, c++ to python is an awkward comparison because c++ is extremely low-level by comparison. So you do get static typing, which is nice, but you also get a lot of other issues to deal with - namely manual memory management - so with either python or c++ you have a high probability you will have code which fails at runtime. It would just be the case that in c++ most of the time it would be because you handled memory incorrectly, whereas with python it would more often be because you made a type-related error. Swift is much more comparable with Python in terms of reducing cognitive load by obviating low-level details from the programmer.
the alternative to python is julia
Swift has many nice similarities with Rust and other current languages from a pure syntactical/language perspective, while aiming to be a GP language. It definitely serves a broader audience than Go, whose main target is server programming. Swift's main target is Apple UI programming, however, the language is capable of so much more.
Which is why Open Source is good news. It may make it escape its Apple box.
Here is one post about writing an Aho-Corasick implementation in Haskell which is as fast as the fastest Rust implementation: https://tech.channable.com/posts/2019-03-13-how-we-made-hask...
I think Swift is where .NET was in its first decade. While capable of so much more, it is limited by its designers and primary purpose so it cannot go beyond. The tight coupling to UI products is a burden an ecosystem carries. Java won in the backend not with is UI, JavaScript with node/npm before Angular reset the frontend and C# just when .NET Core pushed it into spaces it has not been before (apis, lambdas, etc).
Anything on the internet should be cautious of memory unsafety issues. Something like an image processing library I won't consider 'security focused' yet I would trust one written in Rust over one written in C.
You could call Java is niche for the same reason. Some of us want to write native, low overhead code in a language that has a sane design (only disparaging C++ here). It's about time C++ had more alternatives.
While it’s true that Rust shows promise in security and embedded, some of the early adopters have been in the server side and microservices.
God help you if you want to have a swift package with some metal code in it. Rust cargo manages this without a hiccup.
Swift is nice as long as daddy Apple had your use case in mind, God forbid you have to tweak something. Rust feels more "timeless".
Swift for TF is dead.
For server side I’m probably going to use Rust, for MacOS or iOS I’m certainly using Swift, for Android I choose Kotlin, and for Windows I would choose suicide.
Anyway I was trying to use swift for things outside of iOS work to share code and it wasnt worth it, is what I'm really getting at. So agreed on your whole second sentence haha
Oh, unless you're developing GUI stuff. In which case, good luck. Use Qt or something, I dunno.