Reputation: 4488
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)
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
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