In C++ a functor is an object with the application operator, operator()(), defined so that it can be called like a function.
A function object is more general than a function because it can hold data and provide additional operations.
--Taken from Bjarne Stroustrup's C++ Glossary
There are various patterns where by design the implementation of some objects in the system have a single public method.
Here is an example of such implementation (given by @ymassad):
public class EmailSender
{
private readonly string server;
private readonly int port;
//... ctor here to take dependencies
public void SendEmail(string to, string subject, string body)
{
//..
}
}
// Usage:
EmailSender emailSender = new EmailSender(...);
emailSender.SendEmail(...);
Now, because the class has a single public method (SendEmail
) excluding constructors and the name of the method almost repeats the name of the class it seems redundant so in many of these cases we can just create functors.
A C# implementation for a functor might look like this:
public class SendEmail
{
private readonly string server;
private readonly int port;
//... ctor here to take dependencies
public void Invoke(string to, string subject, string body)
{
//..
}
}
When the Invoke
method is implemented on a struct/class the compiler will treat it as a functor and will allow instances to call it using the same syntax available to method calls.
So now we can just do the following:
SendEmail sendEmail = new SendEmail(...);
sendEmail(...); // syntactic sugar to sendEmail.Invoke(...)
To note the ()
operator (it is considered an operator) has explicitly been on the list of operators that cannot be overloaded since C# 1.0. I don't know specifically why but likely to avoid being able to write objects that can behave like delegates/functions that aren't delegates/functions.
I don't understand how does this improve the language.
A delegate is always feasible, even if the object has mutable state: when you call a delegate that points to an instance method of an object, it can modify that object.
In C++ (especially pre-C++11), functors made a lot of sense because of templates: you can make a template function that takes any functor with the right signature and invokes it at the right time with the right parameters.
But C# does not have templates and it does have delegates, so this doesn't seem to be worthwhile to me.
@HaloFour Yeah, I guess so, a lot has changed since C# 1.0 and in today's world it's pretty common to express classes as operations and maybe just maybe it makes more sense to add it.
@svick
A delegate is always feasible, even if the object has mutable state: when you call a delegate that points to an instance method of an object, it can modify that object.
Sometimes I don't want to mutate any state, I want the operation that responsible for creating the state to store it, sometimes it might be for caching purposes.
The class that represents the operation may or may not have methods, you can't have methods inside a delegate, you may even have hierarchies of classes or implement interfaces and such and you can't do that with delegates.
Lastly, A delegate cannot really replace classes because in the type system they represent two different things, a delegate represents a pointer to a function and a class represents an object and in this case I want the class itself to represent the operation, hence, a delegate is not always feasible.
In C++ (especially pre-C++11), functors made a lot of sense because of templates: you can make a template function that takes any functor with the right signature and invokes it at the right time with the right parameters.
Functors are used for many other things in C++ one of the scenarios are describes above!
But C# does not have templates and it does have delegates, so this doesn't seem to be worthwhile to me.
Functors and Templates are completely unrelated features and they didn't introduce one of them to support the other, you probably speak about them in a context that is completely unrelated to this proposal!
Generally the pattern I like to see is using the word Invoke
for the method name in this sort of pattern to try and get as close to the delegate type as you can. I suppose you can do some trickery like this:
public delegate int Operation(int l, int r);
class Add
{
int Invoke(int a, int b) => a + b;
public static implicit operator Operation(Add t) { return t.Invoke; }
}
public class Program
{
static void Main(string[] args)
{
Operation add = new Add();
add(1, 2);
}
}
but I am not convinced of the usefulness.
@bbarry
Why do I have to declare a delegate, then define an Invoke
method and finally have to also define the operator for the object, all this for the same goal? why? what's the purpose of your example?
I don't know but if it's that complex I can't see the usefulness of this either.
@eyalsk
C# also wanted to avoid the forms of abuse that C++ permitted via arbitrary operator overloading. I imagine that the decision to exclude ()
as an overloadable operator was a conscious one, although I don't know if we'll ever get a good summary of the argument at the time.
I agree with @svick, there are numerous ways to accomplish this same task with C#. Wanting it to smell like C++ doesn't seem like reason enough to adopt a C++ idiom. You can just as easily use closures or delegates to instance methods and accomplish the same thing, or you can skip delegates altogether and program to an interface.
I am not sure I get the point.
Do you want to call
var t = add (1, 2)
var x = add (3, 4)
.......
If so that's confusing at best. And if you want that make it a static class with a static using. If u want state in the class that changes u can do.
new add (2,3) and Ave an implicit cast to an int. Either way it all seems very allocaty
It already exist:
Public Class Add
Default Public ReadOnly Property XX(x As Integer, y As Integer) As Integer
Get
Return x + y
End Get
End Property
End Class
CS
public class Add
{
public int this[int x, int y]
{
get
{
return x + y;
}
}
}
The difference is that VB is more civilized and use curly braces, while CS need square braces.
Switch to VB :)
@HaloFour
C# also wanted to avoid the forms of abuse that C++ permitted via arbitrary operator overloading. I imagine that the decision to exclude () as an overloadable operator was a conscious one, although I don't know if we'll ever get a good summary of the argument at the time.
I don't know what was in the past but I don't care about it so much, I don't care what was their reason to exclude it but maybe there's a future to it now.
I agree with @svick, there are numerous ways to accomplish this same task with C#. Wanting it to smell like C++ doesn't seem like reason enough to adopt a C++ idiom.
I don't want it to smell like C++ and if it does I don't think it's a bad thing, I simply want to be able to model some objects as operations and thus be able to execute them as functions.
I'm completely aware that it's possible to accomplish this today in C# in various ways and I even gave an example, whether you can accomplish it today wasn't the point of the proposal but _expressiveness_.
We can accomplish many things without many features but it wouldn't be as expressive as if the feature is part of the language, e.g., we could have used methods to replace indexers and properties and whatnot.
You can just as easily use closures or delegates to instance methods and accomplish the same thing, or you can skip delegates altogether and program to an interface.
I know that but it's not the same.
@Drawaes
I am not sure I get the point.
Do you want to call
I want to express something the way I intended it to be, if I made an object and the object is an operation then I want to be able to express it in the language and allow it to be used in the most natural way, it's exactly the same argument for indexers, we can use GetItem
and/or SetItem
instead of an indexer but it will never feel natural if you compare that to how you access an array.
@vbcodec
It already exist:
Using brackets instead of parenthesis would feel odd to many people. :)
We can accomplish many things without many features but it wouldn't be as expressive as if the feature is part of the language, e.g., we could have used methods to replace indexers and properties and whatnot.
Properties and indexers are also very common, so it makes a lot of sense that they have special syntax.
I think that that situations where you want to invoke an object and a delegate isn't sufficient would be fairly rare, so I think that special syntax isn't warranted.
@svick
Properties and indexers are also very common, so it makes a lot of sense that they have special syntax.
I think that that situations where you want to invoke an object and a delegate isn't sufficient would be fairly rare, so I think that special syntax isn't warranted.
I respect your opinion but I disagree, I guess that in your world it isn't common to treat objects like operations but in mine it's fairly common.
Properties and indexers are common because they exist and this isn't really a special syntax, we're using it all the time in a different context.
C++ doesn't have delegates in exactly the same way we have them in C# but I can write this Func<int, int, int>
in C++ like this int (*func)(int,int)
or in C++11 like this std::function<int(int,int)>
so delegates have absolutely nothing with this proposal.
I know I can also do the following in C#:
interface BinaryOperation
{
int Execute(int a, int b);
}
class Add : BinaryOperation
{
public int Execute(int a, int b)
{
return a + b;
}
}
Both this and delegates can accomplish _almost_ exactly what I want but it's far from how I want it to be expressed, I don't defend this point at all, I'm just saying that it's a lot less expressive and we can have it better.
I don't know what makes you think it's a special syntax but it really isn't.
My world is CQRS and I've found no issues with implementing the command pattern on top of delegates. The complaint you make about tracking state is moot, you can take a delegate to an instance method of some service class.
Both this and delegates can accomplish exactly what I want but it's far from how I want it to be expressed.
That's a very non-compelling argument. I don't see why effort should be expended just to allow the source to look slightly different without actual improvements regarding verbosity or composition.
@HaloFour
My world is CQRS and I've found no issues with implementing the command pattern on top of delegates.
I never said that there's an issue with the current approach or that it's bad or not applicable without my proposal.
The complaint you make about tracking state is moot, you can take a delegate to an instance method of some service class.
I didn't really make a _complaint_, I wrote a proposal and if you dislike it or any of the arguments I made then all I can say is I respect your opinion but it isn't mine.
That's a very non-compelling argument. I don't see why effort should be expended just to allow the source to look slightly different without actual improvements regarding verbosity or composition.
In your opinion it's very non-compelling but luckily I have my own opinion and I see things through a different kind of lens.
I'm not trying to convince you here but making the source look slightly different and a lot closer to the programmer intents is in my opinion worth it.
I'm not part of the design team, you aren't one of them either, we're here to write/express our own ideas/thoughts/opinions and whether efforts should be put into this feature it's for the design team to decide.
@eyalsk
Indeed, you don't have to convince me. I just try to look at these things from the perspective of cost/reward. That every proposal must achieve 100 points to be worthwhile, but starts out at -100 points simply because any change costs time, effort and the liability of permanent support. I'm certainly not the scorekeeper. Heck, half the time the scoring rules baffle me. :)
@HaloFour I know what you mean and I know the chances are low but I do think it has value whether it's worth it? we'll see but if it's going to be declined, now, we will at least know the reason for it! š
Note that for delegates ()
operator is just a synonym for the Invoke
method. When you want to use null-conditional operator you will need to explicitly call it e.g. d?.Invoke()
, so with defining it as an operator you simply make it impossible to invoke it conditionally. Having said that, I think #4801 or #11929 come before this proposal, because in that case, it'd make more sense to be able to _use_ the type with the same syntax as delegates.
@alrz Sure, I agree. :)
@HaloFour
As for required points, it depends where proposal was originated. If team made proposal, then 50 points is enough, and current convoluted scoping rules and half-baked tuples are proof for that. For outsiders, bar is much higher though, and even 100 points is not enough. What features was added, on request from community ? Only probably null check operator and interpolated strings, because uservoice screamed for this.
@vbcodec They can't add all features the community wants and each of us has only its own little world to care about whereas they have the whole world to care about and they probably see more pieces of the picture than we do so I trust their judgement to do the right thing for all of us. š
I think that pattern-based approach looks attractive. Transforming identifier(...) to identifier.Invoke(...)
Smells both linq-ish and lambda-ish, which is nice.
The example of the static operator method does not use anything of your Add
class. Why would I need to new Add()
?
That is what I was saying earlier and with static using you could just have
Add () without the new in your class.
On 31 Oct 2016 10:51 a.m., "Paul Chen" [email protected] wrote:
The example of the static operator method does not use anything of your
Add class. Why would I need to new Add()?ā
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/roslyn/issues/14731#issuecomment-257264114,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APpZuY5mSqHRW5TZRJIL0zjTN-6s0WH2ks5q5cgdgaJpZM4KgYe8
.
Have you seen this?
Roslyn fork for Concept C#
See also this.
@diryboy
The example of the static operator method does not use anything of your Add class. Why would I need to new Add()?
In C# static classes cannot overload operators and the examples I provided are very simplistic, the point of the example you refer to wasn't to show a real world application but mostly the syntax for it and how you would use it.
In the real world Add
_might_ derive from BinaryOperator
or/and it might have some state depends on context and whatnot.
@Drawaes
That is what I was saying earlier and with static using you could just have Add () without the new in your class.
No you can't because in C# constructors need to be parameterless so trying to define Add(x, y)
will result an error and trying to call the constructor directly as you proposed Add()
will result yet another error not to mention that even if Add()
worked it wasn't as helpful because you couldn't pass anything to it, finally, constructors can't return a value and again operators in static classes cannot be overloaded.
@YaakovDavis
I know about this fork, I'll check the link, thanks.
I have been thinking about such a feature lately. I am glad that someone proposed it.
Here is a suggested syntax for such a feature:
public interface IAdd
{
default int Invoke(int number1, int number2);
}
public class Add : IAdd
{
public default int Invoke(int number1, int number2)
{
}
}
Of course, having the interface here is optional. But this feature should also support invoking the default method via a reference of the interface type.
It doesn't really matter what the name of the method is. The "default" keyword can be used to mark the default method. The default method would be called if the object was used as a method like this:
var add = new Add();
var result = add(1,2); //This will call add.Invoke(1,2) in this case
The class can have many other methods (public or private) if we wanted to, although I don't see a value for another public method in this case.
I think that this would be much better than using delegates because delegates are hard to compose in C#. When we want to separate data from behavior, apply the single responsibility principle, and do dependency injection, the application ends up as a set of small function-objects that we need to compose. If we want to "inject" a dependency into a delegate presented by a static method, we need to have the dependency as a parameter of the static method and then do partial invocation to "inject" it. This would at least add an additional frame to the call stack, and the syntax would be ugly.
With classes, we can inject the dependencies via the constructor.
If C# gets this feature and the Primary Constructor feature (#6997), having function-like objects that can take dependencies would become really beautiful.
Why not keep it simple and do the same like for delegate invocations, i.e. try to bind a "call syntax" to an appropriate Invoke
method?
Oh, that's basically what @ymassad said (minus the default modifier)
@ymassad, I agree, this _might_ be better than my proposed approach but this approach _might_ introduce a breaking change, the reason is Functors _may_ have slightly different rules.
However, what would the compiler do when it's marked with default
? I don't think I fully understood this part. :)
@eyalsk, how might it introduce a breaking change? Can you provide more details?
The compiler would allow only one method to be marked with default
on a class or interface. When a method is marked with default
, the compiler would make it legal to use the obj(...)
syntax at the calling site, and would emit IL that corresponds to obj.Invoke(...)
at the _calling site_ (where Invoke
is the method name). Please note that the compiler does not need to change anything in the class/interface IL code (expect for having metadata to know that the method is a default
method).
@ymassad I did not say it will introduce a breaking change but some people may have methods with Invoke
as their name in their codebases and I imagine that we would have the ability to call add(1, 2)
as well as add.Invoke(1, 2)
like delegates so _if_ we will have different rules around functors then it might introduce a breaking change, I hope it won't though.
At the moment we're just discussing about the idea and not the rules so I'm just stating it as a remark, nothing more. :)
Thanks for clarifying the default
modifier for me.
@eyalsk, please note that the developers can choose whatever name for the method that they want, not necessarily Invoke
.
I agree with you. I don't care much about the syntax as much as I care about being able to create and use a class that has a single public method as a function.
@ymassad
please note that the developers can choose whatever name for the method that they want, not necessarily Invoke.
Noted. :)
I agree with you. I don't care much about the syntax as much as I care about being able to create and use a class that has a single public method as a function.
Yup, same here.
Closing this issue for now, I think that it isn't worth discussing it given the low probability for it to actually make the cut.
This was already backlogged though.
I don't think it's a particularly big change in the compiler proper if you just want to bind to the Invoke
method group on any object that is not otherwise invokable (i.e., not a delegate). In Binder_Invocation.cs
at around line 236 you add one last attempt to bind to an Invoke
method on the receiver, which should be similar to BindDelegateInvocation
, except it's a full-fledged method group, not a single method.
The more interesting part is the IntelliSense support. I would start in InvocationExpressionSignatureHelpProvider.cs
and work my way from there. Perhaps I will, if I get off work earlier than usual today.
@alrz Well, yeah but because @CyrusNajmabadi downvote it I thought there's no hope for it, they probably discussed this in the past (as pointed by @HaloFour) and decided not to do it for some reason but anyway, I'm opening it again.
@orthoxerox Interesting, if you will ever make a prototype I'd love to check it out just for kicks! ;)
p.s. It would be nice if people would write why they downvote this proposal so we can discuss it and learn from it.
@eyalsk, in the current proposed syntax, can instance members of the object be accessed inside the operator ()
method? It would be strange if instance members can be accessed from a static method. And I think it is very important to access instance members (state) as you noted in your original post.
@ymassad You're totally right but it doesn't have to be an operator it can just be Invoke(...)
so when you define it yourself, instead of doing add.Invoke(...) you can do add(...) or we can use your idea.
The point here is really expressiveness but some people don't see the value in it, unfortunately. :)
@eyalsk I downvoted because it doesn't bring any new capabilities to the language; it only scratches a syntax itch that you have, and in my opinion that syntax is less clear than what we already have and raises the cognitive load of reading code. I don't see this as a clear win.
@jnm2 hmm well but is it always about capabilities? expressiveness doesn't count?
So you think that either one of the following:
add.Calculate(...)
add.Invoke(...)
add.Execute(...)
add[...] // Not sure who will use indexers as functors but let's say it exists in the wild.
Is more clear than the following:
add(...)
What's so special about indexers that it make sense in this case but not for functors?
When you say:
and in my opinion that syntax is less clear than what we already have
What do you mean exactly? can you elaborate? :)
If something like this were to be implemented I would prefer it to be a compiler convention that would resolve to an instance/extension Invoke
method resolved on the reference. That way we wouldn't need any new syntax or a form of instance operator. It would also bring the behavior inline with how delegates work today since func(x, y, z)
is really func.Invoke(x, y, z)
.
@eyalsk For example, it confuses verbs with nouns. That is not an add
, that is a sum
. What are you adding ...
to? You're adding ...
to a thing, a noun, an object.
It should be sum.Add
or total.Add
or calculation.Add
.
@eyalsk, I think that the syntax you proposed might have caused some people to misunderstand your proposal. I suggest that you provide an alternative syntax in the original post.
@jnm2, C# also supports the functional paradigm (to some extent). I would like to have an add
function, not an object. This function takes two parameters and returns the sum of the their values. It is nice to use classes to design functions because classes can have dependencies injected via the constructor.
@HaloFour Completely agree.
@jnm2
@eyalsk For example, it confuses verbs with nouns. That is not an add, that is an adder. What are you adding ... to? You're adding ... it to a thing, a noun, an object.
I understand, fair point and make a lot of sense.
What I was thinking is because I use functors like functions and because it make sense to have a function called Add
then it make sense to have such functor and of course, I was completely and totally wrong.
I ponder on that for few hours!
It should be sum.Add or total.Add or calculation.Add.
Exactly.
Thank you!
@ymassad
@jnm2, C# also supports the functional paradigm (to some extent). I would like to have an add function, not an object. This function takes two parameters and returns the sum of the their values. It is nice to use classes to design functions because classes can have dependencies injected via the constructor.
But you have exactly that already today without functors:
```c#
class FunctionalCalculator
{
private readonly IDependency dependency;
public FunctionalCalculator(IDependency dependency)
{
this.dependency = dependency;
}
public double Add(double x, double y)
{
return dependency.Foo(x) + dependency.Foo(y);
}
}
var add = new Func
var result = add(1, 2);
Or even:
```c#
class FunctionalCalculator
{
public FunctionalCalculator(IDependency dependency)
{
Add = (x, y) => dependency.Foo(x) + dependency.Foo(y);
}
public delegate double Adder(double x, double y);
public readonly Adder Add;
}
var add = new FunctionalCalculator(dep).Add;
var result = add(1, 2);
Or even:
```c#
static class FunctionalCalculator
{
public delegate double Adder(double x, double y);
public static Adder NewAdder(IDependency dependency) => dependency.Foo(x) + dependency.Foo(y);
}
var add = FunctionalCalculator.NewAdder(dep);
var result = add(1, 2);
```
I'm probably the only person in this thread that always writes .Invoke
so I notice the difference between method calls and delegate invocations.
@ymassad If you have some good and appealing cases for it then I'd edit my post and change it but my only point was really expressing operations as functions because it made sense to me, however, I completely agree with the points raised by @jnm2.
The only case where I think this proposal would make sense now is actually having objects that are used like functions and can contain their own state but I don't have a good example to that.
@jnm2, I have tried your last approach to create a composition of 2 functions. It is not as bad as I thought it would be, but I still see some issues with it.
class Program
{
static void Main(string[] args)
{
Calculate1 calc = Module1.NewMyCalculate1(Module1.NewMyCalculate2());
var result = calc(4, 5);
}
}
//These like interfaces will support polymorphism
public delegate double Calculate1(double x, double y);
public delegate double Calculate2(double x);
public static class Module1
{
public static Calculate1 NewMyCalculate1(Calculate2 calculate2)
{
int state = 0; //This represents state
return (x, y) =>
{
state++;
return x + calculate2(y) + state;
};
}
public static Calculate2 NewMyCalculate2()
{
return x => x + 5;
}
}
For example, the location of the x and y variables inside the NewMyCalculate1
method doesn't seem natural to me. The signature Calculate1 NewMyCalculate1(Calculate2 calculate2)
doesn't really seem natural.
Also, I have to write return (x, y) =>
.
Consider the alternative:
class Program
{
static void Main(string[] args)
{
ICalculate1 calc2 = new Calculate1(new Calculate2());
var result = calc(4, 5);
}
}
public interface ICalculate1
{
double Invoke(double x, double y);
}
public interface ICalculate2
{
double Invoke(double x);
}
public class Calculate1 : ICalculate1
{
private readonly ICalculate2 calculate2;
public Calculate1(ICalculate2 calculate2)
{
this.calculate2 = calculate2;
}
private int state = 0;
public double Invoke(double x, double y) //Signature is natural
{
state++;
return x + calculate2(y) + state;
}
}
public class Calculate2 : ICalculate2
{
public double Invoke(double x) => x + 5;
}
And with the Primary Constructors feature:
class Program
{
static void Main(string[] args)
{
ICalculate1 calc2 = new Calculate1(new Calculate2());
var result = calc(4, 5);
}
}
public interface ICalculate1
{
double Invoke(double x, double y);
}
public interface ICalculate2
{
double Invoke(double x);
}
public class Calculate1(ICalculate2 calculate2) : ICalculate1
{
private int state = 0;
public double Invoke(double x, double y)
{
state++;
return x + calculate2(y) + state;
}
}
public class Calculate2 : ICalculate2
{
public double Invoke(double x) => x + 5;
}
@eyalsk, which point do you agree with? The one about objects and nouns? If this is the case, then what I am trying to create is functions, not objects, and function names are verbs not nouns.
@ymassad Yeah exactly this point where it confuses verbs and noun and if it's a verb then it should act on an object so the examples I made don't make much sense.
I've checked the C++ STL and other places for specific examples and it seems like functors are used to _represent_ an operation (_they are nouns_) but because the syntax to execute them is the same as functions it makes you think that the name should be a verb.
The point is you don't execute functions in a vacuum you operate on something so just because you gave something the name Calculate1
and Calculate2
doesn't mean they are functions, I mean the name is indeed a verb but should the name really be CalculateX
?
@eyalsk, in functional programming, or when applying functional principles in general, it is OK and even desired to define a standalone function that is not attached to any object ("in a vacuum" if you wish). This proposed feature would make it really nice to design functions using classes. In my code bases, most my classes have a single public method and the name of the method is very similar to the name of the class itself. For example:
public class EmailSender : IEmailSender
{
private readonly string server;
private readonly int port;
//... ctor here to take dependencies
public void SendEmail(string to, string subject, string body)
{
//..
}
}
The consuming code looks like this:
emailSender.SendEmail(...)
It seems redundant to have both emailSender
and SendEmail
here. (It is different in true OOP where a single object has many operations).
It would be much better if it simply was sendEmail(...)
@ymassad
It seems redundant to have both emailSender and SendEmail here. (It is different in true OOP where a single object has many operations).
It would be much better if it simply was sendEmail(...)
Yup, this makes more sense than my example, I'll revise the post soon.
@ymassad
in functional programming, or when applying functional principles in general, it is OK and even desired to define a standalone function that is not attached to any object ("in a vacuum" if you wish). This proposed feature would make it really nice to design functions using classes.
But in functional programming your functions are not supposed to retain state or have side effects. If you take functors and remove state, you are left with regular static functions that you already have in C#.
IEmailSender.SendEmail sounds redundant but I'd argue that's trivial to fix. IEmailService.Send
or even better INotificationService.Send
in case it ends up going to SMS or a debug console. This is not the only example of good naming taking a second thought, nor are functors a unique solution to this problem.
@jnm2, I am not an FP expert, but although we should maximize the number of pure functions, some functions have to have side effects (e.g. at the end you need to add something to the database or to a file, etc.). Also, I imagine that some of the functions in application have to close over some state. For example, some state need to survive past a single web request. Such state need to be preserved somewhere.
Regarding static methods, in order to have dependencies injected you need to have these dependencies as parameters of the static methods and then do some kind of partial invocation to inject these dependencies (you don't want the consumer to provide them at call site). The syntax for this is ugly and it will add at least one frame to the call stack.
Regarding naming, please note that I am relating to single-method classes that represent functions. I don't think the name in this case should be NotificationService. Such unit has only the responsibility to send emails or notifications. Also, we should think about the consumer of the unit, why should it invoke a "NotificationService"? It only care about sending a message. So it should simply invoke another dependency function called "sendNotification".
I am not an FP expert, but although we should maximize the number of pure functions, some functions have to have side effects (e.g. at the end you need to add something to the database or to a file, etc.). Also, I imagine that some of the functions in application have to close over some state. For example, some state need to survive past a single web request. Such state need to be preserved somewhere.
Side effects are fine. State isn't. Any time you have such state, you have created a noun. It is a thing, which I would argue in C# is best represented as a thing and not an action. You don't want to adhere to functional programming and yet you don't want to make use of a perfectly idiomatic OOP concept, objects?
Regarding static methods, in order to have dependencies injected you need to have these dependencies as parameters of the static methods and then do some kind of partial invocation to inject these dependencies (you don't want the consumer to provide them at call site). The syntax for this is ugly and it will add at least one frame to the call stack.
You have options. It will not add a frame to the call stack, either. The method will be run directly from the callsite whether it's the lambda you return from the static method or an instance method or a functor.
The static method option is no worse performance-wise than the object instance method option. Functors themselves would have the exact same performance. It's just syntax.
Literally the only thing functors get you is the ability to invoke via new Verb(dependency)()
rather than new Noun(dependency).Verb()
or StaticClass.NewVerb(dependency)()
. I say having to think of nouns is not an even slightly burdensome task.
Also, we should think about the consumer of the unit, why should it invoke a "NotificationService"? It only care about sending a message. So it should simply invoke another dependency function called "sendNotification".
Perfect! So pass in an Action<string>
that does the deed directly, or is a delegate for the method on the service if you have a service. I use this pattern a lot. The dependencies exist to fulfill the client's needs and maybe the client doesn't need INotificationService
, only Action<string>
.
@jnm2, what if you want to decorate a pure function to cache the result in the first call and use it to return the cached version immediately in later calls? You need state in this case.
Regarding static methods, I want to inject dependencies at composition time in the Composition Root, and only pass the other input parameters at the call site.
Consider the following method:
public static void DoSomething(Action dependency1, Action dependency2, int param1)
{
//...
}
dependency1
and and dependency2
must not be passed at the call site, but at composition site like this:
//Composition site
Action<int> partiallyInvoked = ApplyPartial(Module1.DoSomething, dependency1, dependency2);
//Call site
partiallyInvoked(5);
Composing delegates in C# is ugly (so using delegates as dependencies is going to generate an ugly Composition Root). Also calling partiallyInvoked(5)
will get you inside the PartialInvoke
method, hence the additional frame in the stack. Consider the ApplyPartial
method in this article: https://codeblog.jonskeet.uk/2012/01/30/currying-vs-partial-function-application/
Class instances are composed in a much better way because of constructors. You pass dependencies to the constructor at composition site and then you call the method at call site.
@ymassad I've revised the proposal.
I think that in this case @jnm2 takes the _purist_ approach whereas you take the more _pragmatic_ approach to OOP and so you see things differently.
Personally, I still don't know how I feel about it.
p.s. You might want to read this Dont Create Verb Classes.
@eyalsk, thanks for the link.
It is talking about OOP. C# also supports the functional paradigm. In this paradigm, data and behavior are separated. In the link, they argue that verb classes are not really OOP. This can very much be the case (It seems to me that there are a lot of definitions of OOP which makes me not sure if this is actually the case). But it is irrelevant if your writing code in functional style.
Many features have been added to C# to make it support the functional style more. I think that this could be one of these features.
@ymassad
But it is irrelevant if your writing code in functional style.
I don't understand this, what do you mean? I mean if we take the SendEmail
functor example there's nothing _functional_ about it except it's called like a function.
I've added functor support in https://github.com/orthoxerox/roslyn/commit/66c3141f4d863f77e7d04c90a86529f03d405641. I'll see if I can add it to the IntelliSense as well.
Static functors turned out to be a very FP addition, since they basically work as standalone functions... except you cannot pass them as delegates. I think I'll see what I can do about that.
@orthoxerox Thank you! I'll try it asap. :)
@eyalsk, SendEmail
is not an object. It doesn't present a real-world entity, and it doesn't hold any data.
It is a function, it takes some input and does something with it. In this case it has some side effects which is OK in functional programming.
An example of a pure function would be a Query ParseQuery(string queryString)
function. It takes a query string, parses it into a data structure. Output in this case is totally dependent on the input. There is no real-world object here, and there is no data encapsulation.
Passing the functors as delegates is kinda problematic, since I used to rebuild the bound tree when the functors themselves were invoked, but when I pass them as arguments I have no good place to force the compiler to try that.
Microsoft.CodeAnalysis.CSharp.ConversionsBase.ClassifyImplicitConversionFromExpression
, where the conversion is selected, has no access to the current binder, so I can't do that there even when I see "Invoke" members on the source expression. Looping through every argument in the invocation binder to check if it can be used as a functor is a waste of processor cycles. I guess I'll have to sleep on that.
@orthoxerox, what do you mean by "passing functors as delegates"? can you give an example?
SendEmail is not an object. It doesn't present a real-world entity, and it doesn't hold any data.
I guess that you don't speak about objects in general or object in the .NET world here but object as in domain specific objects? correct?
You say it doesn't hold any data but it has two fields to hold the server ip and port and there's a comment about dependencies, I imagine you have fields for it to hold references to the dependencies.
It is a function, it takes some input and does something with it. In this case it has some side effects which is OK in functional programming.
It's okay but isn't really _desired_, it's just where theory faces reality.
An example of a pure function would be a Query ParseQuery(string queryString) function. It takes a query string, parses it into a data structure. Output in this case is totally dependent on the input. There is no real-world object here, and there is no data encapsulation.
Yeah I know what's a pure function but thanks. :)
what do you mean by "passing functors as delegates"? can you give an example?
I guess he means the following:
Foo(new MyFunctor()); // translates to Foo(new MyFunctor().Invoke)
@eyalsk @ymassad yes, that's exactly what I meant, being able to pass them into a higher-order function.
@eyalsk, I mean an object as in OOP. An object that that represents some entity and encapsulate data. Or an object in which runtime data and behavior are put together.
I differentiate between runtime data and other kinds of data. In the case of SendEmail
, the smtp server and port are configuration data, not runtime data. They are assigned (or injected) at composition time in the Composition Root once. On the other hand, the to
, subject
, and body
parameters are runtime data.
Which pieces of data is runtime data and which is configuration data is application-specific. For some applications, the smtp server address is runtime data and thus will be moved as a function parameter.
In OOP I guess SendEmail
would translate to this:
public class Email
{
public Email(string to, string subject, string body)
{
//..
}
public void Send()
{
// I am an object, I know how to send myself
}
}
@eyalsk, @orthoxerox, we can simply pass the functor object as is or via an interface that it implements.
For instance there would be an interface like this:
public interface ISendEmail
{
void Invoke(string to, string subject, string body);
}
And then anyone that has a reference to ISendEmail
, can invoke it like a method:
ISendEmail sendEmail = ...
sendEmail(...);
@ymassad I was thinking about functors being passed to methods expecting delegates, like LINQ methods.
@orthoxerox, I see. But that could be a different feature if it is not easy to implement.
@ymassad yes, now that we have using static
it's not a big deal to use it instead, but static functors keep tempting me.
@eyalsk, in the original post you mention that creating the object is redundant. How can this step be removed? You need to pass some configuration/dependencies when constructing the functor. I think that the only thing that is redundant is the name of the method because it is almost like the name of the "object".
@ymassad
in the original post you mention that creating the object is redundant.
Nop, I did not say that creating the object is redundant I said that calling the only function the object has is _redundant_.
Edit: Maybe you think that I refer to the constructor when I actually refer to the extra function call, anyway, I'll rephrase it because I agree it's kinda vague.
@eyalsk, my concern is that people read "but the need to create the object" and think that you want a way to call the functor without newing it up.
@ymassad Okay, I've rephrased it again, I hope it's crystal clear now.
Thanks @eyalsk. It's crystal clear now :)
@ymassad
@jnm2, what if you want to decorate a pure function to cache the result in the first call and use it to return the cached version immediately in later calls? You need state in this case.
And as such, that state is best represented as an object. A function is functionality, not data. An object is data with or without related functions.
I still don't understand what you're missing. You can do configuration and partial application and even polymorphism with existing C# language features. If you really hate admitting that function + state = object and want to go out of your way to avoid naming nouns as such, existing C# already caters to this as well. Create a delegate to a method on the thing with state and use that.
The whole send email thing is contrived in my opinion. It's bad practice and functors are making it worse. ISendEmail
is in violation of interface naming convention. IIRC, it really should be I
+ adjective if possible and if not, I
+ noun.
Better is ICanSendEmail
or IEmailSender
. And when things sound awkward like this, it raises a further question. I still say your application doesn't care if it's email or SMS or push notification, all it cares is that it has an IUserNotifier
. The rest is infrastructure configuration. Calling the interface ISendEmail
smells of forcing the client to use preconceived infrastructure interfaces rather than the interfaces being purely driven by what the client needs to know about.
@jnm2
_The whole send email thing is contrived in my opinion. It's bad practice and functors are making it worse._
I don't know what _contrived_ really means here but if you take for example the Command pattern which motivated me to write this proposal then by convention it's acceptable to name commands with VerbNoun...Command
like MoveElevatorUpCommand
, OpenElevatorDoorCommand
, they might have state, dependencies and whatnot but anyway, the following seems quite sensible:
MoveElevatorUpCommand moveElevatorUp = new MoveElevatorUpCommand(...);
moveElevatorUp.Execute(...)
So instead of doting into Execute
, Fire
or even Invoke
we would use the syntactic sugar for it, like many of us can do today with delegates' instances:
moveElevatorUp(...)
I don't think that functors are a _bad_ thing at all but I do think that when you create functors and when you name them it should be crystal clear so I completely agree with you on the naming where I think you are correct on all counts.
To me functors are normal objects that can be executed like functions the question how to name them and whether it make sense is debated but VerbNoun
makes sense to me.
I don't agree with @ymassad on some of his terminologies and how he see things specifically functions and objects but I don't want to derail the discussion on this, I don't even try to argue about the meaning of functors because their definition is crystal clear, functors are objects and the only thing they share with functions is that they can be _executed_.
p.s. Removed the IEmailSender
from the OP because I don't think that this or ISendEmail
is an appropriate interface name and it doesn't add anything useful to the example or to this proposal.
@jnm2, let's consider the client perspective. If the client only needs to send an email or a notification, then all it needs to know about is a method call, e.g. SendEmail
or SendNotification
. It doesn't need to know whether it is speaking to an object or a function or a functor whatever these really mean. So such client should have a dependency called SendNotifiation
. Currently, I model this via an interface INotificationSender
that has a method called SendNotification
. The client currently invokes notificationSender.SendNotification
. The proposed feature will make this become sendNotification
which is better.
I am more than happy to use delegates. However, composing delegates (to inject dependencies) is really ugly in C#. Did you try to partially invoke a delegate in C#? The syntax is really ugly.
Class instances in C# are composed in a very nice way. You construct the "object" and give it its dependencies.
Here is an example of an attempt for composing delegates in C#:
class Program
{
static void Main(string[] args) //Composition Root
{
InformCustomers informCustomers = customerIds =>
Module1.InformCustomers(
(to, subject, body) => Module1.SendEmail("my_smtp_server", 25, to, subject, body),
customerIds);
}
}
public static class Module1
{
public static void InformCustomers(SendEmail sendEmail, string[] customerIds)
{
foreach (var customerId in customerIds)
{
sendEmail("customer" + customerId + "@test.lab", "report", "Here is the..");
}
}
public static void SendEmail(
string smtpServer, int port, string to, string subject, string body)
{
Console.WriteLine("Sending email via " + smtpServer + ":" + port);
Console.WriteLine("To: " + to);
Console.WriteLine("Subject: " + subject);
Console.WriteLine("Body: " + body);
}
}
public delegate void InformCustomers(string[] customerIds);
public delegate void SendEmail(string to, string subject, string body);
Here is another attempt that is more ugly:
class Program
{
static void Main(string[] args) //Composition Root
{
Action<string[]> informCustomers =
ApplyPartial<Action<string, string, string>, string[]>(
Module1.InformCustomers,
ApplyPartial<string, int, string, string, string>(
Module1.SendEmail, "my_smtp_server", 25));
}
}
public static class Module1
{
public static void InformCustomers(Action<string, string, string> sendEmail, string[] customerIds)
{
foreach (var customerId in customerIds)
{
sendEmail("customer" + customerId + "@test.lab", "report", "Here is the..");
}
}
public static void SendEmail(
string smtpServer, int port, string to, string subject, string body)
{
Console.WriteLine("Sending email via " + smtpServer + ":" + port);
Console.WriteLine("To: " + to);
Console.WriteLine("Subject: " + subject);
Console.WriteLine("Body: " + body);
}
}
public static class HelperMethods
{
public static Action<T2> ApplyPartial<T1, T2>
(Action<T1, T2> function, T1 arg1)
{
return (b) => function(arg1, b);
}
public static Action<T3, T4, T5> ApplyPartial<T1, T2, T3, T4, T5>
(Action<T1, T2, T3, T4, T5> function, T1 arg1, T2 arg2)
{
return (c, d, e) => function(arg1, arg2, c, d, e);
}
}
With classes here is how it looks like:
class Program
{
static void Main(string[] args) //Composition Root
{
ICustomerInformer customerInformer =
new CustomerInformer(
new EmailSender("my_smtp_server", 25));
}
}
public interface ICustomerInformer
{
void InformCustomers(string[] customerIds);
}
public class CustomerInformer : ICustomerInformer
{
private readonly IEmailSender emailSender;
public CustomerInformer(IEmailSender emailSender)
{
this.emailSender = emailSender;
}
public void InformCustomers(string[] customerIds)
{
foreach (var customerId in customerIds)
{
emailSender.SendEmail("customer" + customerId + "@test.lab", "report", "Here is the..");
}
}
}
public interface IEmailSender
{
void SendEmail(string to, string subject, string body);
}
public class EmailSender : IEmailSender
{
private readonly string smtpServer;
private readonly int port;
public EmailSender(string smtpServer, int port)
{
this.smtpServer = smtpServer;
this.port = port;
}
public void SendEmail(string to, string subject, string body)
{
Console.WriteLine("Sending email via " + smtpServer + ":" + port);
Console.WriteLine("To: " + to);
Console.WriteLine("Subject: " + subject);
Console.WriteLine("Body: " + body);
}
}
With the proposed feature and the Primary Constructors feature, here is how it would look like:
class Program
{
static void Main(string[] args) //Composition Root
{
IInformCustomers informCustomers=
new InformCustomers(
new SendEmail("my_smtp_server", 25));
}
}
public interface IInformCustomers
{
void Invoke(string[] customerIds);
}
public class InformCustomers(ISendEmail sendEmail) : IInformCustomers
{
public void Invoke(string[] customerIds)
{
foreach (var customerId in customerIds)
{
sendEmail("customer" + customerId + "@test.lab", "report", "Here is the..");
}
}
}
public interface ISendEmail
{
void Invoke(string to, string subject, string body);
}
public class SendEmail(string smtpServer, int port) : ISendEmail
{
public void Invoke(string to, string subject, string body)
{
Console.WriteLine("Sending email via " + smtpServer + ":" + port);
Console.WriteLine("To: " + to);
Console.WriteLine("Subject: " + subject);
Console.WriteLine("Body: " + body);
}
}
This is much much better.
Regarding interface names, I see no problem of using a C# feature that was designed with OOP in mind (C# interfaces) to create polymorphic "functions" or "function-objects" even if I used names like "ISendEmail". I know that delegates are the more "functional" way in C# to do polymorphism, but as I showed in this post, the syntax for composing delegates is really ugly. At the end of the day, C# is just a language with a set of features, I can use such features the way I like as long as the code is readable and maintainable.
Two points:
Something similar is supported by several languages. E.g. in Scala, foo(bar)
is simply syntactic sugar for foo.apply(bar)
(unless foo
is a method callable on this
). In Python, foo(bar)
is syntactic sugar for foo.__call__(bar)
. In Ruby, foo.(bar)
is syntactic sugar for foo.call(bar)
.
Please, don't call them Functors. Functors have a very specific meaning in category theory, and since category theory is slowly entering the mainstream (almost every programmer nowadays has at least heard of monads), this will only lead to confusion.
@JoergWMittag
Objects like these have been called functors for a long time in OOP, see http://wiki.c2.com/?FunctorObject
@orthoxerox
You do realize the irony of linking to a page which says this:
The name, FunctorObject, is a very poor one.
Right?
I have never heard that name used outside of C++, where it also creates confusion with the identically named concept from category theory. In Scala, "Functor" only refers to the concept from category theory; objects that implement apply
are simply called "functions". In CāÆ, the name "Functor" is currently only used to refer to the concept from category theory, adding a second meaning to it seems like a bad idea.
There is a third use of the term in ML, where "Functor" is used for modules in the module system. Those Functors are actually related to the concept from category theory.
ell, yeah but because @CyrusNajmabadi downvote it I thought there's no hope for it
Just because i didn't like the original proposal doesn't mean there's no hope for it.
Also, i'm warming to the idea that invocation syntax on a non-method is just shorthand for looking up a .Invoke method.
In that way, we would be removing the specific concept of delegate invocations and generalizing it out to a pattern that can be satisfied by instance/extension methods.
@JoergWMittag
You do realize the irony of linking to a page which says this:
This is really meaningless, just because the name is _poor_ doesn't mean people don't refer to it as such.
I have never heard that name used outside of C++.
_In a more theoretical context a function object may be considered to be any instance of the class of functions, especially in languages such as Common Lisp in which functions are first-class objects._
_The functional programming languages ML and Haskell use the term functor to represent a mapping from modules to modules, or from types to types and is a technique for reusing code. Functors used in this manner are analogous to the original mathematical meaning of functor in category theory, or to the use of generic programming in C++, Java or Ada._
_In Prolog and related languages, functor is a synonym for function symbol._
Source: Function Object
As @orthoxerox pointed out it's been called functor for a very long time, I'm reading a book called Programming Language Pragmatics where the author speaks about it in the context of closures objects and points out that sometimes it's called functors.
where it also creates confusion with the identically named concept from category theory
In math, specifically in Category Theory it means one thing.
In functional languages it may relate to the mathematical term.
In object-oriented languages it may mean something completely different and it does.
I don't understand why would anyone get confused, do people speak about things without any context?
Anyway, what would you call it? š
It's reminiscent of the this[]
indexer. In VB you have named properties with indexers, in C# you only have implicit indexed properties. Today we have named methods, with this proposal we would have implicit methods.
Okay, I added implicit functor conversions in https://github.com/orthoxerox/roslyn/commit/cc4785efd7a379c44fad2123a55a29e216b43ca2. I am not sure how many existing tests I've broken yet, since I am doing this in bed on my SP3 that gets too hot when I run all of them, but you can use both instance and static functors anywhere you can use a delegate:
```c#
using System;
class C
{
static string Invoke() => "static";
string Invoke() => "instance";
static void Foo(Func<string> func) => Console.WriteLine(func());
static void Main()
{
var c = new C();
Foo(C);
Console.WriteLine(C());
Foo(c);
Console.WriteLine(c());
}
}
```
I need to refactor a few bits and pieces (e.g., I need to extract checking for the Invoke
method and inserting it into the syntax tree) before I move on to the IntelliSense.
@orthoxerox Impressive! good job man!
I like this feature. Some objects _are_ functions and, like delegates, invoking them on a set of arguments is their primary use.
Now, consider the following:
namespace N1
{
public static class X
{
public static void F(int x) { }
}
}
namespace N2
{
using static N1.X;
class A
{
public void Invoke(int x) { }
}
class B
{
public A F { get; }
void M()
{
F(1);
}
}
}
Right now, the call to F
in B.M
is resolved to N1.X.F
. If A
becomes callable because it happens to have an Invoke
method, then the same call can also be resolved to A.Invoke
on the F
property. There are a few options:
Leaving this ambiguity as an error breaks existing code.
Resolving it in favor of the imported method does not break code, but seems illogical to me. I would assume the type member would take precedence over an external member or extension method.
Resolving it in favor of the property invocation also breaks existing code because it leads to different behavior.
Resolving it in favor of the property invocation, but making it explicitly an opt-in feature will not break existing code, and allows new code with the more logical method resolution.
The opt-in can be done using (for example) a CallableAttribute
on the type:
[Callable(methodName="Invoke")]
class A
{
public void Invoke(int x) { }
}
The methodName
argument could default to `"Invoke"'. If you want to allow extension methods to define invocations, you can apply the attribute without arguments to the extension method.
Out of the two viable options (2 & 4), I'm not sure which I would prefer. The explicit opt-in seems the more logical and consistent option to me. However, if you choose to opt in, then you may break your consumers' code, so it's not ideal.
@JeffreySax
Resolving it in favor of the imported method does not break code, but seems illogical to me. I would assume the type member would take precedence over an external member or extension method.
Seems reasonable to me simply because I don't think that option 3 is even an option and I don't find option 4 _attractive_ that is I wouldn't want to have an attribute or anything like that. :)
However, it really depends how common this case is if it isn't common at all then it _might_ be okay to introduce a breaking change and issue an error.
Most helpful comment
Just because i didn't like the original proposal doesn't mean there's no hope for it.
Also, i'm warming to the idea that invocation syntax on a non-method is just shorthand for looking up a .Invoke method.
In that way, we would be removing the specific concept of delegate invocations and generalizing it out to a pattern that can be satisfied by instance/extension methods.