Fabulous Adventures In Coding

Eric Lippert's Blog

Standard Generic Delegate Types

Hey all, I'm back from my vacation. Two weeks of reading, sailing, kayaking and visiting with old friends has left me a lot more relaxed and sunburnt than when I left. I could use another week, but it's also good to be back.

We're introducing a lot of new features in C# 3.0 which, when combined to form LINQ are really interesting and powerful, but, like the component parts of Voltron, are pretty interesting and powerful just on their own. Lambda expressions, for example, are not just useful for making query comprehensions work. They make functional-style programming in C# 3.0 much more elegant than the somewhat clunky anonymous method syntax from C# 2.0.

We're also introducing a new standard generic delegate type in the LINQ libraries to make delegate declaration easier. In the old days, to create a function that takes an int and returns a function from int to int, you'd have to do something like this:

delegate int D1(int y);
delegate D1 D2(int x);
//...
D2 makeAdder = delegate(int x){
  return delegate(int y){
    return x + y;
  };
};
D1 addTen = makeAdder(10);
Console.WriteLine(addTen(20));

In the new world we'll have these definitions in a standard library:

delegate R Func<R>();
delegate R Func<A1, R>(A1 a1);
delegate R Func<A1, A2, R>(A1 a1, A2 a2);
delegate R Func<A1, A2, A3, R>(A1 a1, A2 a2, A3 a3);
//...etc, up to some reasonable number of arguments

so that you can use them plus lambdas to make the code above far more concise:

Func<int, Func<int, int>> makeAdder = x=>y=>x+y;
Func<int, int> addTen = makeAdder(10);
Console.WriteLine(addTen(20));

Here's an interesting fact: there are delegate types which can be defined using the old-fashioned syntax but cannot be defined using the newfangled generic syntax. Void delegates, generic delegates and delegates with out or ref parameters are obvious examples. Can you think of any other examples? Next time on FAIC, I'll post an interesting one. 

Published Wednesday, June 21, 2006 11:29 AM by Eric Lippert
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

kfarmer said:

.. and somehow VoidFunc<> just sounds like some trendy techno band ;)
June 21, 2006 4:02 PM
 

Serras said:

I think this Func<> delegates should be somheow extended to allow currying or some other functional features. F#, for example, has a powerful set of delegates allowing these operations (I think they are called FastFunc<>). So, why not enlarge the Base Class Library to allow real interoperation for any language.
In my point of view, the .NET Framework is now getting more tied to C# and VB advancements, whereas some other extensions could behave well on other languages. C#, on the other hand, is becoming a starnge mix of functional and OOP features (why support lambda expressions and now allow an easy way to take functions as parameters). Some other languages that target .NET, such as Boo and Nemerle, are making it easier.
June 21, 2006 4:29 PM
 

Alois Kraus said:

I think you cannot declare the "I don't care for my parameters" delegates with the new mechanism. Example:

string [] arr = ...;
int Count = 0;
Array.ForEach<string>(arr,delegate
{
  Count++;
});

Normally you would declate it this way:
Array.ForEach<string>(arr,delegate(string str)
{
  Count++;
});


By the way I did make some comparison how C# and F# do perform in functional programming. F# does win of course but you can get already quite functional with generators and currying in .NET 2.0

http://geekswithblogs.net/akraus1/articles/79880.aspx

Yours,
 Alois Kraus
June 21, 2006 6:21 PM
 

Eric Lippert said:

Delegates do allow a form of currying in .NET 2.0, but it is pretty gross.  Basically it is a mechanism introduced for the convenience of compiler writers writing compilers for functional languages, not for C# programmers.

For example, suppose you've got public class C { public static int M(T t, int x) { whatever } } and you want to curry M with a T.  You can say:

   Func<int, int> d = Delegate.CreateDelegate( typeof(Func<int, int>), t,
     typeof(C).GetMethod("M", BindingFlags.Static | BindingFlags.Public));

and you'll get a delegate that takes an int and calls M(t, x).

There's no mechanism that I know of for taking an existing delegate object and currying that.  I agree that this would be pretty cool.

