Skip to content
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

Closed
iam3yal opened this issue Oct 25, 2016 · 92 comments
Closed

Proposal: Functors #14731

iam3yal opened this issue Oct 25, 2016 · 92 comments

Comments

@iam3yal
Copy link

iam3yal commented Oct 25, 2016

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

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.

The solution

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(...)

Prototype available by @orthoxerox

@HaloFour
Copy link

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.

@svick
Copy link
Contributor

svick commented Oct 25, 2016

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.

@iam3yal
Copy link
Author

iam3yal commented Oct 25, 2016

@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!

@bbarry
Copy link

bbarry commented Oct 25, 2016

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.

@iam3yal
Copy link
Author

iam3yal commented Oct 25, 2016

@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.

@HaloFour
Copy link

@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.

@Drawaes
Copy link

Drawaes commented Oct 25, 2016

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

@vbcodec
Copy link

vbcodec commented Oct 25, 2016

It already exist:

VB
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 :)

@iam3yal
Copy link
Author

iam3yal commented Oct 25, 2016

@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. :)

@svick
Copy link
Contributor

svick commented Oct 25, 2016

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.

@iam3yal
Copy link
Author

iam3yal commented Oct 26, 2016

@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.

@HaloFour
Copy link

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.

@iam3yal
Copy link
Author

iam3yal commented Oct 26, 2016

@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.

@HaloFour
Copy link

@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. :)

@iam3yal
Copy link
Author

iam3yal commented Oct 26, 2016

@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! 😆

@alrz
Copy link
Member

alrz commented Oct 26, 2016

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.

@iam3yal
Copy link
Author

iam3yal commented Oct 26, 2016

@alrz Sure, I agree. :)

@vbcodec
Copy link

vbcodec commented Oct 26, 2016

@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.

@iam3yal
Copy link
Author

iam3yal commented Oct 26, 2016

@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. 😄

@jveselka
Copy link

I think that pattern-based approach looks attractive. Transforming identifier(...) to identifier.Invoke(...)
Smells both linq-ish and lambda-ish, which is nice.

@pawchen
Copy link
Contributor

pawchen commented Oct 31, 2016

The example of the static operator method does not use anything of your Add class. Why would I need to new Add()?

@Drawaes
Copy link

Drawaes commented Oct 31, 2016

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" notifications@github.com 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
#14731 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/APpZuY5mSqHRW5TZRJIL0zjTN-6s0WH2ks5q5cgdgaJpZM4KgYe8
.

@YaakovDavis
Copy link

YaakovDavis commented Oct 31, 2016

Have you seen this?
Roslyn fork for Concept C#

See also this.

@iam3yal
Copy link
Author

iam3yal commented Nov 1, 2016

@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.

@ymassad
Copy link

ymassad commented Nov 18, 2016

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.

@fgreinacher
Copy link

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?

@fgreinacher
Copy link

Oh, that's basically what @ymassad said (minus the default modifier)

@iam3yal
Copy link
Author

iam3yal commented Nov 19, 2016

@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. :)

@ymassad
Copy link

ymassad commented Dec 7, 2016

@orthoxerox, what do you mean by "passing functors as delegates"? can you give an example?

@iam3yal
Copy link
Author

iam3yal commented Dec 8, 2016

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)

@orthoxerox
Copy link
Contributor

@eyalsk @ymassad yes, that's exactly what I meant, being able to pass them into a higher-order function.

@ymassad
Copy link

ymassad commented Dec 8, 2016

@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
    }
}

@ymassad
Copy link

ymassad commented Dec 8, 2016

@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(...);

@orthoxerox
Copy link
Contributor

@ymassad I was thinking about functors being passed to methods expecting delegates, like LINQ methods.

@ymassad
Copy link

ymassad commented Dec 8, 2016

@orthoxerox, I see. But that could be a different feature if it is not easy to implement.

@orthoxerox
Copy link
Contributor

@ymassad yes, now that we have using static it's not a big deal to use it instead, but static functors keep tempting me.

