winglang / wing

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

Add Support for Generic Function Overloading #4035

Open skorfmann opened 1 year ago

skorfmann commented 1 year ago

Feature Spec

Winglang now supports the ability to overload functions based on parameter types, a feature prevalent in languages like TypeScript. This flexibility is particularly valuable for diverse scenarios, including creating type-specific expectations or assertions in testing frameworks.

Before, in Winglang, you would have to tailor all function signatures to their type.

expectEqualStr("hello", "hello")
expectEqualNum(1, 1)

Now, with generic overloading, you can do build language elements like this, while maintaining full type safety

expect("hello").toEqual("hello");
expect(1).toEqual(0);
expect(1).toBeGreaterThan(0);

Benefits:

Integration with Static Type Analysis:

With this release, Winglang's type inference mechanism has been refined to correctly identify and provide feedback on overloaded functions, ensuring a smooth and predictable developer experience.

Use Cases

This came up in #3976 - so certainly around assertions / expectations. But I could also see some overlap with #104 and the recent examples around type safe database clients.

Implementation Notes

A simple - not working - implementation tested in #3976 ```ts import { InflightClient } from "../core"; export class StringExpectations { /** * @internal */ public static _toInflightType(): string { return InflightClient.forType(__filename, this.name); } constructor(private expected: string) {} /** * * @param actual The string to test. * @throws Will throw an error if the actual string does not match the expected string. * @returns True if the strings are equal, throws otherwise. * @inflight */ public toEqual(actual: string): boolean { if (this.expected !== actual) { throw new Error(`Expected "${this.expected}", but got "${actual}".`); } return true; } } export class NumberExpectations { constructor(private expected: number) {} public toEqual(actual: number): boolean { if (this.expected !== actual) { throw new Error(`Expected "${this.expected}", but got "${actual}".`); } return true; } public toBeGreaterThan(actual: number): boolean { if (this.expected <= actual) { throw new Error(`Expected "${this.expected}" to be greater than "${actual}".`); } return true; } }; /** * The Assert class provides methods for making assertions in tests, * such as comparing two strings for equality. * @inflight * /** * @typeparam T1 */ export class Expectations { /** * @internal */ public static _toInflightType(): string { return InflightClient.forType(__filename, this.name); } /** * Compares two values for equality. * @param expected The expected value. * @param actual The value to test. * @throws Will throw an error if the actual value does not match the expected value. * @returns True if the values are equal, throws otherwise. */ public static expect(expected: string): StringExpectations; /** * Compares two values for equality. * @param expected The expected value. * @param actual The value to test. * @throws Will throw an error if the actual value does not match the expected value. * @returns True if the values are equal, throws otherwise. */ public static expect(expected: number): NumberExpectations; public static expect(expected: any): any { if (typeof expected === "string") { return new StringExpectations(expected); } else if (typeof expected === "number") { return new NumberExpectations(expected); } // You can continue the pattern for other types as necessary. throw new Error(`No expectations available for type "${typeof expected}".`); } } Expectations.expect("foo").toEqual("foo"); Expectations.expect(1).toBeGreaterThan(0); ``` ``` bring util; bring testing; test "inflight base64" { // this succeeds testing.Expectations.expect("hello").toEqual("hello"); // this throws an error testing.Expectations.expect(1).toBeGreaterThan(0); } ```

Component

Language Design, Compiler, SDK

Community Notes

Chriscbr commented 1 year ago

I'm not sure this proposal makes a strong case for bloating the language with function overloading. All of the use cases mentioned can be achieved (in fewer characters) using assert:

assert("hello" == "hello");
assert(1 == 1);
assert(1 > 0);
skorfmann commented 1 year ago

Both versions mentioned above - expectEqualStr("hello", "hello") and expect("hello").toEqual("hello"); are mostly about providing better error feedback for the specific cases or additional features (e.g. thinking about assertThrows or snapshots). I don't think that assert("hello" == "hello"); is the same in that regard. However, what I'm assuming what you're saying is that expectEqualStr("hello", "hello") is probably good enough.

Will check if there are other examples where this could help :)

github-actions[bot] commented 9 months ago

Hi,

This issue hasn't seen activity in 60 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!

github-actions[bot] commented 7 months ago

Hi,

This issue hasn't seen activity in 60 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!

github-actions[bot] commented 4 months ago

Hi,

This issue hasn't seen activity in 90 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!

github-actions[bot] commented 1 week ago

Hi,

This issue hasn't seen activity in 90 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!