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

Symbol autocasting flag enum #10680

Closed
Blacksmoke16 opened this issue May 6, 2021 · 7 comments
Closed

Symbol autocasting flag enum #10680

Blacksmoke16 opened this issue May 6, 2021 · 7 comments

Comments

@Blacksmoke16
Copy link
Member

One of the main features of symbols is the ability to auto cast into an enum member. However symbol auto casting does not currently support flag enums when more than 1 member is used. E.g.

@[Flags]
enum Foo
  One
  Two
end

pp Foo::One | Foo::Two # => One | Two
pp :two | :one         # => Error: undefined method '|' for Symbol

I propose to expand the autocasting functionality to also support multiple members. This will ultimately need to be done if we want to move forward with removing manual symbols holistically, ref: #6736.

The main usecase for this is to just reduce the amount of typing when multiple members are needed. Currently the expression can be quite long, especially if the enum is namespaced.

@asterite
Copy link
Member

asterite commented May 6, 2021

Can't be done, sorry. What's the type of :two | :one and how do you compute it?

@asterite
Copy link
Member

asterite commented May 6, 2021

To explain a bit more, the compiler will first type-check a call's arguments. Once all types are defined it can start searching matches. In the case of a Symbol type, if the argument is a SymbolLiteral it will try to do autocasting. But here it would be different because there's no way the compiler can type :two | :one.

@straight-shoota
Copy link
Member

I suppose technically, the compiler could get similar special handling for calls to | with symbol literals. Or, expressed differently: SymbolLiteral | SymbolLiteral could just be a language feature, say a CombinedSymbolLiteral. It is only valid where the type can be inferred and autocasting would be similar to SymbolLiteral. The only exception is, it only works with flag enum types.

That sounds feasible to me. Yet, I don't think it's high priority. Although I fully relate to the motivation for this, and it would be really nice to have.

@Blacksmoke16
Copy link
Member Author

As a workaround for now, I recently discovered the .flags macro which allows for Some::Long::Namespace::Colors.flags RED, BLUE instead of Some::Long::Namespace::Colors::RED | Some::Long::Namespace::Colors::BLUE, which is about half the length.

@straight-shoota
Copy link
Member

As long as the first flag is properly typed as a flags enum member, all the following ones can be symbol literals because autocasting of Enum#|(other : self) applies:

Foo::One | :two

This should work pretty well as a simplified workaround. You only need the full path once.
I think it reads better than Enum.flags? which would be another alternative: Foo.flags?(One, Two).

Perhaps this could also help to implement proper autocasting for flags enum combinations.

The expression :a | :b is a Call with receiver SymbolLiteral and argument SymbolLiteral. Calls can be chained when more than two flag members are combined: (:a | :b) | :c (parenthesis added for clarity, they can be omitted). Such a chain of calls should be relatively easy to detect. We would only need to autocast the first receiver to the flag enum type, everything else would be handled by the existing autocasting rules.

The trouble with that is the order of typing: When you have a call foo(:a | :b), the inner expression :a | :b is typed first, before the outer foo(result_of_inner). But the inner expression already errors because Symbol does not implement #|. So autocasting of such a call chain would need to happen earlier.

@Blacksmoke16
Copy link
Member Author

Blacksmoke16 commented Apr 2, 2023

With the introduction of #12900, would this feature even be worth it? Foo[:one, :two] might be sufficient. Is about the same as flags if the enum type's name is long, but if you'd have to type that for #| anyway, my as well just use the #[] and not introduce yet another way to do it.

@oprypin
Copy link
Member

oprypin commented Apr 2, 2023

I think yes, that is enough to close this off. The other approach is already there, is less hacky and indeed with this there would be too many ways to do this.

@oprypin oprypin closed this as not planned Won't fix, can't repro, duplicate, stale Apr 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants