-
Notifications
You must be signed in to change notification settings - Fork 200
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
Closed
Closed
rfc: unphased
functions
#1711
Changes from 9 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
645dd43
rfc: phase-independent functions with ?inflight keyword
Chriscbr 0f48be2
add resource example
Chriscbr 948bcf2
mention mutation
Chriscbr 7dbbb83
Merge branch 'main' into rybickic/maybe-inflight
Chriscbr 912e7c1
update syntax
Chriscbr d9908f3
update RFC
Chriscbr 103eab8
explain how array.map can work
Chriscbr a8601c6
clarify
Chriscbr 8827319
add note
Chriscbr 633d18a
fix typo
Chriscbr 4650e34
remove native class thing
Chriscbr cc3d63a
fix typo
Chriscbr 984a67d
Merge branch 'main' into rybickic/maybe-inflight
Chriscbr 23c067d
move to language-reference.md
Chriscbr d2ca05d
propose mechanism for unphased extern functions
Chriscbr 516641f
undo changes to RFC
Chriscbr ce3d292
simplify design slightly
Chriscbr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -752,6 +752,7 @@ For example (continuing the `Bucket` example above): | |
|
||
```ts | ||
let bucket = new Bucket(); | ||
|
||
// OK! We are calling a preflight method from a preflight context | ||
bucket.allowPublicAccess(); | ||
// ERROR: cannot call inflight methods from preflight context | ||
|
@@ -776,15 +777,121 @@ let handler2 = inflight() => { | |
} | ||
``` | ||
|
||
Bridge between preflight and inflight is crossed with the help of immutable data | ||
structures, "structs" (user definable and `Struct`), and the capture mechanism. | ||
The bridge between preflight and inflight is crossed with the help of immutable data | ||
structures, user-defined structs, resources, and the capture mechanism. | ||
|
||
Preflight class methods and initializers can receive an inflight function as an argument. This | ||
enables preflight classes to define code that will be executed on a cloud compute platform such as | ||
lambda functions, docker, virtual machines etc. | ||
|
||
[`▲ top`][top] | ||
|
||
#### 1.3.1 Phase-independent code | ||
|
||
> **Note**: Phase-independent functions are not yet implemented. Subscribe to [issue #435](https://github.com/winglang/wing/issues/435) for updates. | ||
|
||
Code that is not dependent on the phase of execution can be designated as phase-independent using the `unphased` modifier. | ||
|
||
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> => { | ||
let result = MutArray<num>[]; | ||
for num in nums { | ||
if num % 2 == 1 { | ||
result.push(num); | ||
} | ||
} | ||
return result.copy(); | ||
}; | ||
|
||
// OK! We are calling an unphased function from a preflight context | ||
let odds = odd_numbers([1, 2, 3]); | ||
|
||
let handler = inflight () => { | ||
// OK! We are calling an unphased function from an inflight context | ||
let big_odds = odd_numbers([7, 8, 9]); | ||
} | ||
``` | ||
|
||
Phase-independent functions are useful for code that is useful across both | ||
execution phases, such as for data manipulation, utility functions, etc. | ||
|
||
Since phase-independent functions can be used inflight, they inherit the same restrictions as inflight functions, like not being able to call preflight functions or instantiate preflight classes. | ||
|
||
But phase-independent functions can also be used preflight, so they inherit the same restrictions as preflight functions, like not being able to call inflight functions or instantiate inflight classes. | ||
|
||
Phase-independent methods can be defined on resources: | ||
|
||
```TS | ||
class AwsBucket { | ||
name: str; // preflight field | ||
|
||
new() { | ||
this.name = "my-bucket"; | ||
} | ||
|
||
unphased object_url(key: str): str { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Big fan of the keyword approach over the previous |
||
// This method references a preflight field (this.name) -- that is | ||
// allowed in both phases so it is OK! | ||
return `s3://${this.name}/${key}`; | ||
} | ||
} | ||
``` | ||
|
||
Phase-independent methods take on the additional restriction that they | ||
cannot mutate fields of the resource. For example, the following is disallowed: | ||
|
||
```TS | ||
resource Bucket { | ||
Chriscbr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
name: str; // preflight field | ||
|
||
init() { | ||
// initialize `name` | ||
} | ||
|
||
unphased set_name(name: str): void { | ||
// ERROR: cannot mutate a preflight field from a phase-independent context | ||
this.name = name; | ||
} | ||
} | ||
``` | ||
|
||
An unphased function can be passed to a function that expects a preflight function or an inflight function. In this way, we can say that an unphased functions are a superset of both preflight and inflight functions. | ||
|
||
However, a preflight or inflight function cannot be passed to a function that expects an unphased function. | ||
An exception to this rule is that if a function is unphased, then we can automatically assume any functions passed to it or returned by it have a matching phase. | ||
|
||
You can imagine that when a function is unphased, then "preflight" and "inflight" versions of it are generated at compile-time, and unphased-function types in parameters or return types are automatically converted to the appropriate phase. | ||
|
||
For example, `Array<T>.map` is modeled like the following pseudocode: | ||
|
||
```js | ||
native class Array<T> { | ||
Chriscbr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unphased map<U>(f: unphased (T) => U): Array<U> { | ||
// ... | ||
} | ||
} | ||
``` | ||
|
||
At compile-time, since the function is unphased, preflight and inflight versions are generated: | ||
|
||
```js | ||
native class Array<T> { | ||
preflight map<U>(f: preflight (T) => U): Array<U> { | ||
// ... | ||
} | ||
inflight map<U>(f: inflight (T) => U): Array<U> { | ||
// ... | ||
} | ||
} | ||
``` | ||
|
||
Notice how "f" is automatically converted to the appropriate phase. This is possible because the function is unphased. | ||
This way, when you call `Array<T>.map` with in preflight, it's possible to pass a preflight function to it, and when you call it in inflight, it's possible to pass an inflight function to it. (If you're calling `Array<T>.map` within another unphased function, then the unphased version of `Array<T>.map` is used.) | ||
|
||
[`▲ top`][top] | ||
|
||
--- | ||
|
||
### 1.4 Storage Modifiers | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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:
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 seeinflight?
is an optional inflight.(Thinking outloud)
The RFC mentions:
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.