From 645dd437992e8a2f408bb99d3d7fe2d603c8f5cb Mon Sep 17 00:00:00 2001 From: Christopher Rybicki Date: Thu, 2 Mar 2023 21:54:42 -0500 Subject: [PATCH 01/15] rfc: phase-independent functions with ?inflight keyword --- docs/04-reference/winglang-spec.md | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/04-reference/winglang-spec.md b/docs/04-reference/winglang-spec.md index 84a087ba8f0..00f0833c377 100644 --- a/docs/04-reference/winglang-spec.md +++ b/docs/04-reference/winglang-spec.md @@ -741,8 +741,10 @@ For example (continuing the `Bucket` example above): ```ts let bucket = new Bucket(); + // OK! We are calling a preflight method from a preflight context bucket.allow_public_access(); + // ERROR: cannot call inflight methods from preflight context bucket.put("file.txt", "hello"); @@ -765,8 +767,8 @@ 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 resource methods and initializers can receive an inflight function as an argument. This enables resources to define code that will be executed on the @@ -774,6 +776,47 @@ deployed resource (lambda functions, docker, virtual machines etc). [`▲ top`][top] +#### 1.3.1 Phase-independent code + +Code that is not dependent on the phase of execution can be designated as phase-independent using the `?inflight` modifier. +This modifier can be read as "maybe-inflight". + +Using this modifier means that it is safe to call the function or method from +either a preflight or inflight context. + +```TS +let odd_numbers = ?inflight (arr: Array): Array => { + let result = MutArray[]; + for num in nums { + if num % 2 == 1 { + result.push(num); + } + } + return result.copy(); // convert to immutable array +}; + +// OK! We are calling a maybe-inflight function from a preflight context +print(odd_numbers([1, 2, 3])); + +let handler = inflight () => { + // OK! We are calling a maybe-inflight function from an inflight context + print(odd_numbers([1, 2, 3])); +} +``` + +Phase-independent functions are useful for code that does not apply to a +specific phase of execution, such as for data manipulation, utility functions, +etc. + +In order for a function to be phase-independent, it must not use any +phase-specific features, like calling methods on resources that are only +preflight or inflight. Since phase-independent functions can be used inflight, +they inherit the same restrictions as inflight functions, like not being able to +reference mutable data structures or reassignable variables from the outer +scope, and not being able to instantiate new resources. + +[`▲ top`][top] + --- ### 1.4 Storage Modifiers From 0f48be23b8f98a56eaa653d518e8118d5501b277 Mon Sep 17 00:00:00 2001 From: Christopher Rybicki Date: Thu, 2 Mar 2023 22:05:19 -0500 Subject: [PATCH 02/15] add resource example --- docs/04-reference/winglang-spec.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/04-reference/winglang-spec.md b/docs/04-reference/winglang-spec.md index 00f0833c377..103d5fe25c0 100644 --- a/docs/04-reference/winglang-spec.md +++ b/docs/04-reference/winglang-spec.md @@ -815,6 +815,24 @@ they inherit the same restrictions as inflight functions, like not being able to reference mutable data structures or reassignable variables from the outer scope, and not being able to instantiate new resources. +Phase-independent methods can also be defined on resources: + +```TS +resource AwsBucket { + name: str; // preflight field + + init() { + // initialize `name` + } + + ?inflight object_url(key: str): str { + // This method references a preflight field (this.name) -- that is + // possible in both phases so it is OK! + return `s3://${this.name}/${key}`; + } +} +``` + [`▲ top`][top] --- From 948bcf2acbed35a98d881303f91194a6f0563569 Mon Sep 17 00:00:00 2001 From: Christopher Rybicki Date: Thu, 2 Mar 2023 22:25:05 -0500 Subject: [PATCH 03/15] mention mutation --- docs/04-reference/winglang-spec.md | 43 ++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/04-reference/winglang-spec.md b/docs/04-reference/winglang-spec.md index 103d5fe25c0..460ca9b68c8 100644 --- a/docs/04-reference/winglang-spec.md +++ b/docs/04-reference/winglang-spec.md @@ -779,11 +779,13 @@ deployed resource (lambda functions, docker, virtual machines etc). #### 1.3.1 Phase-independent code Code that is not dependent on the phase of execution can be designated as phase-independent using the `?inflight` modifier. -This modifier can be read as "maybe-inflight". +This keyword can be read as "maybe-inflight." -Using this modifier means that it is safe to call the function or method from +Using this modifier means that it is safe to call the function from either a preflight or inflight context. + + ```TS let odd_numbers = ?inflight (arr: Array): Array => { let result = MutArray[]; @@ -792,28 +794,29 @@ let odd_numbers = ?inflight (arr: Array): Array => { result.push(num); } } - return result.copy(); // convert to immutable array + return result.copy(); // convert from MutArray to Array (immutable) }; // OK! We are calling a maybe-inflight function from a preflight context -print(odd_numbers([1, 2, 3])); +let odds = odd_numbers([1, 2, 3]); let handler = inflight () => { // OK! We are calling a maybe-inflight function from an inflight context - print(odd_numbers([1, 2, 3])); + let big_odds = odd_numbers([7, 8, 9]); } ``` -Phase-independent functions are useful for code that does not apply to a -specific phase of execution, such as for data manipulation, utility functions, -etc. +Phase-independent functions are useful for code that is useful across both +execution phases, such as for data manipulation, utility functions, etc. In order for a function to be phase-independent, it must not use any phase-specific features, like calling methods on resources that are only preflight or inflight. Since phase-independent functions can be used inflight, -they inherit the same restrictions as inflight functions, like not being able to -reference mutable data structures or reassignable variables from the outer -scope, and not being able to instantiate new resources. +they inherit the same restrictions as inflight functions, such as: + +- They cannot reference mutable data structures or reassignable variables from +outer scopes. +- They cannot instantiate new resources. Phase-independent methods can also be defined on resources: @@ -833,6 +836,24 @@ resource AwsBucket { } ``` +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 { + name: str; // preflight field + + init() { + // initialize `name` + } + + ?inflight set_name(name: str): void { + // ERROR: cannot mutate a preflight field from a phase-independent context + this.name = name; + } +} +``` + [`▲ top`][top] --- From 912e7c1ebb01c3dfc6e7f7ad368bf6307cd7d0f1 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Mon, 1 Jul 2024 16:10:37 -0400 Subject: [PATCH 04/15] update syntax --- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index d646a530248..25133ef95c6 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -788,8 +788,7 @@ lambda functions, docker, virtual machines etc. #### 1.3.1 Phase-independent code -Code that is not dependent on the phase of execution can be designated as phase-independent using the `?inflight` modifier. -This keyword can be read as "maybe-inflight." +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 it is safe to call the function from either a preflight or inflight context. @@ -797,7 +796,7 @@ either a preflight or inflight context. ```TS -let odd_numbers = ?inflight (arr: Array): Array => { +let odd_numbers = unphased (arr: Array): Array => { let result = MutArray[]; for num in nums { if num % 2 == 1 { @@ -838,7 +837,7 @@ resource AwsBucket { // initialize `name` } - ?inflight object_url(key: str): str { + unphased object_url(key: str): str { // This method references a preflight field (this.name) -- that is // possible in both phases so it is OK! return `s3://${this.name}/${key}`; @@ -857,7 +856,7 @@ resource Bucket { // initialize `name` } - ?inflight set_name(name: str): void { + unphased set_name(name: str): void { // ERROR: cannot mutate a preflight field from a phase-independent context this.name = name; } From d9908f3d472ad030e0041bd4da82282ed7e1c5e5 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Mon, 1 Jul 2024 16:18:35 -0400 Subject: [PATCH 05/15] update RFC --- .../999-rfcs/2023-06-12-language-spec.md | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 25133ef95c6..42104ac93e0 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -790,10 +790,7 @@ lambda functions, docker, virtual machines etc. 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 it is safe to call the function from -either a preflight or inflight context. - - +Using this modifier means that the function can be called from either preflight or inflight contexts. ```TS let odd_numbers = unphased (arr: Array): Array => { @@ -803,14 +800,14 @@ let odd_numbers = unphased (arr: Array): Array => { result.push(num); } } - return result.copy(); // convert from MutArray to Array (immutable) + return result.copy(); }; -// OK! We are calling a maybe-inflight function from a preflight context +// 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 a maybe-inflight function from an inflight context + // OK! We are calling an unphased function from an inflight context let big_odds = odd_numbers([7, 8, 9]); } ``` @@ -818,28 +815,23 @@ let handler = inflight () => { Phase-independent functions are useful for code that is useful across both execution phases, such as for data manipulation, utility functions, etc. -In order for a function to be phase-independent, it must not use any -phase-specific features, like calling methods on resources that are only -preflight or inflight. Since phase-independent functions can be used inflight, -they inherit the same restrictions as inflight functions, such as: +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. -- They cannot reference mutable data structures or reassignable variables from -outer scopes. -- They cannot instantiate new resources. +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 also be defined on resources: +Phase-independent methods can be defined on resources: ```TS -resource AwsBucket { +class AwsBucket { name: str; // preflight field - init() { - // initialize `name` + new() { + this.name = "my-bucket"; } unphased object_url(key: str): str { // This method references a preflight field (this.name) -- that is - // possible in both phases so it is OK! + // allowed in both phases so it is OK! return `s3://${this.name}/${key}`; } } From 103eab8c7302c51698053bc6f9ac2bac54b9d2b6 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Mon, 1 Jul 2024 16:50:45 -0400 Subject: [PATCH 06/15] explain how array.map can work --- .../999-rfcs/2023-06-12-language-spec.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 42104ac93e0..999ac406664 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -855,6 +855,38 @@ resource Bucket { } ``` +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.map` is modeled like the following pseudocode: + +```js +native class Array { + unphased map(f: unphased (T) => U): Array { + // ... + } +} +``` + +At compile-time, since the function is unphased, preflight and inflight versions are generated: + +```js +native class Array { + preflight map(f: preflight (T) => U): Array { + // ... + } + inflight map(f: inflight (T) => U): Array { + // ... + } +} +``` + +This way, when you call `Array.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. + [`▲ top`][top] --- From a8601c60be5069cdd43c1c7d5f96ace036473f2a Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Mon, 1 Jul 2024 16:59:40 -0400 Subject: [PATCH 07/15] clarify --- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 999ac406664..06ee196b333 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -885,7 +885,8 @@ native class Array { } ``` -This way, when you call `Array.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. +Notice how "f" is automatically converted to the appropriate phase. This is possible because the function is unphased. +This way, when you call `Array.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.map` within another unphased function, then the unphased version of `Array.map` is used.) [`▲ top`][top] From 882731999ebbad117711a87f4ab068208941d8b2 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Mon, 1 Jul 2024 17:01:16 -0400 Subject: [PATCH 08/15] add note --- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 06ee196b333..f3571d835f3 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -788,6 +788,8 @@ lambda functions, docker, virtual machines etc. #### 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. From 633d18ac95002910e8e49061154d074846892a45 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 2 Jul 2024 13:58:13 -0400 Subject: [PATCH 09/15] fix typo --- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index f3571d835f3..740e30c355f 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -843,7 +843,7 @@ 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 { +class Bucket { name: str; // preflight field init() { From 4650e3413960c5e20aa71d16343f81506a091bd0 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 2 Jul 2024 13:59:22 -0400 Subject: [PATCH 10/15] remove native class thing --- .../999-rfcs/2023-06-12-language-spec.md | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 740e30c355f..f7ab1969f82 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -867,27 +867,23 @@ You can imagine that when a function is unphased, then "preflight" and "inflight For example, `Array.map` is modeled like the following pseudocode: ```js -native class Array { - unphased map(f: unphased (T) => U): Array { - // ... - } +unphased map(f: unphased (T) => U): Array { + // ... } ``` At compile-time, since the function is unphased, preflight and inflight versions are generated: ```js -native class Array { - preflight map(f: preflight (T) => U): Array { - // ... - } - inflight map(f: inflight (T) => U): Array { - // ... - } +preflight map(f: preflight (T) => U): Array { + // ... +} +inflight map(f: inflight (T) => U): Array { + // ... } ``` -Notice how "f" is automatically converted to the appropriate phase. This is possible because the function is unphased. +Notice how the type of "f" is automatically converted to the appropriate phase. This is possible because "map" is unphased. This way, when you call `Array.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.map` within another unphased function, then the unphased version of `Array.map` is used.) [`▲ top`][top] From cc3d63a95748ec6540452874deca56e1187a5f70 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 2 Jul 2024 17:48:25 -0400 Subject: [PATCH 11/15] fix typo --- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index f7ab1969f82..311584a70d1 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -795,7 +795,7 @@ Code that is not dependent on the phase of execution can be designated as phase- Using this modifier means that the function can be called from either preflight or inflight contexts. ```TS -let odd_numbers = unphased (arr: Array): Array => { +let odd_numbers = unphased (nums: Array): Array => { let result = MutArray[]; for num in nums { if num % 2 == 1 { From 23c067d38b5035eda5d8fa871523264d40e5a186 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Fri, 12 Jul 2024 16:34:21 -0400 Subject: [PATCH 12/15] move to language-reference.md --- docs/api/05-language-reference.md | 102 ++++++++++++++++++ .../999-rfcs/2023-06-12-language-spec.md | 102 ------------------ 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/docs/api/05-language-reference.md b/docs/api/05-language-reference.md index 08ac45f6206..e404d71abfd 100644 --- a/docs/api/05-language-reference.md +++ b/docs/api/05-language-reference.md @@ -689,6 +689,108 @@ 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 (nums: Array): Array => { + let result = MutArray[]; + 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 { + // 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 +class Bucket { + 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.map` is modeled like the following pseudocode: + +```js +unphased map(f: unphased (T) => U): Array { + // ... +} +``` + +At compile-time, since the function is unphased, preflight and inflight versions are generated: + +```js +preflight map(f: preflight (T) => U): Array { + // ... +} +inflight map(f: inflight (T) => U): Array { + // ... +} +``` + +Notice how the type of "f" is automatically converted to the appropriate phase. This is possible because "map" is unphased. +This way, when you call `Array.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.map` within another unphased function, then the unphased version of `Array.map` is used.) + +[`▲ top`][top] + --- ### 1.4 Storage Modifiers diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 311584a70d1..59eff82418e 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -786,108 +786,6 @@ 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 (nums: Array): Array => { - let result = MutArray[]; - 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 { - // 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 -class Bucket { - 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.map` is modeled like the following pseudocode: - -```js -unphased map(f: unphased (T) => U): Array { - // ... -} -``` - -At compile-time, since the function is unphased, preflight and inflight versions are generated: - -```js -preflight map(f: preflight (T) => U): Array { - // ... -} -inflight map(f: inflight (T) => U): Array { - // ... -} -``` - -Notice how the type of "f" is automatically converted to the appropriate phase. This is possible because "map" is unphased. -This way, when you call `Array.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.map` within another unphased function, then the unphased version of `Array.map` is used.) - -[`▲ top`][top] - --- ### 1.4 Storage Modifiers From d2ca05d9b649f5de7bcd838d46e398d11b71af6a Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Fri, 12 Jul 2024 16:54:29 -0400 Subject: [PATCH 13/15] propose mechanism for unphased extern functions --- docs/api/05-language-reference.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/api/05-language-reference.md b/docs/api/05-language-reference.md index e404d71abfd..3c48d088e8d 100644 --- a/docs/api/05-language-reference.md +++ b/docs/api/05-language-reference.md @@ -2085,6 +2085,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: one for each phase. +Each function must be suffixed with `__preflight` or `__inflight` to indicate the phase it is intended for. + +This can be useful when the implementation of the function is different in preflight and inflight phases (for example, inflight functions are allowed to be async). + +```TS +// main.w +class Util { + extern "./helper.js" static unphased randomHex(length: num): str; +} + +// helper.js +const { promisify } = require("node:util"); +const crypto = require("node:crypto"); + +const randomFillAsync = promisify(crypto.randomFill); + +exports.randomHex__inflight = async function (length) { + const buffer = Buffer.alloc(length); + const bytes = await randomFillAsync(buffer); + return bytes.toString("hex"); +} + +exports.randomHex__preflight = function (length) { + const buffer = Buffer.alloc(length); + crypto.randomFillSync(buffer); + return buffer.toString("hex"); +} +``` + ### 5.2.1 Type model The table below shows the mapping between Wing types and JavaScript values, shown with TypeScript types. From 516641f9f620bab45a5007b3a4a38681c9cf64df Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Fri, 12 Jul 2024 16:55:41 -0400 Subject: [PATCH 14/15] undo changes to RFC --- docs/api/05-language-reference.md | 5 +++-- docs/contributing/999-rfcs/2023-06-12-language-spec.md | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api/05-language-reference.md b/docs/api/05-language-reference.md index 3c48d088e8d..1a24ba16a2f 100644 --- a/docs/api/05-language-reference.md +++ b/docs/api/05-language-reference.md @@ -656,6 +656,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 into inflight phase while preflight @@ -680,8 +681,8 @@ 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 constructors 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 diff --git a/docs/contributing/999-rfcs/2023-06-12-language-spec.md b/docs/contributing/999-rfcs/2023-06-12-language-spec.md index 59eff82418e..4e36c5c59e7 100644 --- a/docs/contributing/999-rfcs/2023-06-12-language-spec.md +++ b/docs/contributing/999-rfcs/2023-06-12-language-spec.md @@ -752,7 +752,6 @@ 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 @@ -777,8 +776,8 @@ let handler2 = inflight() => { } ``` -The bridge between preflight and inflight is crossed with the help of immutable data -structures, user-defined structs, resources, and the capture mechanism. +Bridge between preflight and inflight is crossed with the help of immutable data +structures, "structs" (user definable and `Struct`), 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 From ce3d29204f51a0c0132ba0c8ba9a7d22df8cb122 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Thu, 25 Jul 2024 12:03:01 -0400 Subject: [PATCH 15/15] simplify design slightly --- docs/api/05-language-reference.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/api/05-language-reference.md b/docs/api/05-language-reference.md index 1a24ba16a2f..3b810cebf40 100644 --- a/docs/api/05-language-reference.md +++ b/docs/api/05-language-reference.md @@ -2086,8 +2086,7 @@ 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: one for each phase. -Each function must be suffixed with `__preflight` or `__inflight` to indicate the phase it is intended for. +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. This can be useful when the implementation of the function is different in preflight and inflight phases (for example, inflight functions are allowed to be async). @@ -2103,16 +2102,17 @@ const crypto = require("node:crypto"); const randomFillAsync = promisify(crypto.randomFill); -exports.randomHex__inflight = async function (length) { - const buffer = Buffer.alloc(length); - const bytes = await randomFillAsync(buffer); - return bytes.toString("hex"); -} - -exports.randomHex__preflight = function (length) { - const buffer = Buffer.alloc(length); - crypto.randomFillSync(buffer); - return buffer.toString("hex"); +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"); + } } ```