Thanks for raising some interesting points. I would like to use this context to explain some of the extensibility aspects of the proposal (possibly answering some of your questions). Particularly, what this proposal offers currently and some future directions to explore (as a community).
I want to bring out the different levels of extensibility here (in the increasing order of their generality):
Level 1: Customizing log messages passed to osLog: This is kind of what the proposal enables as such, as it is narrowly focused on just osLog. You can customize the string interpolation methods of PackedLogMsg
as you like e.g. to append tags/prefixes/suffices etc. to messages. The updations can use static, interpretable state. For example, you can choose between singular/plural based on the count of something (as long as the count is compile-time constant). If you look at the format string construction, it is already quite complicated. Having said that, all of this is limited by two things: (a) the interpretable language fragment (which doesn't include impure functions with side-effects, globals, classes, exceptions etc), and (b) the stdlib APIs supported by the interpreter. Note that the interpreter would only support a tiny fraction of string and array operations to accomplish the goals of this proposal. So doing advanced processing on strings will need extensions to the interpreter. (Often complex stdlib APIs are not interpretable and require special modeling inside the interpreter.)
Level 2: Customizing the backend and making it work with other loggers: The next level of extension would be to be able to use a custom logger instead of the unified-logging system, which in the simplest case, can just be writing to a log file. This is kind of plausible with this proposal (But is not the focus, at least until the basic functionality is accomplished). This can be achieved, if you make the APIs that does the write to file accept a PackedLogMsg
(which could well be customized to not create a byte buffer and format string but instead, say, just convert everything to string). E.g. you can create a function
func writeToLogFile(msg: PackedLogMsg) {
msg.getAsString().writeToFile(...)
}
extension PackedLogMsg {
func getAsString() -> String {
// construct a string using the tracked format string and "argument encoders" tracked by PackedLogMsg.
}
}
What you get out of this implementation is that PackedLogMsg
is constructed at compile-time and only getAsString()
and writing to file has to happen at runtime. The code for getAsString
is a bit complex but it can be constructed with the public members of PackedLogMsg
(viz. the format string property and encode
method). Ideally, the extension may want to write to file asynchronously and also construct the full message asynchronously (like osLog) for performance. This is doable with this proposal, but again has the limitation of being within interpretable/optimizable language fragment. This is how we think the proposed design can benefit/inter-operate with server-side logging (in the future). (See Server-side logging discussions: [Discussion] Server Logging API - #45 by johannesweiss)
From here, I am going to get a bit futuristic and present some ideas. (They are completely outside the scope of this proposal) and I am just sharing ideas for potential extensions/generalizations of this idea, and the interpreter more broadly.
c) Level 3: A general InterpretableStringInterpolation struct: There could be many other cases where one may want to optimize a custom string interpolation by interpreting constant parts of it. The compiler can expose a general custom string interpolation struct on which it will run the interpreter and try to fold its uses, independent of what it is used for. It could be for logging or for some other purpose. An open question/challenge here is how to design the properties and interfaces of the struct to make it general enough. (This is a non-trivial challenge especially when some parts of the interpolation are constant and some are not as in the osLog case.) PackedLogMsg
can then be seen one instance of this.
d) Level 4: A general-purpose "constexpr" for Swift: Such a language feature would enable Swift users invoke the interpreter and ask it to statically fold a value. This is a major feature and has several open questions/challenges. (A remark: the original compile-time interpretation pitch proposed a static-assert construct, which can also invoke the interpreter on arbitrary code but does not fold the result.)
The experience we gain and the challenges we address with this work may help inform a larger effort to evolve such user-visible language features as a part of Swift. This is completely outside the scope of this work.