Skip to content

Commit

Permalink
feat(compiler)!: deprecate @inflight intrinsic (#6988)
Browse files Browse the repository at this point in the history
This PR deprecates `@inflight()`, an intrinsic function that was added to the Wing language with the aim of streamlining the process of integrating existing TypeScript code into Wing programs.

Today, the language currently supports two mechanisms for interoperating with TypeScript code: `extern` methods and `@inflight`. (See docs: https://github.com/winglang/wing/blob/9c36ce5fd386123744079db65e7e9b22f356a16a/docs/api/03-language/10-using-javascript.md). The main differences between them are:
- `extern` requires you to specify the signature of the function you're using in Wing, while signature of the `@inflight` function can be inferred
- `@inflight` allows you to interact with Wing resources from TypeScript, while `extern` only allows passing non-resource types (e.g. primitives, collections, functions)

The `@inflight` syntax has a few DX problems that make it tricky to use, without clear paths to resolution. The main issues are:
- It's hard for users to correctly set up the type-linkage between Wing and TypeScript.
- Type checking errors inside TypeScript files won't result in compilation errors to your Wing project. To provide a guarantee like this, we would require coupling our compilation process to that of TypeScript's.
- By exposing Wing resources to TypeScript through the "lifts" object, users to lose the permission-inferred experience that they get when writing inflight code in Winglang. If users specify the wrong permissions or "ops", the errors will only be caught at runtime.

The `@inflight` syntax was a valuable experiment and it's possible a variation on this will return in the future.

In the mean time, we're planning to focus on improving the experience of making TypeScript code available to Wing by bringing the type systems closer together, and improving the ergonomics of the language's `extern`, and exploring other ideas like supporting mechanisms for importing TypeScript libraries directly into Wing.

BREAKING CHANGE: `@inflight()` has been removed. We recommend using the existing `extern` method syntax for directly referencing JavaScript or TypeScript code that does not use Wing resources.

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] 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)*.
  • Loading branch information
Chriscbr authored Aug 6, 2024
1 parent 5cb1c5c commit b72cfa8
Show file tree
Hide file tree
Showing 22 changed files with 5 additions and 1,077 deletions.
69 changes: 0 additions & 69 deletions docs/api/03-language/10-using-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,6 @@ id: using-javascript
keywords: [example, javascript, extern, typescript, js, ts]
---

## Creating inflight function from JavaScript/TypeScript file

Inflight closures are an extremely important Wing functionality. Consider the following simple wing program:

```wing example
// main.w
bring cloud;
let bucket = new cloud.Bucket();
bucket.onCreate(inflight (file) => {
log(file);
});
```

Being able to write inflight wing alongside the preflight code is beautiful, but you may want to write the inflight function in a separate file and *language*. The `@inflight` intrinsic function can be used to create an inflight closure from a JavaScript/TypeScript:

```wing
// main.w
bring cloud;
let bucket = new cloud.Bucket();
bucket.onCreate(@inflight("./bucket_create.ts"));
// ^ onCreate expects an `inflight (str): void` function, so the file must export a function with a typescript signature that matches
// ^ Relative (to current file) path to javascript or typescript file
// Note: This must be a static string
```

`wing compile` will generate `.bucket_create.inflight.ts` which will contain all of the information needed for TypeScript type checking and IDE support.
With that, you can create the `bucket_create.ts` file:

```ts
// bucket_create.ts
import inflight from "./.bucket_create.inflight";

export default inflight(async ({}, file) => {
// ^ This is known to be a string, the first positional argument needed for `onCreate`
console.log(file);
});
```

Something missing here is the ability to reference preflight resources inside an inflight function.
Let's create a Queue and pass it to the inflight function while exploring other options:

```wing
// main.w
bring cloud;
let bucket = new cloud.Bucket();
let queue = new cloud.Queue();
bucket.onCreate(@inflight("./bucket_create.ts",
export: "default",
// ^ Optional named export from the file, "default" is the default export
lifts:[{ obj: queue, alias: "myQueue", ops: ["push"] }],
// ^ object to lift, can be any preflight expression
// ^ Optional alias, by default, this will be the variable name passed to obj
// ^ methods to lift, if not provided then all methods will be granted
));
```

```ts
// bucket_create.ts
import inflight from "./bucket_create.inflight";

export default inflight(async ({ myQueue }, file) => {
// ^ inflight interface to your preflight queue
await myQueue.push(file);
});
```

## Using `extern` to expose JavaScript/TypeScript functions in preflight and inflight

When you want to use a JavaScript/TypeScript file anywhere in Wing, you can use the `extern` keyword to expose functions from that file.
Expand Down
45 changes: 0 additions & 45 deletions docs/api/04-standard-library/std/resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,6 @@ id: resource

## Structs <a name="Structs" id="Structs"></a>

### ImportInflightOptions <a name="ImportInflightOptions" id="@winglang/sdk.std.ImportInflightOptions"></a>

Options for the `@inflight` intrinsic.

#### Initializer <a name="Initializer" id="@winglang/sdk.std.ImportInflightOptions.Initializer"></a>

```wing
let ImportInflightOptions = ImportInflightOptions{ ... };
```

#### Properties <a name="Properties" id="Properties"></a>