However, we don't add features because they're cool, or because they enable functional programming, or any other such highfalutin concerns.  We add features because they enable our customers to get work done.  We're not adding lambdas because we're a bunch of functional language wonks who love lambdas -- I mean, we are that, but that's not why we're adding lambdas.  We're adding lambdas because lambdas will help massively with query comprehensions, and we're adding query comprehensions because our research shows that pro devs could really use a language-integrated approach to querying disparate datasets without losing the benefits of static typing.  If some day there's a highly pragmantic language feature that needs currying, that's the day we'll figure out how to do elegant currying in C#.

I'm not sure I quite undersand your point about the framework being tied to language advancements.  By the "framework" do you mean the common language runtime, or the base class library?  I agree that we are making significant additions to the base class library in order to make LINQ practical for real-world query problems.  But C# 3.0 features are driving exactly zero enhancements into the CLR itself.  If somehow you could avoid using any of the LINQ libraries that will ship with the next version of the BCL, then there's no reason I know of why you couldn't run the output of the C# 3.0 compiler in the 2.0 .NET CLR.
June 21, 2006 6:41 PM
 

Eric Lippert said:

Alois is correct; lambdas do not support the "I am assignable to any delegate type which has no out parameters" form of anonymous methods.

However, that's not what I'm getting at.  I'm asking whether there is a delegate _type declaration_ which can be written in the classic form:

delegate R D(A a);
...
D x = something;

which _cannot_ be written using the generic form

delegate R Func<A, R>(A a);
...
Func<A, R> x = something;

What can you choose for A or R that makes this possible in the first form but not in the second?
June 21, 2006 6:45 PM
 

Geert Baeyaert said:

A couple of questions/remarks

1) Any chance you'll also define standard Method delegates for void functions:

delegate void Method();
delegate void Method<TArg1>(TArg1 arg1);
...

2) As for currying, if there's nothing builtin in the framework, it's dead easy to write the following class yourself (although a bit tedious, because of all the overloads...)

public static class Curry
{
   public static Function<TReturn> Function<TReturn, TArg1> (Function<TReturn, TArg1> f, TArg1 arg1)
   {
       return delegate { f(arg1); };
   }

   ...
}

3) Finally, is there any chance there could be structural equivalence for delegates.  It would allow us to use the new generic standard delegates in old api's as well.  What I mean is, could the following ever be legal:

delegate void MyMethod(int i);

private void DoSomething(MyMethod m)
{
   m(10);
}

private void PrintInt(int n);
{
   Console.Writeline(n.ToString());
}

public void Example()
{
   Method<int> m = new Method<int>(PrintInt);
   DoSomething(m);  // Although DoSomething expects a MyMethod, we can give it a Method<int>
}
June 22, 2006 5:07 AM
 

Eric Lippert said:

Excellent questions Geert.  

Before I answer them, let me give the caveat that none of this has been decided and written in stone yet.  Things are still in a state of flux here.

1) Probably, yes.  And they'll probably be called "Action", which I'm not thrilled with, but it does emphasize that we've got a side effect going on here.

2) As a functional-style-loving wonk, I'd love to see currying operators built into the framework like this.  But as you note, they're pretty easy to roll yourself, so odds are good we probably won't be adding them.

3) We have no plans to have structural equivalence for delegates.  One of the points of standardizing on Func<A, R> is so that there is one clear choice for interoperable delegate typing.  If everyone uses Func<A,R> as their type then you don't need structural equivalence because you already have type equivalence -- that's how we want to solve this issue.

Thanks for your feedback!
June 22, 2006 2:20 PM
 

Ayende Rahien said:

Eric,
This is very important, yes.
I often want to use those in cases where it is just a chore to create my own delegate.
You can see how I solved it at the moment here:

http://www.ayende.com/Blog/2005/10/29/StaticReflection.aspx

I got:

R Func<R>();
R Func<R, A1>(A1 a);

void Proc();
void Proc<A1> (A1 a);

it wroks very nicely.
June 22, 2006 3:07 PM
 

Alois Kraus said:

Hm,

I could think of
      delegate R  D(params A1 [] a);
      delegate R  Func<A1 [],R>(A1 [] a);
since you would introduce ambiguities if you let params into the game. Your questions are always quite tricky ;-)

Yours,
  Alois Kraus


June 22, 2006 6:26 PM
 

Eric Lippert said:

Good one!  

Also, delegate declarations where the formal parameter list contains a parameter with an attribute cannot be declared using the generic syntax.

Any others you can come up with?

Big hint:  for any "old fashioned" delegate declaration, there is a type symbol which is valid as an argument or return type in that declaration, but which actually has no equivalent at all in the generic form.

