Closed MarkMcCulloh closed 1 week ago
This design feels well thought out. I think it addresses a significant gap / confusion in the language today (how come built-ins functions like log
, assert
, and lift
function work different from user defined functions) and leads to more consistency than the alternative of designing dedicated syntax for each builtin function.
While the syntax resembles the decorator syntax used in some other languages, I think the confusion is reduced since the intrinsics appear in an expression position.
I don't think there are many capabilities that would be more useful to reserve "@" for. One idea that crossed my mind was using @ to designate explicit lifting, e.g.
let b = new cloud.Bucket();
inflight () => {
@b.put("key", "value");
}
But even this idea doesn't feel that strong since an expression like @foo.bar.baz
doesn't really make it clear "how much" of the expression is from preflight vs inflight. So using @ for intrinsic functions seems more appropriate.
I'm also in agreement that user-defined intrinsics / macros don't need to be designed at this stage.
@MarkMcCulloh Do you want to add this to a RFC and post it in the #dev channel?
In favor of this change!
While the arguments/return types must be normal wing types
I am not 100% sure that we need to take this constraint. For example, the @log()
intrinsic needs to accept any
, which is not a type users can declare, right?
Let's make sure we list all the current builtins and get a feel of what it looks like to use them. For example, @log
is doing to take time to get used to:
new cloud.Function(inflight () => {
@log("hello, world");
});
Another suggestion - especially if we change log
to @log
is to do a soft deprecation with a compiler warning and only hard-deprecate it later. We also discussed that warnings will have a quickfix in VSCode (I know we don't have those but it's about time).
We can't afford to hard deprecate log()
anymore.
I'll just say that I'm not sure about adding @
to builtins.
It doesn't add any information or readability, and it adds an extra (irregular) char.
log()
is pretty intuitive for programmers - it makes sense that it is a builtin.
If we need the intrinsic character for @inflight
maybe we can just use it for that?
There is value in having an explicit way to denote built-ins in order to signal to developers that this thing might not behave like a normal function and that it might not be something you can actually define on your own (at least for now).
Specifically for log()
, I think we can be pragmatic and support both @log
and log
because it's such a common builtin to use, so we can be pragmatic about it for the sake of ergonomics. Maybe we can even make this exception for assert
.
But for stuff like @lift
and @inflight
and @extern
I think it makes a lot of sense.
The other benefit is that if we make something like extern
a keyword, users can't use it as a variable name.
I'm for this change, even trying out @log
while deprecating log
(it's still less chars than print
or console.log
).
Do you want to add this to a RFC and post it in the #dev channel?
I'm not against an RFC but I'm not sure what open questions need to be answered. As an idea the @
is pretty simple since it represents something we already have in our compiler. Individual intrinsics could support some discussion I suppose.
While the arguments/return types must be normal wing types
I am not 100% sure that we need to take this constraint. For example, the
@log()
intrinsic needs to accept any, which is not a type users can declare, right?
I think "normal wing types" can be pretty flexible, so I maybe should phrase it differently. I guess I moreso mean "representable in wing's type system". So "any" would be valid. I figured it was useful to add so that we can still make use of wing's type system, but maybe it's not really useful as a constraint to always have.
Congrats! :rocket: This was released in Wing 0.73.38.
Use Case
In wing there are currently several built-in globals, all of which are injected with their own macro functionalities. These can intentionally have behaviors different from those possible to implement in wing itself. Some are simply macros that emit code, while some interact with the compiler in more novel/deep ways (e.g.
lift
). Even the simple macros have access to information that user-defined code would typically not (types, source position, etc.).These are incredibly useful, but their behavior is surprising when to the user they appear no different than any other symbol/variable. Additionally they allow you to do things like do variable shadowing and assign them to variables which can actually break their functionality.
Proposed Solution
Introduce a new syntax to reflect these special behaviors.
I propose
@x()
as a "compiler intrinsic", with the following notes:Having an intrinsic called
@x
does not mean that there is a symbol calledx
. Intrinsics are their own separate concept.It will not be possible to write
let variable = @x;
or otherwise use@x
as an expression on its own, to prevent the impression that this a singular value being referencedWhile the arguments/return types must be normal wing types, the kind of expressions that can be passed as arguments may have certain restrictions. For example, it would be common to only allow non-interpolated string for certain cases to ensure all information is available statically.
Why
@
?@
and you see all the available options.@
is used in js for decorators which has a similar vibe of being special in the eyes of the compiler. It would not be unreasonable to use @ for decorators of some kind in wing as well.With this, we can convert our existing globals to intrinsics instead and go forward implementing new ones. Some existing issues that I think would benefit:
@file
- https://github.com/winglang/wing/issues/5449@inflight
- https://github.com/winglang/wing/issues/6045Other thoughts
I think it would be reasonable to expose the ability to create these in user-space via some sort of compiler API. This would be amazing but the design and implementation that would be a pretty huge task. Even if we did, we could still use the same syntax for invoke them even though we probably shouldn't call them "intrinsic" anymore.
Community Notes