JL0PD
JL0PD

Reputation: 4488

Why instance methods can't be called as static methods

In python I can write code like this:

class MyClass:
    def foo(self, bar):
        return str(self) + bar

    def __str__(self):
        return "MyClass"


a = MyClass()
r1 = a.foo('1') # MyClass1
r2 = MyClass.foo(a, '1') # MyClass1

Which will define MyClass and allows me to use foo as static method, passing instance as first parameter. It can be performed because of high dynamism of language.

But why I can't use same principle in .Net languages?

type MyClass() =
    member this.Foo bar = this.ToString() + bar
    override _.ToString() = "MyClass"

let a = MyClass()
let r1 = a.Foo "1" // MyClass1
let r2 = MyClass.Foo a "1" // Compilation error
class MyClass
{
    public string Foo(string bar) => this.ToString() + bar;
    public override string ToString() => "MyClass";
}
class Program
{
    static void Main(string[] args)
    {
        var a = new MyClass();
        var r1 = a.Foo("1"); // MyClass1
        var r2 = MyClass.Foo(a, "1"); // Compilation error
    }
}

this implicitly passed as first parameter to any instance method during compilation and from perspective of compiler call a.Foo(b) is equivalent to MyClass(a, b).

Lack of this feature causes to write additional code to help type inference. So instead of writing this:

let startsWithAny str (chars: char[]) =
    chars.Any (String.StartsWith str)

I must write this:

let startsWithAny (str: string) (chars: char[]) =
    chars.Any (fun c -> str.StartsWith c)

or this:

let startsWithAny str (chars: char[]) =
    chars.Any (fun c -> (str :> string).StartsWith c)

Update

Due to some misunderstanding I'm extending my question.

At the moment I'm mostly write on F# and trying to take advantage of each feature include currying, partial application, etc.

Functional style gives such advantages as type inference, clarity, composability. So to use all of them, needs to remove all code that behaves unpredictably (virtual method call, overloads)

I want to write code as this:

let startsWithAny str (chars: char[]) =
    chars.Any (String.StartsWith str)
    // or better
    // Array.any chars (String.StartsWith str) // ideal world

As @DmitriTsoy said, it can be achieved with extension methods:

type System.String with
     static member StartsWith((str: string), (char: char)) =
         str.StartsWith char

But such extension makes it impossible to use partial application and results in resort of using lambdas fun c -> str.StartsWith c

Another way to implement extension:

type System.String with
     static member StartsWith (str: string) =
        Func<char,bool>(str.StartsWith)

It takes string and returns partially applied StartsWith. This leads to weird C# call:

chars.Any(c => String.StartsWith("someStr")(c))

but nice F#:

chars.Any (String.StartsWith str)

Question: Why compiler can't transform such static-style call to instance-style? If no static method found, try to find instance method and convert call to instance-style at compile time

Upvotes: 1

Views: 298

Answers (1)

Guillaume S.
Guillaume S.

Reputation: 1545

The simple answer is that C# was designed like this. As you say the code below does not compile because Foo is not static but also because the signature does not exist:

var r2 = MyClass.Foo(a, "1"); // Compilation error, the signature Foo(MyClass, string) does not exist

If we had some syntactic sugar that allowed it, the compiler would have to add, for each instance method, a static method with such a signature:

Foo(MyClass, string) // add an object of the class as a first parameter

But note that in C# a class can't have a static and an instance method with the same signature.

So what if, as a programmer, I want to write a Foo instance method that takes another object of the same class and different than this? I could not have a class such as:

class MySpecialArray
{
    int[] myArray;

    void Copy(int number) { /* copy number everywhere in myArray */ }

    void Copy(MySpecialArray copied, int number) { /* deep copy of copied's values + number in in myArray */ }
}

Upvotes: 4

Related Questions