Ruby parens-free function calls [was Re: Accessing parent objects]

Chris Angelico rosuav at gmail.com
Mon Mar 26 20:34:17 EDT 2018


On Tue, Mar 27, 2018 at 10:10 AM, Python <python at bladeshadow.org> wrote:
> Ruby touts itself as being a simple language with elegant syntax.
> This thread is my only exposure to it to date, but what I've seen here
> is, frankly, the exact opposite of that.  You should not need a map to
> distinguish function calls from variables, or operands from function
> arguments, let alone have the latter depend on the position of the
> operator token relative to any whitespace between it and the two
> tokens on either side of it.  That's hard to read, complicated,
> inelegant, and just poor syntax. [My opinion, obviously.] That's not
> enough exposure to decide that the language isn't simple and elegant
> overall, but it certainly does induce a strong negative prejudice.

My understanding of Ruby's design model is that it prefers "intuitive"
to "easy to explain". That means there's a lot of magic to make things
behave the way you would expect. The trouble is that you then end up
with edge cases; the benefit is that you get an easy REPL. Let me
compare three REPLs for you (I'm not a Ruby expert so I won't use it
here):

=== JavaScript ===
rosuav at sikorsky:~$ node
> function f() {
... let x = 1
... let y = 2
... return x + y
... }
undefined
> f()
3
>

=== Pike ===
rosuav at sikorsky:~$ pike
Pike v8.1 release 11 running Hilfe v3.5 (Incremental Pike Frontend)
Ok.
> int f() {
>> int x = 1;
>> int y = 2;
>> return x + y;
>> }
> f();
(1) Result: 3
>

=== Python ===
rosuav at sikorsky:~$ python3
Python 3.8.0a0 (heads/literal_eval-exception:ddcb2eb331, Feb 21 2018, 04:32:23)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f():
...     x = 1
...     y = 2
...     return x + y
...
>>> f()
3
>>>

=======

Python and Pike have fairly simple rules. In Python, a statement ends
at the newline, and a function's body must be indented. That means I
have to hit Tab at the beginning of each line until I'm done. (AIUI
other Python front ends do things differently, but I don't have
ipython handy to test.) Pike defines things by semicolons, so I have
to toss a semicolon after each statement, even in the REPL (it's
annoying to have to write "f();" not just "f()"), but the UI doesn't
need indentation, and it just modifies the prompt to indicate nesting
level (which is controlled by braces).

JavaScript, on the other hand, has far more complex rules. The benefit
is that, like with Python, semicolons aren't required, and like with
Pike, indentation can be skipped when you're typing at the REPL. The
downside is that... well, let's try breaking one of those lines.
First, in Pike:

rosuav at sikorsky:~$ pike
Pike v8.1 release 11 running Hilfe v3.5 (Incremental Pike Frontend)
Ok.
> int f() {
>> int x = 1;
>> int y = 2;
>> return
>> x + y;
>> }
>
> f()
>> ;
(1) Result: 3
>

Exactly the same. The semicolons define it. A line break after "f()"
requires the semi before the code runs. The line break after "return"
doesn't change anything.

rosuav at sikorsky:~$ node
> function f() {
... let x = 1;
... let y = 2;
... return
... x + y;
... }
undefined
> f();
undefined
>

Strange... even though I have all the semicolons, it's still borked.
Because JS has complicated rules about implicit semicolons. Yes, this
is an edge case (the "return" statement takes an optional expression,
so it's legal on its own - it'd be different if I broke the line
before or after an equals sign, for instance, as the two lines
wouldn't be legal on their own), but you get more edge cases when you
create more magic.

Ruby appears to have created a corner case here. It's an extremely
weird one, and it's a consequence of the fact that (a) you can call a
function just by naming it OR by naming it and giving a parameter, and
(b) the plus operator has both unary and binary forms. But you can't
change the plus operator without breaking a LOT of people's
expectations (and even if you decide to disallow unary plus, the exact
same problem will happen with unary minus, and you can't get rid of
that one), and the ability for functions to accept optional arguments
is an excellent feature. So the only way to solve this is to remove
the ability for functions to be called without parentheses.

Question: How do you get a reference to a Ruby function? Or are they
not first-class objects?

ChrisA



More information about the Python-list mailing list