Skip to content
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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
86 changes: 84 additions & 2 deletions docs/04-reference/winglang-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -765,15 +767,95 @@ 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
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.
Chriscbr marked this conversation as resolved.
Show resolved Hide resolved
This keyword can be read as "maybe-inflight."

Using this modifier means that it is safe to call the function from
either a preflight or inflight context.

<!-- TODO: is it also safe to pass the maybe-flight function by value, or to capture it? -->

```TS
let odd_numbers = ?inflight (arr: Array<num>): Array<num> => {
let result = MutArray<num>[];
for num in nums {
if num % 2 == 1 {
result.push(num);
}
}
return result.copy(); // convert from MutArray to Array (immutable)
};

// OK! We are calling a maybe-inflight 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
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.

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:

- 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:

```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}`;
}
}
```

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]

---

### 1.4 Storage Modifiers
Expand Down