Why does Crystal support such unusual passing arg usage when invoke method?

Why we support such a misleading usage?

def hello(arg1 = 100, arg2 = 200)
  p [arg1, arg2]
end

hello(arg2 = 300)
p arg2

Guess what’s the result?

If you support it, users will (incorrectly) use it. I just LEARNED it by chance from a shard.

This syntax support is needed in order for macros like property to work amongst other things (IIRC it’s also how you can assign in control expressions). It doesn’t make sense to restrict it to just the macro context given that it’s valid Crystal code regardless of whether it’s in a call, and I’d argue it’s quite useful.

Ruby performs the same in this case, so I think it’s ok.
The syntax highlighting in the editor always helps to distinguish between the parameter name and a regular named variable.

But it’s still somewhat diffierent, I didn’t expect that hello(arg2: 300) would return such a hash {:arg2=>300} in Ruby.

Could you please give a example?

It doesn’t make sense to restrict it to just the macro context

This usage misleading the newbie, especially, those come from languages which use = as keyword argument, e.g. Lua, Python.

foo(port=8080, host=‘localhost’) in the Python same as foo(port: 8080, host: ‘localhost’) in Crystal

Related: Forbid variable assignment in function call · Issue #14722 · crystal-lang/crystal · GitHub

1 Like

I’ve very confused on what the problem is here. This works exactly like I would have expected. When I run this in carc.in it gives me the expected result. https://carc.in/#/r/h9x3

I read the line for hello(arg2 = 300) as arg2 = 300; hello(arg2), which makes perfect sense, we are not assigning to the arg2 parameter, we are creating an arg2 variable and setting the arg1 parameter to the value produced by arg2 = 300. If we were to rewrite the line as hello(arg2: arg2 = 300) it would produce the result I’m guessing you are expecting.

3 Likes

This usage is confusing for users coming from Python.

This works as expected to me.
equals(=) always means assignment, and method arguments are specified by positional index in Ruby.

1 Like

I’m going to sound dismissive here but that’s a them problem. Languages have different features, different syntax, different ideas, etc, etc, you can’t really expect one to cater to another. Crystal and Ruby like the named argument approach as some_fun(arg1: 1, arg2: 2), other languages do not, some languages want to do the named argument approach a different way like SomeFun(arg1 = 10, arg2 = 20). To anyone familiar with Ruby/Crystal’s style, assigning variables in function call is something we can do, and the results aren’t surprising to anyone. If you come from Python things are done different, so you have to learn the proper way of doing things for the language.

In my opinion there is no reason to change this language feature, and I might even be against removing it entirely, since what removing it would do is add another weird edge case of something Crystal could do, but now can’t because it was removed for no reason.

7 Likes

For the record, getter x = 1 is also syntactically passing an assignment as a call argument, it just doesn’t look like that because usually the parentheses here are omitted

4 Likes