2
$\begingroup$

C# 3.0 introduced extension methods to "enhance" behavior of existing types without interface bloating to avoid modifying/breaking existing interfaces. This was the first time I learned of such a concept. If I'm not mistaken after 15 years of not working with C#, extension methods are just static methods with their first parameters marked with this (so that it makes extension methods null-safe in terms of calling a method on "this", but obviously static methods obey visibility rules).

So are Kotlin extensions (have never worked with Kotlin, but Kotlin declarations look more natural to me though).

In Java there is no such thing as extension methods (Java 8+ interface default methods are not merely syntax sugar), however javac plugins like Lombok or Manifold allow using extension methods right in Java.

C#, Kotlin, and the Manifold plugin uses the declaration-site approach: the extension method declarations must declare they can be used as extension methods (Manifold, if I remember well, allows declare a whole class of extension methods at once). Lombok uses a call-site approach: the class using extension methods can inject all static methods from specific classes, as if all the injected methods were declared extension methods.

I'm wondering, if all of these implementations use static methods, how would it affect if any scope-visible static methods could be merely used as extension methods not requiring extensions to be declared at declaration-site or call-site at all (staticMethod(obj) -> obj.staticMethod(), or whatever non-dot like obj.:staticMethod())? What implications might this idea have?

I've just came across this question and the idea above seems to be equal to UFCS, so let me rephrase the question above: how would it affect Java and C# that both have mature codebases?

My question does not seem to be a duplicate of this question.

$\endgroup$
4
  • $\begingroup$ Extension methods relate to the Expression Problem (en.wikipedia.org/wiki/Expression_problem), which is "how do we provide a way for third-party code to extend a datatype's representation (i.e. add subclasses) and behaviors (i.e. add methods)?" Object-oriented languages like Java and C# allow you to extend a third-party class's representation, and extension methods partly allow you to extend behaviors. $\endgroup$
    – tarzh
    Nov 30, 2023 at 15:15
  • $\begingroup$ But they don't count as a solution, because extension methods can't be virtual. The fundamental issue is if one third-party codebase were to define a virtual extension method, and a completely separate library defines a new subclass, the new subclass won't have an implementation for the new extension method. $\endgroup$
    – tarzh
    Nov 30, 2023 at 15:15
  • $\begingroup$ What kind of consequences of having all static methods eligible to be used as extension methods are you interested in? Are you asking about the backwards compatibility problems it would create? $\endgroup$ Nov 30, 2023 at 19:42
  • $\begingroup$ @EldritchConundrum Yes, mostly backwards compatibility issues and drawbacks it might bring up (e.g., importing all classes from a package in Java would probably add too much static=extension methods this way). As of now, to me, it looks like syntax sugar that breaks nothing (I still don't know). I think that the C# designers might consider this approach too, but they chose a slightly weird syntax to declare extension methods. If I get the UFCS concept right, the D and Nim designers thought over it and adopted it (did the concept appear in the initial version of the languages or later?) $\endgroup$ Nov 30, 2023 at 22:42

1 Answer 1

1
$\begingroup$

Disclaimer: this answer is based the underlying concepts and nomenclature from C#.

If you make every static method callable like an extension method, one problem becomes prominent that, in the restricted concept of C#, rarely shows up.

If every static method becomes eligible for use in the extension-method syntax, then name clashes will happen. So:

  • You either need a method-call syntax that explicitly names the method to be called, including the name of the static class where the method resides. But then these calls look very different from normal method calls.
  • You might try to do the name resolution by declaration, e.g. with an extension of the "using" syntax. But that would not fit into the current semantics of "using", that it's completely optional, that, by always writing fully-qualified names, there is no need for "using". If you don't want to break that, you'd still need the "fully qualified method name" call-syntax extension.

In C#, it's required to mark the static methods by

  • residing in a static class
  • and using the this keyword with the first parameter.

Typically, there are so few candidates for the name clashes that the problem can be ignored.

My personal opinion is that the syntax sugar called "extension methods" is rather poisonous. The static method that gets called behaves very differently from methods inside the class (is not polymorphic, accepts null, has no access to class internals, ...). So, making both types of call look similar hurts language clarity, IMHO.

If you want the ability to add methods to pre-existing classes, your language should completely throw away the idea that methods are part of a class. Maybe you want to have a look at LISP's object-oriented concept CLOS as an example for such an approach.

$\endgroup$
8
  • 1
    $\begingroup$ @terrorrussia-keeps-killing I have no recollection of why we chose this T over T this, but I was not yet regularly attending design meetings when that feature was designed. I'll check my notes! $\endgroup$ Jan 10 at 5:12
  • 1
    $\begingroup$ @terrorrussia-keeps-killing: This point was debated and coincidentally it was exactly 19 years ago this week. "T this" was rejected because you would have to change the name of the parameter if the static method already existed and was made an extension, and it makes the code less self-documenting. That then left the question of how to signal intention to make an extension method. $\endgroup$ Jan 10 at 7:23
  • 1
    $\begingroup$ @terrorrussia-keeps-killing The rejected options considered were public static extension RETURNTYPE NAME(PARAMLIST) , and [Extension] public static ..., and adding as this after the parameter name and that sort of thing. Language design isn't about finding the perfect, it's about making reasonable compromises that feel right. $\endgroup$ Jan 10 at 7:29
  • 1
    $\begingroup$ @terrorrussia-keeps-killing: To address your earlier point, yes there was also a vigorous debate over whether there should be a mechanism for cherry picking particular static classes, and there were ambiguity-avoidance arguments made on both sides. $\endgroup$ Jan 10 at 7:32
  • 1
    $\begingroup$ @EricLippert Wow, it's a real honor to have my low-interest question commented by a core design team member! This question bugged me for about 15 years (especially since I switched to the Java/JVM stack and I really missed a similar mechanism in the Java language), and it's very nice that you spent your time to share the notes that are 19 years old very this week. What a sign! Thank you! $\endgroup$ Jan 10 at 8:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .