Virtual methods as a way of eliminating the need for interfaces?
aradalvand opened this issue · 1 comments
Hey there, thanks for creating Orleans; it's very much an underrated framework and deserves way more traction. Keep up the great work.
Most often, there is a one-to-one correspondence between grain interfaces and classes that implement them; the reason for the interface declaration requirement is, of course, the fact that Orleans creates proxy objects for the grains. But this results in suboptimal DX, as it effectively means that you have to declare a separate interface every time:
public interface IHelloGrain : IGrainWithStringKey
{
Task SayHello(string value);
}
public class HelloGrain : Grain, IClientAuthenticationContextGrain
{
public async Task SayHello(string value)
{
await Task.Delay(1000);
Console.WriteLine($"SayHello called: '{value}'");
}
}
But the proxy thing could also work with virtual methods; so why not introduce a more concise way of writing grains without the need for interfaces, and by enforcing virtual methods on grain classes? Example:
public class HelloGrain : GrainWithStringKey
{
// the Orleans analyzer could also err when the methods in class-based grains aren't marked as `virtual`, for example, and enforce the requirement that way.
public virtual async Task SayHello(string value)
{
await Task.Delay(1000);
Console.WriteLine($"SayHello called: '{value}'");
}
}
This significantly reduces the amount of boilerplate that may be slightly off-putting at first sight in Orleans compared to other frameworks like Akka.NET.
I think this idea must have come up at some point in the past, surely? Though I couldn't find any related issues in the repo. So, let me if there is one. I'd like to know if I'm missing and if this is somehow unfeasible. Thanks.
Update: Thinking about this more, I don't think it's feasible. What if the class requires constructor parameters? The derived proxy wouldn't be able to supply those, and we don't necessarily want to create an in-memory instance of the underlying grain at all, which in this proposal would entail.
So, instead, what about having Orleans source-generate the interface for the developer, so as to still achieve the same reduction in boilerplate?
Thinking about this more, I don't think it's feasible. What if the class requires constructor parameters? The derived proxy wouldn't be able to supply those, and we don't necessarily want to create an in-memory instance of the underlying grain at all, which in this proposal would entail.
I think you hit the nail on the head. The cost of creating a new interface definition is greatly outweighed by the benefits, this is true for Orleans and often true in general.