winglang / wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
https://winglang.io
Other
5.07k stars 198 forks source link

Bang operator in function signatures #5645

Open Chriscbr opened 10 months ago

Chriscbr commented 10 months ago

Use Case

This is a proposal for a piece of syntax sugar. It does not fundamentally change the type system.

Suppose you have a situation where an API accepts a function whose parameter is an optional type. Because of this, the code that calls the function may call it with nil or a concrete value of the appropriate type:

class MyService {
  handler: inflight (str?): void;
  new(handler: inflight (str?): void) {
    this.handler = handler;
  }

  inflight doStuff() {
    // I might call handler with a string...
    this.handler("");
    // ...or I might call it with nil...
    this.handler(nil);
  }
}

With an API like this, it's not possible to pass a function whose type is non-optional:

// Invalid because `m` is typed as `str` instead of `str?`
// This reflects the reality that `m` might be a nil value,
// so `m.length` would fail
new MyService(inflight (m: str): void => {
  log("length of string: {m.length}")
});

The solution is to let m be str? (or let the compiler infer that type annotation for you) and handle each case:

new MyService(inflight (m: str?): void => {
  if let mStr = m {
    log("length of string: {mStr.length}");
  } else {
    log("no string");
  }
});

In some scenarios, you might know for sure that the string will always be there. In this case, it's also possible to use the ! operator. This unwraps the value, and raises a runtime error immediately if the assumption was nil. It's a way to bypass the safety of the type system:

new MyService(inflight (m: str?): void => {
  log("length of string: {m!.length}");
});

But this syntax could be verbose if you are using m! a lot in the body of the function.

As an alternative, we propose adding the "!" operator to the function signature:

inflight (m: str!): void => {
  log("length of string: {m.length}");
}

The meaning of this signature is that m is a str? (it's treated the same as str? in the type system), but the value will be automatically unwrapped at the beginning of the function, letting you use m inside the body as if it's a plain string.

Proposed Solution

No response

Implementation Notes

No response

Component

Language Design

Community Notes

eladb commented 10 months ago

Nice! I like it.