Unlike with other languages, there is no ambiguity here.
In python, you don't know when you dir() an object whether your dealing with a property or method. In Ruby, you are guaranteed it's always a method. A perk is that you don't need parens.
And I think I’m in the minority here, but I prefer the cleanliness of optional punctuation.
That feature also allows for fluent DSLs (which seem to be falling out of favor for various reasons), but is one of the reasons I still love Ruby.
To look at the other side, I hate having to constantly add semicolons and parens in other languages. Makes my poor fingers hurt even more than they usually do.
I think this kind of falls in line with the idea of Ruby giving you sharp knives[0].
Like you I used to have a love / hate relationship with this because it feels inconsistent to sometimes use parenthesis and sometimes not. In a world where you just want to run Python's Black code formatter and always use parenthesis, having to think about when to use parenthesis feels really tedious.
But I think nowadays I'm happy that they are optional because when you want them to be optional it creates a really nice aesthetic to your code.
For example with Rails you can do: `2.days.ago` and then there's also things that feel like a mini DSL such as `validates :title, :body` or `belongs_to :users`. These just look nicer without parenthesis.
With that said, I do use parenthesis more often than not but it's nice to avoid using them when it makes something look nicer visually.
It's really nice because it means you don't have to pre-wrap all of your properties with accessor methods. They can just be properties, and if something changes, you swap the property out with a decorated method and you don't need to change any code.
Optional parentheses exist because it is impossible for there to be ambiguity: after a dot, if a name starts with a lowercase letter it is a method (and the reference is a call). If it starts with an upper case letter, it is constant (which is not callable as a method.) There are no other possibilities.
“Attributes” in Ruby are literally just getter methods (possibly paired with setter methods.) There is no object-dot-field access to fields (instance variables exist, but outside access to them doesn't use that syntax, but instead goes through a different set of method calls: Object#instance_variable-(get/set)
Knowing that in Ruby a method will always return its last executed line unless explicitly returned early makes (at least for me) things very easy to get.
A method with two branches will always return the last line of the executed branch, thus you will always know what the response could be.
But this (IMO) is because Ruby OOP where the main concept is that when you are invoking a method you are actually sending a message to that object thus you should get a response. So Ruby makes this way of working with objects universal.
Once you call a method you will get a response.
I find it strange the other way around, working in languages where I need to put return even on the last line of the method. For example after almost one year of full time backend development in Ruby, I needed to write some JS code. I debugged for half an hour why a short function that I wrote was not working properly until I got it: I need to add 'return' to that last line to get the actual result. This was strange for me :)
How about optional dots in Scala?
if (Day today saturday) println("It's Saturday")It presents OOP in Ruby in such a beautiful way and it takes a while to start thinking like this.
It does make code more difficult to understand for exactly the reason you mentioned, and furthermore it can sometimes be disallowed based on the rest of the expression (such that parens are required).
In my own Ruby code, I often use parens when they are optional.