-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rfc: unphased
functions
#1711
base: main
Are you sure you want to change the base?
rfc: unphased
functions
#1711
Conversation
Another idea/suggestion is to designate these functions with the keyword |
Im on board, I just dont much care for the prefixed |
I agree that letting the compiler infer whether a function is phase-independent is not the right way to go. Like @hasanaburayyan, I'm also not crazy about the I'm also OK with experimenting with having to always put a phase modifier on functions, and then we have |
@Chriscbr , great doc |
Perhaps an alternative is to create functions that are neither This type of function only becomes something like // a template function, if no variable receives this template function
// with a prefix (pre/in) the compiler gives an error
let f = flight(...) { ... };
let p = pre f; // create a preflight function
let i = in f; // create a inflight function |
Maybe I'm missing something, but I think any project has general purpose utility functions that are used throughout the code and have no business being confined to only one phase. For example: sorting an array with different algorithms |
Yes, I think @ShaiBer 's answer is correct, the main use case that comes to my mind is utility functions that do not need to be restricted to one phase or another. If I think of any more use cases I'll update the proposal. BTW, my initial guess is we don't need this feature for beta, or at least we should dogfood more before considering it. @marciocadev that's a very interesting idea! From an implementation angle, I think that could be a good option if we want to simplify modeling phase-independent functions in the compiler (perhaps we would only need our But I'm a little cautious about templating based solutions / not sure I understand the benefits. In your example, suppose I use a preflight function inside |
I was thinking in gives an error if Some errors bring cloud;
let f = flight ( ... ) { ... } // ERROR, something like f variable never used bring cloud;
let f = flight ( ... ) { ... }
new cloud.Function(pre f); // ERROR, can not pass a preflight to cloud.Function This works bring cloud;
let f = flight ( ... ) { ... }
new cloud.Function(in f); // WORKS Somehow, I think of a bring cloud;
let f = flight ( ... ) { ... } // verify some data
let p = pre f;
if (p.invoke() == false) { // verify before create the cloud.Function (preflight)
...
}
let i = in f;
new cloud.Function(inflight(event: str): str => {
...
if (i.invoke() == true) { // verify inside cloud.Function (inflight)
...
}
...
}
if (p.invoke() == true) { // verify after create the cloud.Function (preflight)
...
} or the same without create new variables bring cloud;
let f = flight ( ... ) { ... } // verify some data
if ((pre f).invoke() == false) { // verify before create the cloud.Function (preflight)
...
}
new cloud.Function(inflight(event: str): str => {
...
if ((in f).invoke() == true) { // verify inside cloud.Function (inflight)
...
}
...
}
if ((pre f).invoke() == true) { // verify after create the cloud.Function (preflight)
...
} |
Another naming suggestion from @MarkMcCulloh is "unphased" |
Hi, This PR has not seen activity in 20 days. Therefore, we are marking the PR as stale for now. It will be closed after 7 days. |
Hi, This PR has not seen activity in 20 days. Therefore, we are marking the PR as stale for now. It will be closed after 7 days. |
Hi, This PR has not seen activity in 20 days. Therefore, we are marking the PR as stale for now. It will be closed after 7 days. |
I'd expect Wing to infer the phase of the function most of the time, and also cast a function to inflight if necessary. In the same way that TypeScript can infer that the following function returns a number: export const sum = (a: number, b: number) => {
return a + b;
}; Regarding Chris' point:
I like Mark's Here's how I'd like it to work in different scenarios: Compiler infers that the function is inflight
Compiler can cast phase-independent functions to inflight (if they don't interact with resources nor capture mutable state)
Compiler infers the function is inflight so it has access to bucket's inflight API
Compiler infers that "put" is preflight because it accesses the bucket
Compiler infers that "append" is preflight because it captures mutable state
|
Hi, This PR has not seen activity in 20 days. Therefore, we are marking the PR as stale for now. It will be closed after 7 days. |
unphased
functions
Using this modifier means that the function can be called from either preflight or inflight contexts. | ||
|
||
```TS | ||
let odd_numbers = unphased (arr: Array<num>): Array<num> => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the suggestion but I prefer inflight?
instead of introducing another keyword to the language.
I think it reads nicely:
let foo = inflight? () => {
return 34;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With just inflight
, preflight
, unphased
perspective I think it reads nice, but my concern here is the first thing I think of when I see inflight?
is an optional inflight.
(Thinking outloud)
The RFC mentions:
However, a preflight or inflight function cannot be passed to a function that expects an unphased function.
How would an expected unphased function look in a method signature if we use inflight?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little worried there could be a confusion or mental overloading with "?" since "foo?" looks a lot like an optional type. (It's also handy that "unphased" is fewer characters). But I'm not sure - let's keep the option open, it's probably the easiest part of the design to change 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that “optional inflight” is actually conveying the idea of “unphased” pretty well.
It implies that this is a preflight function that can also be used inflight.
We should be very careful to add additional keywords to the language. Every new keyword is a huge cognitive overload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that thinking of the function as "optionally inflight" means it could be inflight or preflight.
However, we define optionality in the language as potentially having a "lack of value" so T? is [T || nil], which makes inflight?
being [inflight || preflight] weird in my opinion.
this.name = "my-bucket"; | ||
} | ||
|
||
unphased object_url(key: str): str { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big fan of the keyword approach over the previous ?
, it feels very clear and is consistent with existing keyword modifiers.
First step towards implementing the `unphased` functions RFC (#1711) ## Checklist - [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [ ] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
I've added an addendum with a proposed mechanism for how unphased extern functions can be implemented. It's not a high priority to support this immediately, but this will help us build a path forward for rewriting various parts of the |
First step towards implementing the `unphased` functions RFC (#1711) ## Checklist - [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [ ] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
Random thought: |
@@ -1983,6 +2086,36 @@ matching name (without any case conversion). | |||
|
|||
Extern methods do not support access to class's members through `this`, so they must be declared `static`. | |||
|
|||
If an extern function is declared as `unphased`, a preflight and inflight implementation must be provided, by providing two separate functions in the JavaScript module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could also still accept a function, if both implementations are the same. I'm just trying to avoid clutter.
So it could be:
exports.randomHex = async function (length) {
const buffer = Buffer.alloc(length);
await randomFillAsync(buffer);
return buffer.toString("hex");
}
but also:
exports.randomHex = {
preflight: function (length) {
const buffer = Buffer.alloc(length);
crypto.randomFillSync(buffer);
return buffer.toString("hex");
},
inflight: async function (length) {
const buffer = Buffer.alloc(length);
await randomFillAsync(buffer);
return buffer.toString("hex");
}
A simple check could be used to detect both cases: exports.randomHex instanceof Function ? "single implementation" : "dual implementation"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, this assumes externs can only be functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds ok to me as an extra enhancement (I probably wouldn't implement it for the MVP but maybe later).
Note: if you only provide a single function, it will need to be sync (not async), otherwise it can't be safely called in preflight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also FWIW possible to reuse a single implementation twice in the JavaScript (so I'm not sure it's worth slowing down performance by making it Wing's responsibility to check if externs are instanceof Function
each time an extern is called).
function randomHex(length) {
const buffer = Buffer.alloc(length);
await randomFillAsync(buffer);
return buffer.toString("hex");
}
exports.randomHex = { preflight: randomHex, inflight: randomHex };
This doc proposes how phase-independent functions should look and behave in Winglang. Related to #435
From a compiler perspective, these functions would essentially be code generated in both the preflight JS class and the inflight JS class.
By submitting this pull request, I confirm that my contribution is made under the terms of the Monada Contribution License.