Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] union type #442

Open
b1ek opened this issue Sep 3, 2024 · 17 comments
Open

[Feature] union type #442

b1ek opened this issue Sep 3, 2024 · 17 comments
Labels
enhancement New feature or request syntax

Comments

@b1ek
Copy link
Member

b1ek commented Sep 3, 2024

Is your feature request related to a problem? Please describe.
this type definition is valid in typescript:

let thing: string | null

i'd love to be able to do the same in amber

Describe the solution you'd like

let thing: String | Null
thing = "abc"
thing = null
if thing is Text {
    echo "its text!" // never executed
}

Describe alternatives you've considered
Generic types, but.. meh

Additional context
Add any other context or screenshots about the feature request here.

@b1ek b1ek added enhancement New feature or request syntax labels Sep 3, 2024
@github-project-automation github-project-automation bot moved this to 🆕 New in Amber Project Sep 3, 2024
@b1ek b1ek changed the title [Feature] "or" type [Feature] union type Sep 3, 2024
@hdwalters
Copy link
Contributor

hdwalters commented Sep 3, 2024

let thing: string | null

It depends on whether you want (i) any combination of types, or (ii) just nullable/optional types. I can't see much use for the former, because the operations you can perform on multiple types are limited, basically just + for numbers, strings and arrays.

If the latter, you might consider allowing a question mark at the end of the type, like C# and Kotlin; so Num?, Text?, [Text]? etc. You may even want [Text?]?. I suspect this would be easier to parse, too.

@b1ek
Copy link
Member Author

b1ek commented Sep 3, 2024

let thing: string | null

It depends on whether you want (i) any combination of types, or (ii) just nullable/optional types. I can't see much use for the former, because the operations you can perform on multiple types are limited, basically just + for numbers, strings and arrays.

If the latter, you might consider allowing a question mark at the end of the type, like C# and Kotlin; so Num?, Text?, [Text]? etc. You may even want [Text?]?. I suspect this would be easier to parse, too.

any combination of types. question mark after type is already taken, it means that it is fallible

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

okay so there has been some controversy about how union types should be equal to other types and each other. i think we should do it like this:

  1. a union type is equal only to another union that has the exact same types under it, no matter the order
  2. a union type is converted to a singular type with an if union_variable is Type statement, only inside that if block

@Ph0enixKM @Mte90 @mks-h

@mks-h
Copy link
Member

mks-h commented Sep 15, 2024

Here's how I see this:

The union type must function just like the Failable and Generic Array types in #472. This means that:

  1. Arguments of a type that is a subset of the parameter's type must be allowed.
  2. The return statement must allow value to be returned if its type is a subset of the defined return type.
  3. The is operator must continue to check the actual type of the value.

Additionally, the union type must not allow failable types as part of the union. The union type itself must be failable, instead. Otherwise it won't come together with the error handling logic.

The groundwork for the subset logic is already laid out in #472, which I intend to merge before this one. Then you will have to put your union logic on top of it.

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

how would it work that the whole union is failable, from the syntax point of view? do we just assert that all types in union are either failable or non failable? or something like (Text | Null)??

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

Arguments of a type that is a subset of the parameter's type must be allowed

i assume that you mean in the function arguments, right?

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

i think that any singular type can be automatically converted to union with the same type in there Text ==> Text | Null, but not in reverse Text | Null ==> Text - that would require an if var is Type check

@mks-h
Copy link
Member

mks-h commented Sep 15, 2024

I'm not exactly sure how the syntax should look, but it should probably be (Text | Num)? The extra parentheses are kind of a hassle, but I don't think we can do anything about it. That is, unless we want to change the failable syntax to fun whatever()?: Text, ():? Text, or (): ?Text.

i assume that you mean in the function arguments, right?

Yes.

automatically converted

We must not convert any types. The compiler has to keep track of the actual types, so that the is type checking works. Instead of converting a type, we simply check if it is allowed to pass where another (wider/superset) type was expected.

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

we could also change failables to Failable<T>

@Ph0enixKM
Copy link
Member

The [Text?]? makes no sense as of right now. Let's later later add parentheses syntax so that it can parse this case:

fun foo(): (Text | Num)? {}

because Text | Num? looks as if the Num was a failable type. And so this should be forbidden and compiler should suggest adding parentheses around the union type expression when function is failable.

image

@b1ek
Copy link
Member Author

b1ek commented Sep 15, 2024

i have a feeling that failable types should not be implemented as types, but rather as a function modifier, like:

pub failable function get_data(): Text {
    return $curl https://data.com$?
}

@mks-h
Copy link
Member

mks-h commented Sep 16, 2024

we could also change failables to Failable

This is a whole word more verbose than the (T)?

pub failable function get_data(): Text

That's also quite verbose.

@Ph0enixKM
Copy link
Member

One idea is that we could move the ? to the function name:

fun foo?(): Text 

@mks-h
Copy link
Member

mks-h commented Sep 16, 2024

Question mark right after a word makes it a bit funny to read, lol. Reads like a question.

@b1ek
Copy link
Member Author

b1ek commented Sep 18, 2024

... more verbose ...

... quite verbose

so what is your point being?

@mks-h
Copy link
Member

mks-h commented Sep 18, 2024

The point is that none of them is any better than the (T | U)? syntax, in terms of being succinct (which is the issue we try to solve). So far, the only better options I see are:

  1. fun name()?: T | U
  2. fun name?(): T | U by @Ph0enixKM

The failable modifier before fun keyword is not the worst idea, but I don't like to have a lot of keywords before fun.

@b1ek
Copy link
Member Author

b1ek commented Sep 22, 2024

we should discuss failable types further in #478

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request syntax
Projects
None yet
Development

No branches or pull requests

4 participants