Skip to content

Commit

Permalink
Allow leading | and & in types (#25)
Browse files Browse the repository at this point in the history
* Propose.

* Little grammar error.

* Semi-colons and language standardized indentations.

* n) -> n.
  • Loading branch information
alexmccord authored Mar 11, 2024
1 parent 85b4d9a commit 2b28353
Showing 1 changed file with 82 additions and 0 deletions.
82 changes: 82 additions & 0 deletions docs/syntax-leading-bar-and-ampersand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Leading `|` and `&` in types

## Summary

Allow the use of `|` and `&` without any types preceding them.

## Motivation

Occasionally, you will have many different types in a union or intersection type that exceeds a reasonable line limit and end up formatting it to avoid horizontal scrolling. Using the English alphabet as an example:

```lua
type EnglishAlphabet = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
```

Or you might just format it for readability:

```lua
type EnglishAlphabet = never
| "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m"
| "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
| "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M"
| "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
```

Currently, there are two solutions to effect it:

1. Moving `=` to the next line
2. Keep `=` on the line and add `never` if using `|`, or `unknown` if using `&`

```lua
-- 1) union:
type Result<T, E>
= { tag: "ok", value: T }
| { tag: "err", error: E }

-- 2) union:
type Result<T, E> = never
| { tag: "ok", value: T }
| { tag: "err", error: E }

-- 1) intersection:
type MyOverloadedFunction
= ((string) -> number)
& ((number) -> string)

-- 2) intersection:
type MyOverloadedFunction = unknown
& ((string) -> number)
& ((number) -> string)
```

OCaml and TypeScript supports this same syntax:

```ocaml
type 'a tree =
| Leaf
| Node of 'a tree * 'a * 'a tree;;
```

```ts
type Tree<T> =
| { type: "leaf" }
| { type: "node", left: Tree<T>, value: T, right: Tree<T> };
```

## Design

This type becomes valid Luau syntax.

```lua
type Tree<T> =
| { type: "leaf" }
| { type: "node", left: Tree<T>, value: T, right: Tree<T> }
```

## Drawbacks

No drawbacks. The parser change is trivial. Instead of trying to parse a simple type and then calling `Parser::parseTypeSuffix`, simply sense whether the current lexeme is `|` or `&` and directly invoke `Parser::parseTypeSuffix` with `nullptr` for the first type and making sure that `nullptr` does not get pushed into the resulting vector. If the current lexeme is not `|` or `&`, then revert to current logic.

## Alternatives

Don't do this and accept one of the solutions already mentioned.

0 comments on commit 2b28353

Please sign in to comment.