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

Comma binds closer to Proc type literals than generic type vars #9973

Open
HertzDevil opened this issue Nov 25, 2020 · 2 comments
Open

Comma binds closer to Proc type literals than generic type vars #9973

HertzDevil opened this issue Nov 25, 2020 · 2 comments

Comments

@HertzDevil
Copy link
Contributor

While writing #9964 I was surprised to find that the -> literal grabs surrounding commas even inside a generic type list, so all types to the left become Proc arguments and the types after the -> become the return type:

Tuple(Int32, Bool ->)          # => Tuple(Proc(Int32, Bool, Nil))
Tuple(Int32, Bool -> String)   # => Tuple(Proc(Int32, Bool, String))
Tuple(-> Int32, Bool)          # => Tuple(Proc(Int32), Bool)
Tuple(String -> Int32, Bool)   # syntax error

(While typeof itself can appear inside a type name, its own arguments are Crystal expressions, not types, so the only parenthesized argument lists that can appear in a type name are generics.)

3 out of those 4 cases mandate parentheses around the -> to make the types 2-tuples:

Tuple(Int32, (Bool ->))        # => Tuple(Int32, Proc(Bool, Nil))
Tuple(Int32, (Bool -> String)) # => Tuple(Int32, Proc(Bool, String))
# Tuple(-> Int32, Bool)        # as before
Tuple((String -> Int32), Bool) # Tuple(Proc(String, Int32), Bool)

I find this rather counterintuitive; I think the type grammar would look better if -> only grabs one argument inside a generic type list when there are no extra parentheses:

Tuple(Int32, Bool ->)          # => Tuple(Int32, Proc(Bool, Nil))
Tuple(Int32, Bool -> String)   # => Tuple(Int32, Proc(Bool, String))
Tuple(-> Int32, Bool)          # => Tuple(Proc(Int32), Bool)
Tuple(String -> Int32, Bool)   # => Tuple(Proc(String, Int32), Bool)

# the 1-tuples from the original snippet would then require double parentheses
Tuple((Int32, Bool ->))        # => Tuple(Proc(Int32, Bool, Nil))
Tuple((Int32, Bool -> String)) # => Tuple(Proc(Int32, Bool, String))
@asterite
Copy link
Member

asterite commented Dec 2, 2020

I think this is a good change and it will solve the issue of sometimes having to surround some expressions in parentheses to avoid accidentally specifying a proc type with multiple arguments.

That said, that syntax is used a lot in C bindings and it will be very annoying to always surround things with parentheses.

@straight-shoota
Copy link
Member

straight-shoota commented Dec 2, 2020

For the record, the obligatory parenthesis can also be limited to the proc's argument types, which IMO looks less annoying:

Tuple((Int32, Bool) ->)        # => Tuple(Proc(Int32, Bool, Nil))
Tuple((Int32, Bool) -> String) # => Tuple(Proc(Int32, Bool, String))

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

3 participants