Read the whole pitch for the first time, great to see this moving forward again.
I wondered why Artem wasn't as active on github late last year, guess this explains it. Thanks for working on this detailed proposal.
Is any of this implemented yet? For such a large pitch, would be good to try things out with a working implementation, even if not fully baked yet.
One issue I've seen with compile-time implementations in other languages is how to deal with initializing floating-point constants when cross-compiling. Say you're initializing some 128-bit floating-point constant for AArch64 with a compile-time calculation, but the native hardware only supports 64-bit floating point: you then have to push everything to some soft-float library to get the same full precision as you would on native hardware, ie when compiling on AArch64 itself (I guess Float128 isn't supported by Swift yet, just expecting it will be, and the point stands for 80-bit also).
I don't have much else to say about this necessary @const feature, but I'd like to link this recent blog post comparing compile-time programming in Rust, Zig, and some other languages, including their syntax for such compile-time constants. That @embedFile
feature would be useful in Swift too, so we could import and parse arbitrary data at compile-time. Also, I started watching this Jon Blow interview, the developer of the upcoming Jai language, and he talks a lot about his thoughts on macros and compile-time programming in the first hour.
While this @const computation would be a great feature to have, what I'm really looking forward to is being able to manipulate code using compile-time reflection, whereas this pitch is really about the first step of what I called compile-time code execution a couple years ago. You can see some of that compile-time manipulation in other languages in Renato's blog post, but I was trying to come up with a simple example that really demonstrates it, and this is what I got.
We all know data layout really impacts performance, as Andrew Kelley of Zig talked about a couple years ago. So if we start packing structs like he shows there, but need to do it differently for each platform's types and alignments, we might write it out like this today:
// long comment explaining our calculations of why
// we chose this data layout for each platform
struct Foo {
#if os(Android) && (arch(arm64) || arch(x86_64)
var goo: UInt
var moo: CLongDouble
var boo: Float
var noo: Double
#elseif os(Android) && arch(arm)
var goo: UInt
var boo: Float
var noo: Double
var moo: CLongDouble
#elseif os(Linux) && (arch(i386) || arch(x86_64))
...
// Lot of other platforms
...
#endif
}
This is verbose and depends on not always provided doc to explain the algorithm used for layout.
Conversely, if we move this to compile-time, it might look like this:
// compile-time code to calculate the right ordering layout
@const layout : String = calculate_layout()
public func calculate_layout() @const -> String {
if (CLongDouble.size ... // compile-time logic to get the right layout
}
struct Foo {
#if layout == "CLDLast"
var goo: UInt
var boo: Float
var noo: Double
var moo: CLongDouble
#elseif ...
}
Nobody has to read doc to figure out the layout algorithm used, it is explicitly shown and run on each build. Of course, this won't work without such platform properties being available to reflect on at compile-time. In theory, this is all doable with macros too, but looking at both Swift's macro code and those other languages' alternatives shown above, that would just bury the compile-time algorithm in a bunch of obscure macro code.
Obviously, this is a simple example to demonstrate the value of such compile-time code manipulation, one can come up with much more. No doubt this is currently not possible without first figuring out the layering issues Artem mentions above.
Even if Swift never is able to do this, this is the direction other languages are heading in and the Swift language group should be contemplating.