Better crypto support in platform-agnostic Swift is definitely #1 on my wishlist. On macOS/iOS you have Apple's libraries to provide cryptographic support. However, for server-side Swift running on Linux, your options are pretty much reduced to wrappers around OpenSSL. I'd love to see more formal cryptographic and security libraries for Swift. While Java's JCE definitely has its flaws from a design perspective, something along those lines for Swift would greatly enhance its viability for enterprise software, or other applications where security is of paramount concern.
Now refactor everything to support async/await to support asynchronous use in addition to synchronous use. And then you'll have the answer as to why async/await and stackless coroutines are a dead-end.
SwiftTLS is non-blocking (using Dispatch). Yes, many libraries will have to be updated when async/await arrives, but I'd say that's business as usual for Swift developers (who have to do some refactoring every year anyway).
Also, SwiftTLS is not meant to be used in a production environment at this point. The disclaimer states: "It is not ready, has certainly a lot of bugs and received virtually no real world testing yet."
TLS stacks are dead out of the water if they don't support non-blocking I/O.
The point of my post wasn't to criticize SwiftTLS, per se, but rather to make a larger point that supporting asynchronous I/O will never become substantially easier unless and until languages provide the correct primitives. Case in point: try to refactor SwiftTLS (which otherwise may be a laudable TLS implementation) to support Swift's async/await proposal. All the headaches and compromises will say much more about the viability of async/await than about the implementation quality of SwiftTLS. Nonetheless, it's the end product (SwiftTLS, etc) that will be bear the blame for the added complexity (or lack thereof if they abstain from supporting the model).
your comment is really interesting to me, but i’m not familiar enough with tls to understand what is impossible with adding usage of async/await on this codebase. Could you give a specific example on the kind of problem it will run into ? (a function from this project for example)
I'm not saying it's impossible. I'm saying that the process will be incredibly intrusive. To support both synchronous and asynchronous models you often need to go through contortions to split out as much common code as possible. That's because you can only call async functions using specialized await invocations. See http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
Alternatively, you need to make one or the other a 2nd class citizen (e.g. write the core implementation using async/await and make the synchronous API a wrapper), making that aspect of the implementation substandard in the eyes of your library users.
There are alternative language primitives, like asymmetric stackful coroutines, that provide much better semantics. You can continue sharing all your code using the same function composition patterns because there's no bifurcation in the function space--a coroutine looks and behaves and is invoked like any other function.
Stackful coroutines aren't memory efficient if you try to maintain compatibility with traditional C ABIs, especially on 32-bit platforms. To reliably run C code musl libc, for example, has minimum stack sizes of 80KB. By contrast, a stackful coroutine in Lua has an initial allocation size (including stack) of less than 300 bytes, and the stack is dynamically resized as needed. So stackful coroutines aren't a good fit for Rust which emphasize seamless backward compatibility with existing C code.
But for a language like Swift I think the designers could have gone the other way[1], and it's a shame they've committed themselves to a language primitive that is a dead-end and only as popular as it is because of extrinsic limitations--Rust's is backward compatibility with C, for JavaScript and Python it's the fact that the existing engines intertwine the C stack with the languages logical stack. (For use cases where async/await may be a preferable paradigm, e.g. code documentation or ability to block suspension of the caller, async/await can be implemented cleanly and cheaply atop the lower-level primitive. Stackful coroutines are a strictly more
elegant and capable primitive.)
People debate these issues endlessly and many people would disagree with me. But my point is that the real proof of my argument can be seen in the difficulty of authoring and maintaining real world libraries like SwiftTLS. Forget abstract arguments--look at the concrete use cases and not anecdotes or proofs-of-concept. The async/await world brings significant code duplication and decreased composability (i.e. ability to mix-and-match libraries and toolkits). It's a hack that is rationalized post hoc.
[1] While Swift needs to support C, C++, and Objective-C interoperability, Apple's long-term goal is a predominately Swift-based ecosystem. And all of Apple's hardware is now 64-bit, so theoretically they could even support full C ABI interoperability without an undo memory hit by sparsely allocating stacks and extending them in-place on demand. Ultimately, I think part of the calculus is that it's easier to fiddle with your languages type system than it is to fix the low-level stack management of a pre-existing compiler (i.e. LLVM) to support many dynamic stacks. Go could do this because they wrote (or rewrote) their own compiler toolchain from scratch. Scheme and Lua can do this because the runtime VM stack is kept distinct from the native C ABI stack used by the engine; they effectively multiplex many VM stacks atop a single native C stack. Similar story for Java's Loom project regarding JVM stack management.
I wonder how many times we'll impl tls, http 2, aes, etc or how many times we'll leverage libs with C ffi. I'm not sure about Swift's CSP support, but I'm beginning to think translating Go everywhere, except for some of the core (or other asm-level pieces like constant time byte array actions) that may be language specific, may be a better way to open up tons of libs for use. It's a fairly simple and unchanging source language. I know Haxe has these kinds of goals, but I figure the ecosystem is weaker.
I don't see Go as a panacea for opening up libraries for use. You're still going to see languages that can export C interfaces for that. It's going to be C, C++, and Rust that are likely to persist as the languages of choice for massively consumable libraries. As long as they can export a C interface, you can get low (or no) overhead interop. Go's biggest disadvantage is that it ships by default with a runtime.
I meant translate the actual Go code to Swift code so it's pure Swift, but the effort to implement the algorithm right is removed. But yes, to support Go's features you will need at least a GC. Once Rust gets async/await down and people run with it, a static lib in Rust w/ an exported C interface might instead be the norm. But in the meantime, it's annoying that the options are to either reuse a complicated/unsafe C/C++ impl or re-impl in each language individually. Go has a terrible C export story with lots of limitations and impedance mismatches so that's not a great approach imo (granted it's what gomobile does iirc).
Ah, I understand now. Yes, translating from a Go implementation to a Swift one would erase most of the complexity of reimplementing the algorithm (in a potentially error prone way).
I agree that once Rust lands async/await and it matures, it could be the go-to for safe, cross-platform libraries. That's my hope, too. I'm selfishly a Rust evangelist.
C interop can introduce a lot of complexity in the build toolchain. It's nice to have native versions of libraries like this, vs. relying on compiling c/c++/rust versions.