-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Functors #14731
Comments
To note the |
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.
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.
Functors are used for many other things in C++ one of the scenarios are describes above!
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 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. |
Why do I have to declare a delegate, then define an I don't know but if it's that complex I can't see the usefulness of this either. |
C# also wanted to avoid the forms of abuse that C++ permitted via arbitrary operator overloading. I imagine that the decision to exclude 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. var t = add (1, 2) 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:
The difference is that VB is more civilized and use curly braces, while CS need square braces. Switch to VB :) |
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 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.
I know that but it's not the same.
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
Using brackets instead of parenthesis would feel odd to many people. :) |
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 I know I can also do the following in C#:
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.
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. |
I never said that there's an issue with the current approach or that it's bad or not applicable without my proposal.
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.
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. |
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 |
@alrz Sure, I agree. :) |
@HaloFour |
@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(...) |
The example of the static operator method does not use anything of your |
That is what I was saying earlier and with static using you could just have On 31 Oct 2016 10:51 a.m., "Paul Chen" notifications@github.com wrote:
|
Have you seen this? See also this. |
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
No you can't because in C# constructors need to be parameterless so trying to define 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:
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 |
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 |
@orthoxerox, what do you mean by "passing functors as delegates"? can you give an example? |
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's okay but isn't really desired, it's just where theory faces reality.
Yeah I know what's a pure function but thanks. :)
I guess he means the following:
|
@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 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
|
@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:
And then anyone that has a reference to
|
@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 |
@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". |
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 :) |
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. Better is |
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
So instead of doting into
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 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 |
@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. 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#:
Here is another attempt that is more ugly:
With classes here is how it looks like:
With the proposed feature and the Primary Constructors feature, here is how it would look like:
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:
|
Objects like these have been called functors for a long time in OOP, see http://wiki.c2.com/?FunctorObject |
You do realize the irony of linking to a page which says this:
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 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. |
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. |
This is really meaningless, just because the name is poor doesn't mean people don't refer to it as such.
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.
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 |
Okay, I added implicit functor conversions in orthoxerox@cc4785e. 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: 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 |
@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
The opt-in can be done using (for example) a [Callable(methodName="Invoke")]
class A
{
public void Invoke(int x) { }
} The 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. |
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. |
All this discussion about nouns and verbs is one of the reasons I migrated to F#, I had enough with the Kingdom of Nouns.
Like, let data just be data, let behavior be behavior, and let functions do whatever with it. Data can go whatever, behavior can go whatever. We don't care about what is a verb or a noun, they all are ways of either changing the shape of the data of referring to the data by name. We couldn't care less about where do they go, they can go together if that makes sense or they can go separate in modules if that makes sense. And what happens when you take SOLID principles to the extreme logical conclusion ? you get precisely interfaces with one method that are functors ! And being able to call them with
All I wanted was a simpler way to call my F# functions from C# without syntactic juggling ... Just my two cents, I guess, or digress. (and I realized this was 3 years ago) |
What is a functor?
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
The problem
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):
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.The solution
A C# implementation for a functor might look like this:
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:
Prototype available by @orthoxerox
The text was updated successfully, but these errors were encountered: