<div dir="ltr">I would just like to express some of my concerns regarding pipe operator ( |> syntax ).<div><br></div><div>I am not opposing it, and it may be just my aesthetic concerns, but what made me like Java syntax so much is that Java code most of the time looks like word.anotherWord. I am not sure how to explain this correctly, but this always gives off the impression of a homogenous code style: no ** operators from python, no object referencing-dereferencing etc I am kind of afraid that a pipe operator in such environment could feel alien to users.</div><div><br></div><div>From what I managed to find in the net, maybe code style convention that Elixir provides could partially fix that: it recommends to add whitespace before and after pipe like so:<font color="#000000"> <span class="gmail-n" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">other_function</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">(</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">)</span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-o" style="background-color:transparent;font-family:inherit;font-size:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235);font-weight:bold">|></span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-n" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">new_function</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">(</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">)</span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-o" style="background-color:transparent;font-family:inherit;font-size:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235);font-weight:bold">|></span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-n" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">baz</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">(</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">)</span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-o" style="background-color:transparent;font-family:inherit;font-size:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235);font-weight:bold">|></span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-n" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">bar</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">(</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">)</span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-o" style="background-color:transparent;font-family:inherit;font-size:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235);font-weight:bold">|></span><span class="gmail-w" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"> </span><span class="gmail-n" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">foo</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">(</span><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">). This code is readable and, imo, will not catch reader eyes so much when they are reading the code.</span></font></div><div><font color="#000000"><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)"><br></span></font></div><div><font color="#000000"><span class="gmail-p" style="background-color:transparent;font-family:inherit;font-size:inherit;font-weight:inherit;box-sizing:border-box;border-width:0px;border-style:solid;border-color:rgb(229,231,235)">Regarding traits, I am not an expert in functional programming, but what catches my eye right away is that, as I understand, this approach is incompatible with var keyword.</span></font></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Apr 28, 2024 at 2:06 PM Johannes Spangenberg <<a href="mailto:johannes.spangenberg@hotmail.de">johannes.spangenberg@hotmail.de</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>
<div>
<p>Here are a few notes from my side, although I am just a fellow
subscriber to the mailing list and not an API designer of the Java
project:</p>
<blockquote type="cite">
<div dir="ltr">
<div>2. Documentation accessibility is a strange point for me to
be fair. Every IDE nowadays is capable of fetching the right
documentation, as well as explicitly mentioning where the
method comes from, as it is done in C#, kotlin, swift and many
other languages. I don't think anyone has ever heard
complaints about poor documentation of LinQ. Unless someone is
writing in notepad, this is poorly applicable.</div>
</div>
</blockquote>
<p>I am regularly annoyed by this issue whenever I read source code
at GitHub which uses Kotlin, C++, JavaScript, or on-demand imports
in Java. I my view, it is less about finding the documentation,
and more about finding the definition of the method. People are
not always in their feature-rich IDE when reading source code.</p>
<p>
</p><blockquote type="cite">Regarding ambiguity, the common practice
(which also is applied in my design) is to prioritize members
over extensions, there is no ambiguity if behaviour is well
defined.This "potentially" could sometimes result in source
incompatibility with some third-party libraries, but as soon as
this is not conflicting with anything from stdlib, that's not
really our concern. Also, I think it's kind of exotic scenario,
and may crash only with utilities from stdlib classes, which
cuts off virtually all production code from the risk group.</blockquote>
<p></p>
<p>Regarding the related risk of collisions, I recently was affected
by the following Gradle bug, which is caused by a collision
between the stdlib of Java and Kotlin (made possible by extension
methods):</p>
<blockquote>
<p><a href="https://github.com/gradle/gradle/issues/27699" target="_blank">https://github.com/gradle/gradle/issues/27699</a></p>
</blockquote>
<blockquote type="cite">
<div dir="ltr">
<div>3. Not overridable. Should they be? I don't think there is
a way to achieve some kind of "polymorphic" extensions, and I
don't think there should be: extension methods should provide
polymorphic target handling, floow LSP etc., not the other way
around.</div>
</div>
</blockquote>
<p>Rust uses a single concept (Traits) which effectively supports
both. Traits also provide a nice solution for the issues behind
Unions. However, while I would like to see something similar to
Traits in Java, I would actually cut the
extension-method-semantic. (i.e. I would make the methods
available only after the object was assigned to the type of the
Trait.)<br>
</p>
<p>For people who don't know Rust, you can think of Traits as
interfaces, which can also declare implementations for already
existing classes. Here is an attempt to map it to some made-up
Java syntax:</p>
<blockquote>
<pre>/* usage */
MyFilesTrait files1 = Path.of("src")
MyFilesTrait files2 = List.of(Path.of("src-1"), Path.of("src-2"))
/* trait definition */
interface MyFilesTrait {
Stream<Path> asPathStream();
for-class Path {
@Override
Stream<Path> asPathStream() {
return Stream.of(this);
}
}
for-class File {
@Override
Stream<Path> asPathStream() {
return Stream.of(this.toPath());
}
}
for-class Collection<? extends MyFilesTrait> {
@Override
Stream<Path> asPathStream() {
return this.stream().flatMap(item -> item.asPathStream())
}
}
}
</pre>
</blockquote>
<p>If you would seal the interface, you would effectively have an
union.<br>
</p>
<blockquote type="cite">
<div dir="ltr">
<div>Its common issue users just aren't aware of the existence
of certain utility classes and end up with nonoptimal code.</div>
</div>
</blockquote>
<p>This seems mostly related to the auto-completion of the IDE. I
don't think the extension functions would be necessary for that,
but I agree that extension methods would guide IDEs and kind of
force them to implement the discovery during auto-completion.<br>
</p>
<blockquote type="cite">
<div dir="ltr">
<div>Code without extension methods is always much more verbose,
if the API is supposed to be fluent developer could end up
with deep nested invocations.<br>
</div>
</div>
</blockquote>
<p>
</p><blockquote type="cite">
<div>as for "the illusion of belonging" it could be addressed by
introducing some special operator instead of dot to highlight
the difference, e.g. something like:</div>
<div><span style="font-family:monospace">x¬f3("p3")¬f2("p2",
"p22")¬f1("p1")</span></div>
</blockquote>
<p></p>
<p>In the JavaScript community, there were some discussions about
introducing a "pipe operator" some years ago, but it seems to be
stalled right now.<br>
</p>
<blockquote>
<p><a href="https://github.com/tc39/proposal-pipeline-operator" target="_blank">https://github.com/tc39/proposal-pipeline-operator</a><br>
</p>
</blockquote>
<blockquote type="cite">
<div dir="ltr">JS also has its own unique way of extending APIs.
The only modern widely-used language that does not have
extension methods is Python, but that only applies to built-ins,
custom classes could be extended as well.<br>
</div>
</blockquote>
<p>If you are referring to the possibility to assign new properties
to prototypes (JS) or classes (Python), than I think it is
considered bad practice in parts of both communities.</p>
<p>From my point of view, extension functions as implemented by
Kotlin are not really a good fit for Java. Java and Kotlin have
greatly different design philosophies. While Kotlin focuses more
on convenience (or ease of use), Java has a much bigger focus and
language simplicity. The complex method resolution required for
this style of extension functions doesn't seem to fit well for
Java in my opinion. Other solutions like the pipeline operator
seem like a better fit to me. While they also increase the
complexity by introducing a new operator, it keeps the method
resolution quite simple and doesn't hide the complexity as done by
extension methods. As a result, it also doesn't suffer from the
accessibility issues of extension methods. </p>
<p>Best Regards,<br>
Johannes<br>
</p>
</div>
</blockquote></div>