Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Some people want generics and ADTs in Go. If you're using go because it's

1) fast

2) expressive

3) concurrent

4) compiles natively

then I think Swift is the better of the two.

If you just want a very simple language, then sure, Go is there. But I don't think that's necessarily what makes Go, Go.



I would say that yes, in fact, simplicity is characteristic of Go. That is clear from how the go team considers the impact of every new language feature, often choosing not to do things that would've been considered obvious in other languages. Many parts of the community still resent generics, and there is widespread backlash to the new iterators proposal. Most Go enthusiasts wouldn't be fans of Swift and vice versa.


after using swift extensively i did not find it better of the two, in fact it is a complex keyword ridden language that seems bolted together at very many angles, each swift codebase i enter is like a new mystery to unfold because of its extremely rocky evolution


One thing that bugs me to this day is reading Swift code without an IDE can be an exercise in “let me build up a mountain of context first”, due to the way enums and types can get shortened.

It’s nice to write but a pain to review. Meanwhile if I read Rust or Go, it’s more or less what you see is what you get.

I do agree in general that it’s frustrating that Rust hasn’t kept up with some of Swift’s pushes, but then again that’s the difference when you have the richest company in the world involved.

For context, I’ve written many apps in Swift, deployed a backend in Vapor, and worked with teams who use the language. I always thought I’d get over it but it just never fully clicked.


Yes! Reading Swift code, even tutorial code, can be difficult.


Swift has its strengths against Go but it does not really beat Go at Go's own game.

Where are channels and select? Why is async code special? Why do protocols have to be explicitly adopted? Why are errors "thrown" instead of returned?


The async question is actually really interesting - and gets at a few things Swift does very differently from some other languages.

Normal languages just sort of call functions on threads - and use locks when there is contention. You certainly can write the same sort of code in Swift - but that is not how async functions work.

The issue with locks is that waiting on a lock could be expensive - depending on the type of lock. And it also ties up the thread. If you have a thread pool you could end up with a lot of threads getting tied up in locks.

Async functions are suspendible. When they encounter a data hazard - the thread is released and allowed to go work on something else. When the conflict clears the function is picked back up and execution continues. Note: the way this is designed means that a _different_ thread may continue execution of the async function, and async functions should assume that they may change threads during execution. But this does prevent stalled threads just waiting on some condition. The compiler needs to extra notation to know your function has been designed around this sort of operation.

Basically: Async functions are not normal functions - they're allowed to be suspendible and may change threads mid execution - hence the extra notation. This improves both performance and safety.


But that's the thing, we're comparing with Go, where all functions behave that way, and no async/await ceremony is required. Go has many weaknesses, but I don't think a language that treats async as unusual/special beats Go in that respect.


This is just a hunch but likely because of the application the two languages were originally designed for. Go is/was a purely backend lang (as I understand it) whereas Swift was built with the UI in mind which must be run on the main thread. So Swift has async/await for making clear boundaries between synchronous (aka UI) operations and asynchronous operations.

I’m am not well educated on the subject though so take that with a grain of salt.


This is a good point. It's possible to use Go this way too, but it gets tricky. You can lock the main goroutine to its O/S thread with runtime.LockOSThread and then do the heavy lifting on other goroutines, sending messages back to the main goroutine typically using channels. Given Swift's lineage, this is probably more ergonomic there than in Go.


This melding of the sync and the async is actually kinda interesting to me. I know that at least in lots of environments, the sync and async paths are effectively separate for things like I/O[1]. I wondered (and still do for some cases) how Go handles this.

For those curious I looked at Windows and Linux, but not much else.

Linux: no io_uring support. There's debate on even whether to use it as people are discussing security implications[2]. It looks like (from perusing this issue, but could be wrong) AIO wasn't used.

Windows: it looks like they're using IOCP everywhere. Seems sensible enough.

General case: there seems to be an open issue regarding this[3].

[1]: For example, Windows has IOCPs, Linux has io_uring, FreeBSD has kqueue, POSIX has... POSIX AIO, etc.

[2]: https://github.com/golang/go/issues/31908

[3]: https://github.com/golang/go/issues/6817


Interesting. My understanding of how things get implemented is that all code runs essentially "async" by default, but the runtime scheduler can switch to running code synchronously when requested (runtime.LockOSThread) or necessary (e.g. "slow" calls to C via cgo).

I was not aware that file I/O isn't async on Linux. Even so, I'm pretty sure network I/O and channel operations (send/receive/select) are async via epoll. I'm not as sure about these, but I think time.Sleep and sync.Mutex.Lock suspend the goroutine as well.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: