Too terse and too alien?

Alessio Stalla alessiostalla at gmail.com
Tue Jun 1 13:56:25 PDT 2010


On Tue, Jun 1, 2010 at 9:48 PM, Mikael Grev <grev at miginfocom.com> wrote:
> On Jun 1, 2010, at 21:35 PM, Alessio Stalla wrote:
>
>> On Tue, Jun 1, 2010 at 8:54 PM, Mikael Grev <grev at miginfocom.com> wrote:
>>> Thanks Ruben, I feel the exact same way and has been going on and off if I was to say something given that almost all on this list seem to be very compiler focused, at least those who respond.
>>>
>>> What won't be understood by the general (95%) developer is the reasons why syntax turned ugly/non-Java/whatever. They will all judge closures by the simplicity of the syntax, how "Java-like" it is, and do so swiftly with a few examples. And mind you, Java-like will mean different things to different people, which makes this all so complex.
>>>
>>> As I think Oracle already know, things like
>>>
>>>  myClosure.(1, 5)
>>>
>>> won't fly very well with the general developer. The reasoning behind the dot, that the name otherwise lives in the same name space as methods, is not understandable by most. And even for me it looks like a compiler purists way of reasoning. myClosure(1, 5) is the only way. It looks Java (my interpretation if it) and my IDE will color/font code it for me to distinguish it from a method call, which it isn't completely unrelated to anyway.
>>
>> I don't like .(...) either, but saying that myClosure(1, 5) is the
>> only way is a bit simplistic imho. This substantially affects an
>> important aspect of the language, that is, how symbols are resolved to
>> program constructs. So far in Java foo() is only legal if foo is a
>> method, while foo.bar is only legal if foo is a variable or a field.
>> By choosing to (or not to) invoke closures like methods, you're making
>> a trade-off between uniformity - everything that can be called is
>> called like foo() - and separation of namespaces - you can't
>> accidentally shadow a method with a variable (or a field!) or
>> vice-versa, depending on how precedence between the two is specified:
>> is foo.bar() a call to method bar() or a call to the closure stored in
>> the field named bar?
>
> Should we optimize for the 1 .% that will have this as an issue, or for the other 999 that have to write the dot all the time?
> IDEs will warn you, and color code it for you to make the distinction. If your IDE can't do that, change. The problem of them not supporting is will pass. The extra dot will never go away.

It's not a matter of IDE support; that will come whatever syntax is
chosen - well, if it's reasonably parseable ;)
It's really a matter of language design: do you want closures to be
indistinguishable from methods (and if so, how do you want to handle
the name clashes?), or do you want to make a difference - at the
language level! - between invocation of a closure and invocation of a
method? Note that the dot is merely *one* way of achieving the latter,
you could well have .invoke or .call or whatever can distinguish a
call on a variable/field from a call to a method. I'm not advocating
the dot in particular :)
Another analogy - properties in C#. They have chosen to make them work
with = so it's indistinguishable if you're assigning to a field or to
a property (not 100% true, there are different naming conventions, but
let's ignore that). They could have chosen instead to make them
accessible via fake set()/get() methods. Which is better? As you can
see, it's not a matter of IDE support at all.

It may be true that only 1% will stumble into a name clash, but if you
design a language you have to specify corner cases as well. I'm
personally more confident with writing foo.invoke(...) and being sure
that foo is a closure than with writing foo(...) and having to
remember complicated precedence rules that might be triggered if
someone else later adds a method called foo, maybe even in a
superclass I don't control at all. It's a personal opinion of course,
I'm just saying that the decision to follow a path or another does
change how the language "feels", and not just the parser.

> And, the problem is still there with the dot. If you forget the dot, you might be in trouble anyway.

Right, but this is a different kind of trouble; it's more like calling
the wrong overload of a method because you forgot a parameter. The
language can only go that far in preventing programmer errors.

> This is a non-issue IMO.
>
>
>> If I'm allowed to divert a bit to non-Java folklore, this issue
>> resembles very closely what in the Lisp world is known as Lisp-1 vs
>> Lisp-2, i.e., one namespace for functions and variables vs two
>> separate namespaces. The first makes functional programming more
>> convenient (there's no need of an explicit operator to call a closure,
>> like the dot in this case), the latter makes name clashes much rarer
>> and so, for example, makes it safer to write macros (i.e., code
>> transformations - making a parallel with Java, you could say that it
>> makes refactoring less problematic, for example). People are very
>> passionate about this issue even today, many years later than it was
>> first raised, so I wouldn't dismiss it as a problem for compiler
>> purists only.
>
> LISP was really a language for the people and it made it big time. :)

Heh, I don't want to get into language flame wars (btw Lisp rocks! :D)
I just cited it to point out that the similar problems have been
discussed countless times in other camps and valid points have been
provided for both alternatives, so that no clear winner has emerged.
To me, this is a sign that none of the two approaches is absolutely
"better", they simply have different, incompatible goals, which can be
summarized to more functional and uniform vs more resilient to code
changes/transformations.

Another source of inspiration, much closer to Java, are delegates in
C#, and they are called like methods, as you suggest. For what is
worth (very little), I have done little C# in my life, but I do
remember being bitten by a delegate with the same name as a method :)

Cheers,
Alessio

>
>>
>>> I also believe that
>>>
>>>   myMethod(Runnable r)
>>>
>>> should be invoked
>>>
>>>   myMethod( #{System.out.pringln("") } );
>>>
>>> and that the parsing should be lenient with all the missing parenthesis and semi colon stuff, if at all possible without creating ambiguities. Same there, I understand the reasoning why someone wants to keep the parsing clean but, again, that is the parser coder's problem and I would happily give the extra resources for it if I were at Oracle. All in the very important name of shaping how this will be received in the blogosphere.
>>>
>>
>> I agree to a certain extent, but keep in mind that complicating the
>> parser too much has a negative effect not only on the parser itself,
>> but on all the tools that parse Java code, including IDEs, so it's
>> again a matter of trade-offs.
>
> Take the pain once for your second part. Take the pain again for the first part, it is worth it. Besides, this is most likely the biggest change to Java that will be in the next 10 years so lets not already now start optimizing for a future that might or might not come. I don't think we should be short sighted, but this is worth it (given my perspective as a user and not a compiler/parser writer).
>
>>
>>> Also,
>>>
>>> #(int i) (i * i)
>>>
>>> should be written
>>>
>>> #(int i) {i * i}
>>>
>>> just like a normal closure. There's no need to make it special and use parenthesis just because it is only one expression in there. That's for the parser/compiler to figure out. Less to learn, easier for the user. And he can write it
>>>
>>> #(int i) {return i * i}
>>>  or even
>>> #(int i) {return i * i;}
>>>
>>> without making it look like something else entirely.
>>
>> I agree to having only a single syntax, #(int i) {return i * i;}. An
>> extra return won't make much difference, and special-casing closures
>> with a single expression as their body makes it slightly inconvenient
>> to change them in case you later need to have more than an expression.
>> Consider also that Java is not a functional language, and its
>> expressions are limited to math, assignments, comparisons, and ternary
>> conditionals; if, try-catch, switch, etc. are not expressions, so I
>> think single-expression closures will be seldom used anyway.
>>
>>>
>>> I don't in any way say that the syntax simplifications above is the ultimate answer, or even better. But anyone that don't understand why those kind of user-space simplifications are important shouldn't have a final call on the syntax IMO.
>>>
>>> KISS, and let the compiler/parser do the heavy lifting.
>>>
>>> Cheers,
>>> Mikael
>>>
>>
>> Cheers,
>> Alessio
>
>


More information about the lambda-dev mailing list