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.
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.
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.
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.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.
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).
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)
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.
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) }
Which for is for and would work as for for?
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.
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)
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.
So can Perl.
Or the internalization of the managerial imperative for cheap labor, take your pick.
Companies prefer a churn of cheaper juniors happened.
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.
[0]: https://forums.swift.org/t/on-the-road-to-swift-6/32862/
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).
If you claim something like this, please obviously back up your claims or in the very least, cite a simple example.
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.
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.
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...
Swift supports LSP now, so you can use VS Code if that's your thing.
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
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.
and afair obj-c interrop is only available/supported on apple platforms
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?
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'.
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
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
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.
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.
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 . . .
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.
An alternative syntax could be helpful for Ada to gain more traction.
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).
- 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).