| **Name** | **Type** | **Description** |
| --- | --- | --- |
| <code><a href="#@winglang/sdk.std.ImportInflightOptions.property.export">export</a></code> | <code>str</code> | Name of exported function. |
| <code><a href="#@winglang/sdk.std.ImportInflightOptions.property.lifts">lifts</a></code> | <code>MutArray&lt;<a href="#@winglang/sdk.std.LiftAnnotation">LiftAnnotation</a>&gt;</code> | Mapping of available symbols to a lift declaration. |

---

##### `export`<sup>Optional</sup> <a name="export" id="@winglang/sdk.std.ImportInflightOptions.property.export"></a>

```wing
export: str;
```

- *Type:* str
- *Default:* "default"

Name of exported function.

---

##### `lifts`<sup>Optional</sup> <a name="lifts" id="@winglang/sdk.std.ImportInflightOptions.property.lifts"></a>

```wing
lifts: MutArray<LiftAnnotation>;
```

- *Type:* MutArray&lt;<a href="#@winglang/sdk.std.LiftAnnotation">LiftAnnotation</a>&gt;
- *Default:* * All possible operations will be available

Mapping of available symbols to a lift declaration.

---

### LiftAnnotation <a name="LiftAnnotation" id="@winglang/sdk.std.LiftAnnotation"></a>

Annotations about preflight data and desired inflight operations.
Expand Down

This file was deleted.

6 changes: 0 additions & 6 deletions examples/tests/invalid/inflight_intrinsic.test.w

This file was deleted.

8 changes: 0 additions & 8 deletions examples/tests/valid/inflight_ts/.example1.inflight.ts

This file was deleted.

63 changes: 0 additions & 63 deletions examples/tests/valid/inflight_ts/.example2.inflight.ts

This file was deleted.

46 changes: 0 additions & 46 deletions examples/tests/valid/inflight_ts/.example3.inflight.ts

This file was deleted.

5 changes: 0 additions & 5 deletions examples/tests/valid/inflight_ts/example1.ts

This file was deleted.

11 changes: 0 additions & 11 deletions examples/tests/valid/inflight_ts/example2.ts

This file was deleted.

5 changes: 0 additions & 5 deletions examples/tests/valid/inflight_ts/example3.ts

This file was deleted.

44 changes: 0 additions & 44 deletions examples/tests/valid/intrinsics.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,3 @@ let currentFile = fs.join(@dirname, filename);
expect.equal(filename, fs.basename(currentFile));
expect.equal(@dirname, fs.dirname(currentFile));
expect.equal(bar.Bar.getSubdir(), fs.join(@dirname, "subdir"));

// @inflight

let counter = new cloud.Counter();
pub class Example {
pub inflight getMessage(): str {
return "message";
}
pub inflight done() {
counter.inc();
}
}

let echo: inflight (str): str = @inflight("./inflight_ts/example1.ts");

let example = new Example();
let funcFunction = @inflight("./inflight_ts/example2.ts",
export: "main",
lifts: [
{ obj: example },
{ obj: example, alias: "exampleCopy", ops: ["getMessage"]},
{ obj: [1, 2, 3], alias: "numbers" },
],
);
let func = new cloud.Function(funcFunction);

let defaultMessage: inflight (): str = @inflight("./inflight_ts/example3.ts",
// Intentionally use { } instead of struct expansion
{ lifts: [{ obj: example }] }
);


test "invoke default function" {
expect.equal(echo("message"), "message");
}

test "invoke inflight function" {
funcFunction("message");
func.invoke("message");
}

test "invoke default with lift" {
expect.equal(defaultMessage(), "message");
}
21 changes: 0 additions & 21 deletions libs/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,15 +584,13 @@ pub enum IntrinsicKind {
/// Error state
Unknown,
Dirname,
Inflight,
}

impl Display for IntrinsicKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IntrinsicKind::Unknown => write!(f, "@"),
IntrinsicKind::Dirname => write!(f, "@dirname"),
IntrinsicKind::Inflight => write!(f, "@inflight"),
}
}
}
Expand All @@ -601,7 +599,6 @@ impl IntrinsicKind {
pub fn from_str(s: &str) -> Self {
match s {
"@dirname" => IntrinsicKind::Dirname,
"@inflight" => IntrinsicKind::Inflight,
_ => IntrinsicKind::Unknown,
}
}
Expand All @@ -613,10 +610,6 @@ impl IntrinsicKind {
Phase::Preflight => true,
_ => false,
},
IntrinsicKind::Inflight => match phase {
Phase::Preflight => true,
_ => false,
},
}
}
}
Expand Down Expand Up @@ -717,20 +710,6 @@ impl Expr {
let id = EXPR_COUNTER.fetch_add(1, Ordering::SeqCst);
Self { id, kind, span }
}

pub fn as_static_string(&self) -> Option<&str> {
match &self.kind {
ExprKind::Literal(Literal::String(s)) => {
// strip the quotes ("data")
Some(&s[1..s.len() - 1])
}
ExprKind::Literal(Literal::NonInterpolatedString(s)) => {
// strip the quotes (#"data")
Some(&s[2..s.len() - 1])
}
_ => None,
}
}
}

pub type ArgListId = usize;
Expand Down
Loading

0 comments on commit b72cfa8

Please sign in to comment.