June 22, 2006 7:14 PM
 

Alois Kraus said:

Another thing could be unsafe code and the * symbol. I do not think that I am able to do this with generics. This is getting really dirty now but it is legal C# code.

       unsafe int func(int *a)
       {
           return (int) a;
       }

       unsafe delegate int D(int* a);
       Program()
       {
           unsafe
           {
               D x = func;
           }

Ahh the good old C++ times shine through here ;-).

Yours,

 Alois Kraus
June 22, 2006 7:51 PM
 

Eric Lippert said:

You're finding all kinds of great corner cases here.  I am definitely re-reading this post before I write the check-in tests for lambda type inference!

:-)
June 22, 2006 8:02 PM
 

Alois Kraus said:

Hi Eric,

I think I know what type symbol you had in mind:

int func(Type a) // This will become interesting with generics
{
     return 0;
}

delegate int D(Type a); // perfectly legal
delegate int Func<Type,int>(Type a); // This is definetely not possible with generics

This was too easy to think first of. ;-)

Yours,
 Alois Kraus
June 23, 2006 4:02 AM
 

Stuart Ballard said:

If you're not going to do structural equivalence for delegates, any chance of at least putting implicit conversion operators bidirectionally between Func<T, bool> and Predicate<T>?

Another interesting approach to this would be to provide a general way to "cast" or convert between two structurally equivalent delegate types. Currently the closest I can figure is:

Func<T, bool> f = whatever;
Predicate<T> p = x => f(x);

but that gets clumsy when there are a lot of parameters and it seems like it would have more overhead, creating a method to perform the identity function rather than just using the existing method which is known to fit the delegate type.

Thoughts?
Stuart.
June 23, 2006 10:10 AM
 

Fabulous Adventures In Coding said:


Last time I asked whether there were examples of delegate types which could be declared with the traditional...
June 23, 2006 1:23 PM
 

kerneltrap said:

I'd prefer to reorder type arguments like this:

delegate R Func<R, A1>(A1 a1);

delegate R Func<R, A1, A2>(A1 a1, A2 a2);

delegate R Func<R, A1, A2, A3>(A1 a1, A2 a2, A3 a3);

It seems more natural.

November 13, 2006 3:14 PM
 

kerneltrap said:

It woild be nice to have System.Action with more type arguments in BCL

delegate void Action< A1>(A1 a1);

delegate void Action< A1, A2>(A1 a1, A2 a2);

delegate void Action< A1, A2, A3>(A1 a1, A2 a2, A3 a3);

like Func family.

November 13, 2006 3:21 PM
 

Eric Lippert said:

Putting the return type first makes it harder to read.  I read "Func<int, double, string>" as "a function from int and double to string".

Also, it certainly does NOT feel more natural to VB programmers, who are used to the return type coming at the end of a function declaration.  

November 13, 2006 7:10 PM
 

adk said:

I often want a generic type where the parameter is a value or an object rather than a type: for example I want a class of integers modulo p for some prime number p. The simplest approach is to construct a class IntegerModulo with p as an instance variable along with the particular value k (so the constructor IntegerModulo(k,p) constructs an object representing k modulo p). There are two problems with this: first I may have a lot of IntegerModulo objects with the same p, which is a bit wasteful; and second and more importantly I cannot catch attempts to add two IntegerModulo objects with different values of p at compile time. What I would like is to be able to define a generic type,

IntegerModulo<7> for example, but this is not easy to do. I could construct a class to represent the value 7 using reflection.emit, but this is very cumbersome. Do you have any suggestions?

February 25, 2007 1:40 PM
 

Fabulous Adventures In Coding said:

Last time I mentioned that one of the subtleties of programming language design is weighing the benefit

September 6, 2007 11:48 AM
 

Fabulous Adventures In Coding said:

Last time I asked whether there were examples of delegate types which could be declared with the traditional

September 6, 2007 6:38 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Eric Lippert

Eric Lippert is a senior developer on the Microsoft C# compiler team. Before that he worked on the framework of Visual Studio Tools For Office. Before that, he worked on the compilers, runtimes and tools for VBScript, JScript, Windows Script Host and other Microsoft Scripting technologies. He lives in Seattle and spends his free time editing books about programming languages, playing the piano, and trying to keep his tiny sailboat upright in Puget Sound.

This Blog

Syndication


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker