Extension methods

Olexandr Rotan rotanolexandr842 at gmail.com
Sun Apr 28 11:43:56 UTC 2024


I would just like to express some of my concerns regarding pipe operator (
|> syntax ).

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.

>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: other_function() |> new_function() |> baz()
|> bar() |> foo(). This code is readable and, imo, will not catch reader
eyes so much when they are reading the code.

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.

On Sun, Apr 28, 2024 at 2:06 PM Johannes Spangenberg <
johannes.spangenberg at hotmail.de> wrote:

> 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:
>
> 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.
>
> 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.
>
> 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.
>
> 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):
>
> https://github.com/gradle/gradle/issues/27699
>
> 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.
>
> 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.)
>
> 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:
>
> /* 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())
>         }
>     }
> }
>
> If you would seal the interface, you would effectively have an union.
>
> Its common issue users just aren't aware of the existence of certain
> utility classes and end up with nonoptimal code.
>
> 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.
>
> 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.
>
> 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:
> x¬f3("p3")¬f2("p2", "p22")¬f1("p1")
>
> In the JavaScript community, there were some discussions about introducing
> a "pipe operator" some years ago, but it seems to be stalled right now.
>
> https://github.com/tc39/proposal-pipeline-operator
>
> 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.
>
> 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.
>
> 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.
>
> Best Regards,
> Johannes
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240428/6dfae71d/attachment-0001.htm>


More information about the amber-dev mailing list