@ymassad
Copy link

ymassad commented Dec 8, 2016

@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".

@iam3yal
Copy link
Author

iam3yal commented Dec 8, 2016

@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.

@ymassad
Copy link

ymassad commented Dec 8, 2016

@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.

@iam3yal
Copy link
Author

iam3yal commented Dec 8, 2016

@ymassad Okay, I've rephrased it again, I hope it's crystal clear now.

@ymassad
Copy link

ymassad commented Dec 8, 2016

Thanks @eyalsk. It's crystal clear now :)

@jnm2
Copy link
Contributor

jnm2 commented Dec 9, 2016

@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.

@jnm2
Copy link
Contributor

jnm2 commented Dec 9, 2016

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.

@iam3yal
Copy link
Author

iam3yal commented Dec 10, 2016

@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.

@ymassad
Copy link

ymassad commented Dec 11, 2016

@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.

@JoergWMittag
Copy link

Two points:

  1. 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).

  2. 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.

@orthoxerox
Copy link
Contributor

@JoergWMittag

Objects like these have been called functors for a long time in OOP, see http://wiki.c2.com/?FunctorObject

@JoergWMittag
Copy link

@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.

@CyrusNajmabadi
Copy link
Member

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.

@iam3yal
Copy link
Author

iam3yal commented Dec 12, 2016

@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? 😄

@jnm2
Copy link
Contributor

jnm2 commented Dec 12, 2016

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.

@orthoxerox
Copy link
Contributor

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 Invoke method and inserting it into the syntax tree) before I move on to the IntelliSense.

@iam3yal
Copy link
Author

iam3yal commented Dec 12, 2016

@orthoxerox Impressive! good job man!

@JeffreySax
Copy link

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:

  1. Leaving this ambiguity as an error breaks existing code.

  2. 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.

  3. Resolving it in favor of the property invocation also breaks existing code because it leads to different behavior.

  4. 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.

@iam3yal
Copy link
Author

iam3yal commented Jan 23, 2017

@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.

@iam3yal iam3yal closed this as completed Feb 15, 2017
@Luiz-Monad
Copy link

Luiz-Monad commented Jan 2, 2021

All this discussion about nouns and verbs is one of the reasons I migrated to F#, I had enough with the Kingdom of Nouns.

Nouns are the most important citizens in the Kingdom Java. In Javaland, by King Java's royal decree, Verbs are owned by Nouns. They are, in effect, the kingdom's slaves, or at very least the serfs and indentured servants. Because the Verb citizens in this Kingdom have it very, very bad. The residents of Javaland are quite content with this situation, and are indeed scarcely aware that things could be any different.
No Verb is ever permitted to wander about freely. If a Verb is to be seen in public at all, it must be escorted at all times by a Noun.

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.
The whole bike-shedding on naming conventions, verb this, name this. Its ironic this coming up on C#, a pragmatic multiparadigm language, I think its too late for the OO purists, they should go back to Smalltalk, or something, go do true.if(something()).then(something()).
Ironically, most of F# developers are less pragmatic about FP purity than some C# dev are about "OOness".
Like come on, none of this really matters, what matter is the message passing between objects, messages are what matter, go read the original Alan Kay rant on all of this "object orientation" non-sense.
I am a pragmatic guy, if I see void message(this object obj, something parameter); or obj.message, I call that "OO", OO is about dispatching, not objects.

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 i() instead of i.do() would be excellent.

In pursuit of "single responsibility principle", i do break the code down to small classes, which in some cases end up having a single method/function. Here is an example:
(from dotnet/csharplang#6616)

All I wanted was a simpler way to call my F# functions from C# without syntactic juggling ...
It a shame this won't go further anymore, its the small things, look at what Kotlin does with extensions, call conventions and lambdas, it puts C# to shame, and OO purists are horrified by the extension of the extension methods (pun intended).

Just my two cents, I guess, or digress. (and I realized this was 3 years ago)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests