From 4f3753df1305f2c115e4bcc42e2e9a8d37568bfb Mon Sep 17 00:00:00 2001 From: kalrnlo Date: Mon, 11 Nov 2024 17:04:12 -0500 Subject: [PATCH 1/3] RFC: Result type function --- docs/result-type-operator.md | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/result-type-operator.md diff --git a/docs/result-type-operator.md b/docs/result-type-operator.md new file mode 100644 index 00000000..5ebc3166 --- /dev/null +++ b/docs/result-type-operator.md @@ -0,0 +1,121 @@ +# `result` type function + +## Summary + +This RFC proposes the addition of a new type function `result`, which can be used to make result types in luau. That are consistent with the idiom established by the [pcall and xpcall global functions](https://luau.org/library#global-functions) in the language currently. + +## Motivation + +Currently, in order to properly type out a result one would have to write the following: + +```luau +type Mrow = ((meow: string, mrrp: number) -> (true, string)) & + ((meow: string, mrrp: number) -> (false, nil)) + +--[[ + Note: the function is being cast to any, + as this rfc is being written with the assumption the developer is using --!strict mode +--]] +local mrow: Mrow = (function(meow: string, mrrp: string) + if math.random() > .5 then + return true, `{meow} {mrrp}` + else + return false, nil + end +end) :: any +``` + +Due to to that making the developer have to write an overloaded function, and with overloaded functions not consistently working. Most will instead do this: + +```luau +type Mrow = (meow: string, mrrp: number) -> (boolean, string?) +``` + +Leading to having the type burden passed onto the developer using the function, as now they have to cast the result if they want to avoid type errors: + +```luau +local success, result = mrow("cat food", ":3") + +if success then + local new_result: string = result :: any + print(`new food of type: {new_result}!`) +else + error("no food :(") +end +``` + +Some may take a diffrent approach by making their own result type, where they break from the luau idiom: + +```luau +type Result = { + ok: true, + value: S +} | { + ok: false, + value: F +} + +local function mrow(meow: string, mrrp: string): Result + if math.random() > .5 then + return { ok = true, value = `{meow} {mrrp}` } + else + return { ok = false, value = nil } + end +end +``` + +## Design + +The functionality of the `result` type function is special, with it being an exception as it'll create a type-pack union. Thus using the result type function will not require the developer to write an overloaded function type. + +```luau +type result +``` + +```luau +local function mrow(meow: string, mrrp: string): result + if math.random() > .5 then + return true, `{meow} {mrrp}` -- wont error + + return true, nil -- Error message: type 'nil' is not of type 'string' + else + return false, nil + end +end +``` + +## Drawbacks + +This type function will need to be special cased, complicating maintenance for the type solver. But, having a result type would allow for pcall and xpcall to get proper types in the future. Where calls to `error` within a function being pcalled would set the second typepack in the result type to the type of the value `error` was called with. + +```luau +local function mrow(meow: string, mrrp: string): string + if math.random() > .5 then + return `{meow} {mrrp}` + else + error("no cats allowed") + end +end + +-- inferred as result +local success, result = pcall(mrow, "cat food", ":3") +``` + +## Alternatives + +Allow for type pack unions to be written by developers, with the syntax probably looking like this: + +```luau +local function mrow(meow: string, mrrp: string): (true, string) | (false, nil) + -- code here +end +``` + +With this example breaking backwards compadibility with some types developers may have written already, as today the following is allowed: + +```luau +-- inferred as: ((meow: string, mrrp: string) -> (true, string)) | false +type mrrp = (meow: string, mrrp: string) -> (true, string) | false +``` + +Do nothing, and leave it up to developers if they want to write overloaded functions, or make their own result type. From 97f738eaf0762baa9e9927ea7bbe7ab63a93d254 Mon Sep 17 00:00:00 2001 From: witchiest <62822174+gaymeowing@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:19:51 -0500 Subject: [PATCH 2/3] Add reasoning for not preferring type-pack union syntax --- docs/result-type-operator.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/result-type-operator.md b/docs/result-type-operator.md index 5ebc3166..adda6bb2 100644 --- a/docs/result-type-operator.md +++ b/docs/result-type-operator.md @@ -104,18 +104,16 @@ local success, result = pcall(mrow, "cat food", ":3") ## Alternatives Allow for type pack unions to be written by developers, with the syntax probably looking like this: - ```luau local function mrow(meow: string, mrrp: string): (true, string) | (false, nil) -- code here end ``` - With this example breaking backwards compadibility with some types developers may have written already, as today the following is allowed: - ```luau -- inferred as: ((meow: string, mrrp: string) -> (true, string)) | false type mrrp = (meow: string, mrrp: string) -> (true, string) | false ``` +Although it should be mentioned that result types are the only valid usecase for type pack unions, and just having a result type would remove a potential footgun of someone using type pack unions for something thats not a result type. Do nothing, and leave it up to developers if they want to write overloaded functions, or make their own result type. From e7054b87dce3cd1b8eb6c5886dac58c8bea8bb0d Mon Sep 17 00:00:00 2001 From: witchiest <62822174+gaymeowing@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:25:01 -0500 Subject: [PATCH 3/3] Grammar Fixes * Include `-` in all spellings of type-pack * Make reason for preferring not having type-pack union syntax more explicit. --- docs/result-type-operator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/result-type-operator.md b/docs/result-type-operator.md index adda6bb2..92a8b33f 100644 --- a/docs/result-type-operator.md +++ b/docs/result-type-operator.md @@ -103,7 +103,7 @@ local success, result = pcall(mrow, "cat food", ":3") ## Alternatives -Allow for type pack unions to be written by developers, with the syntax probably looking like this: +Allow for type-pack unions to be written by developers, with the syntax probably looking like this: ```luau local function mrow(meow: string, mrrp: string): (true, string) | (false, nil) -- code here @@ -114,6 +114,6 @@ With this example breaking backwards compadibility with some types developers ma -- inferred as: ((meow: string, mrrp: string) -> (true, string)) | false type mrrp = (meow: string, mrrp: string) -> (true, string) | false ``` -Although it should be mentioned that result types are the only valid usecase for type pack unions, and just having a result type would remove a potential footgun of someone using type pack unions for something thats not a result type. +Although it should be mentioned that result types are the only valid usecase for type-pack unions, and just having a result type instead of general type-pack union syntax would remove a potential footgun. Of someone using type pack unions for something thats not a result type. Do nothing, and leave it up to developers if they want to write overloaded functions, or make their own result type.