The rule is: No param list () when calling a 'named piece of code' as a sub. The nasty trap is: For single parameter npcs, the () may look like param list (), but are (interpreted as) pass-me-by-value ().
Your set o = MyClass()
calls my MyClass() as a function; that is clear from the assignment.
Your o.doSomething("a")
calls .doSomething as a sub, the () are seen as pass-me-per-value (); doSomething "a"
would be the correct call.
The first part of your o.doSomething("b").doSomethingElse("c", "d")
works, because o.doSomething("b").
is/uses a function call to get an object whose .doSomethingElse()
is to be called; the second part .doSomethingElse("c", "d")
can't work, because it isn't a sub call (no return value used/assigned) and the () can't be seen as pass-me-by-value (). o.doSomething("b").doSomethingElse "c", "d"
would be correct.
The first part o.doSomething("e").doSomethingElse("f", "g").print()
(upto the .print) is a function call (to get the object that will .print), so the () are param list (); the () after .print are wrong, but the compiler/interpreter let them slip by.
To sum up: don't use () when you don't want something back.
WRT comment:
To put it the other way around: use () when you want something back!
set o1 = MyClass()
<- return value should go into o1
set o2 = o.S(1, 2).S(3, 4).S(5, 6)
<- first two return values are needed/used for the next call; last return value goes into o2.
o.S(1, 2).S(3, 4).S 5, 6
<- last return value is discarded - no sub call
The () rule is about what you do (use the return value or not), not about what the npc is.