Why Is The Return Type Parameter Last?

Why Is The Return Type Parameter Last?

Rate This
  • Comments 26

The generic delegate type Func<A, R> is defined as delegate R Func<A, R>(A arg). That is, the argument type is to the left of return type in the declaration of the generic type parameters, but to the right of the return type when they are used. What’s up with that? Wouldn’t it be a lot more natural to define it as delegate R Func<R, A>(A arg), so that the R’s and A’s go together?

Maybe in C# it would, but in this case, it’s C# that’s the crazy one.

When we speak it in English, the argument type comes before the return type. We say “length is function from string to int”, not “length is a function to int from string”.

When we write it in mathematical notation, we say that a function’s domain and range are defined as f:A→R – again, the “return type” comes last.

And in many languages, the return type of a function comes in the sensible position. In VB it’s Function F(arg As A) As R.  In JScript .NET it’s function F(arg : A) : R.

And finally, consider higher-order functions; say, a function from A to a function from B to C. You want to think of this as A→(B→C); do you really want to write that as Func<Func<C, B>, A> ? This is completely backwards. Surely you want A→(B→C) to be represented as Func<A, Func<B, C>>.

C# gets it wrong because C# inherits the basic pieces of its syntax from C, and C gets it wrong. Well, no, rather, it would be more fair to say that C is a non-typesafe, non-memory-managed language where it is vitally important that the code maintainer understands the lifetime and type of the data in every variable. Given that unfortunate situation, it makes sense to emphasize the storage mechanism first, and then the semantics second. Therefore in C you put the storage metadata first (static int customerCount;) rather than the semantics first (it could have been var customerCount: static int;). Once you’re in the position where the type comes first on variable declarations, it makes sense to apply the same rule to all other kinds of declarations – methods, formal parameters, and so on.

It might have been nicer back in the early days of C# to say “you know, we have a type-safe, memory-managed language, let’s do what VB does, de-emphasize the type mechanism and put the type as an annotation on the end”. We could then make that consistent throughout the language so that Func<A, R> referred to delegate Func<A, R>(arg : A ) : R. But that ship has sailed and we’re stuck with the declaration syntax we’ve got.

  • I can't say that I agree with you on what C# should have done - maybe it's just because I've used the C#/Java syntax for so long that I find it awkward to think of variables and function declarations the opposite way...

    But my comment is specifically on Func<T, TResult> part of your post.

    Since C# declares things like:

    string ConvertToString(int i) {}

    and VB.NET declares it as:

    Function ConvertToString(i as Integer) as String

    It would make sense that C# people would want it as Func<TResult, T> and VB people would want it as Func<T, TResult>.

    I can see one of two things happening:

    1) The person in charge of deciding the order of the type parameters asked the C# dev lead and the VB.NET dev lead to help him decide - they couldn't come up with a consensus, so they played rock-paper-scissors two out of three to decide which order they would be in.  VB guy won.

    I was not there for that decision, but knowing the principles who were there, this strikes me as highly unlikely. I think your premise is incorrect. I don't think anyone on the C# team actually thinks that "return type first" would be better. I suspect that consensus was reached early. -- Eric

    2) Microsoft felt sorry for the VB.NET team, since C# seems to be Microsoft's favorite language (although I'm sure they'll never admit it) - there just seems to be a little bit of favoritism whenever Microsoft is demonstrating or talking about .NET, it always seems to favor C#.  They figured they'd be nice to the VB.NET folks this one time and throw them a bone.

    I hear this supposition a lot. The idea that VB and C# are in a zero-sum game, and that any benefit to one is a loss of the other is not at all how things work around here. Nothing could be further from the truth. The VB and C# teams have of course had friendly rivalries over the years, but we are ultimately all the same big team and get a lot of support from each other. I suspect that any observed bias is either (1) actually the bias of an observer who is likely to interpret support of C# as lack of support for VB, as though it were a zero-sum game, or (2) a result of the fact that most people at Microsoft are simply more comfortable working in a C-like language than a BASIC-like language. That fact has nothing whatsoever to do with the level of investment that the corporation is making in the future of VB. -- Eric

  • Now this is a hint when I try to remember the signature of the Func delegates.

  • >>> Well, no, rather, it would be more fair to say that C is a non-typesafe, non-memory-managed language where it is vitally important that the code maintainer understands the lifetime and type of the data in every variable. Given that unfortunate situation, it makes sense to emphasize the storage mechanism first, and then the semantics second. Therefore in C you put the storage metadata first

    Or maybe Kernigan & Ritchie were just Algol-60 fans (it had type-first variable declarations)? ;)

    On a more serious note. It's interesting that many languages from the curly-braces family seem to be moving away from the traditional C-style "type first" declarations to Pascal-style "identifier first". The obvious example is those EcmaScript dialects which mimicked the stillborn ES4 - such as JScript.NET:

     function foo(x: Number) : Number { ... }

    But more recently there's also C++0x, at least for function return types:

     auto Foo(int x) -> int { ... }

     [](int x) -> int { ... }

    It seems that type-first declaration is inconvenient in many ways - not just for humans to read for the more complicated cases, but also for compilers to parse, and for cases such as generics. We still have this odd bit in C# where a generic type parameter has to be used before it is properly defined, in generic method declarations::

     T Get<T>(int i);

    I would imagine it isn't exactly friendly to the parser, but also wrecks IntelliSense when writing out that method declaration.

    It's also interesting to see how C# dodges the bullet for return type of anonymous delegates / lambdas by not giving any way to explicitly specify it at all. C++0x guys had to stick to the "(TArgs...) -> TResult" syntax there...

    Then, also, for languages with ability to declare structural types in-line, when you are defining a function that returns a list of tuples of function pointers, type-first declarations tend to be unreadable (you know, all those C/C++ interview questions such as "write the definition of a function returning an array of pointers to functions...").

    >>> But that ship has sailed and we’re stuck with the declaration syntax we’ve got.

    So, the question then is - is there any remote chance that a "new-style" declaration syntax would be introduced in some future version of C# (obviously, while keeping the old one)?

  • One of the 'good' sides is that you can scan your code faster. Often I am looking for a specific function (for example the function that returns the "type" of a derived class, returned by an overwritten virtual function of derived instances), and I can scan for the return values (String, certain enum, ...) much faster as they are all aligned at the left side.

    For "reading clarity", I prefer the "short" part (return value) to the left, so that I can align the name and arguments starting at the same row below each other.

    Maybe it's just that I'm used to it, but I came from Pascal to C (C++) and I had no problem either way. A programming language is not a 'natural' language and need not behave that way.

    Christian

  • I wonder whether borrowing syntax from a language as arcane and archaic as C for any new language, possibly with vastly differing concepts and paradigms is ever a good idea. The only benefit you have is that programmers familiar with C-like languages may have less trouble understanding or writing simple code, but as soon as you're getting to more advanced things you're lost in any case. I certainly didn't know how to read and understand lambdas before I read about how they work and are defined, for example.

    Having some known syntax might be good but ultimately it probably restricts what you can do with the language.

  • I personally prefer the Pascal style, but I would point out that I find C# declarations a significant incremental improvement on C++. I really hate the way type information is arranged in C++:

    template <class T> ReturnType ClassName<T>::methodName(int *pi, T other) const

    We've got type data scattered all over the place - the template parameters, return type and class on the left, the argument list and const attribute on the right. The method name is utterly buried in amongst all that other stuff. I've not even gotten into the way "*" and "[]" work in variable or function declarations. I am very glad that C# puts all the type information for a variable in one contiguous string of text. I would have preferred return types to go on the right in method declarations, but I'm already pretty glad for what we've got.

    Also, I don't think it even *occurred* to me that it should be Func<R,A>.

  • Eric Lipert has another very interesting entry which explains why the team decided to use the last type

  • Eric Lipert has another very interesting entry which explains why the team decided to use the last type

  • There's another reason.  C++ was already using this convention, e.g:

    std::binary_function<typename Arg1, typename Arg2, typename Result>

    Programming .Net generics in C++ using a different convention would have been super-confusing.

    Though this doesn't explain why C++ did this.  For the answer, see above.

  • Eric,

    since you just ruled out the free infoof pony, is this now the time to request a free unicorn? Yes? Well, then, I'd like to ask for

    1) A -> B -> C syntax in addition to Func<A, B, C>. e.g. Func<IEnumerable<TSource>, Func<TSource,TResult>, IEnumerable<TResult> just makes my brain try to escape through my ears. how beautiful would that be: IEnumerable<TSource> -> (TSource -> TResult) -> IEnumerable<TResult> *)

    2) automatic currying: (A -> B) -> C should be equivalent to A -> (B -> C)

    3) some kind of variadic type argument capability (maybe building on currying? not thought through though)

    4) equivalence of A -> B -> C to any delegate type, not just Func<A,B,C>

    5) type inference: (int x) -> x.ToString() should be of type int -> string automatically, where we have to provide an explicit type today or resort to tricks like Func<...> MakeFunc (Func<....> f) { return f; }

    Right now, C# is in an uncomfortable spot. Very complicated for beginners and people who don't get FP, but too limiting for people who really want to embrace FP. In a way, C#3 just made us horny, but it often leaves us unsatisfied. A fine currying-like syntax and variadic type args would bring us a lot closer to FP heaven, don't you think?

    I agree, those are all awesome features. I'll see what I can do, but no promises; we have a LOT of good suggestions for future features and very constrained budgets these days. -- Eric

    *) I remember trying to grok monads in C#: http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx (and I failed: I was able to implement the IO monad in C#, but I did not understand the code I created on a global level, only line by line, but I digress).

    the only way for me to follow the explanations was to translate the Func<A,B> types to A -> B using a pencil on the edge of the paper. Really, use any generic type in a Func<...> type and you're lost in angle brackets. I might as well code in XSLT. :-(

  • My personaly bias, but I think C#'s strong typing is its one of its most important attributes. Under those circumstances, it seems right to emphasize type in the declaration syntax. Storage mechanism attaches fluently to type, so it makes sense to put that first, too. I think C# is just fine the way it is. If we make C# too much like VB, then what point is there in having two different languages? Vive la différence!

    I agree with you that static typing is important. I note though that "strong typing" basically is meaningless -- it pretty much means "a type system I like". Remember, there's a difference between "static types" -- that is, every expression and variable's type is known by the compiler, and compile-time checks are made on the basis of that knowledge -- and "explicit types" -- that is, the text of the program explicitly contains the type information for each variable. C# is statically typed but is no longer explicitly typed, thanks to "var". F# is statically typed but almost never has explicit types. -- Eric

     

  • One nitpick with moving the type to the end is the amount of needless typing / extra characters:

    static int customerCount;

    var customerCount: static int;

    There's a "var " and ": " in there that I don't really feel a really strong urge to type in every time I declare a variable.  Of course, it's not like a little typing killed anyone, but I don't know that this semantic difference is worth it.

  • But, your argument about right & wrong syntax is merely looking at function *declarations*.  Why don't we look at function *use*?

    int returnVal = Func(parm1, parm2);

    And that syntax is essentially the same in C# and VB.Net and pretty much every other major language, and go back to high school math (i.e, we say "Let B equal the number of berries picked" rather than "let the number of berries picked be B").

    Then, we can say that VB.NET does it "wrong" because it's declaration does not match it's use. (which can be explained as strict typing in VB is a hack added in well after the original syntax was set)

    I see your point but I do not believe that it generalizes to anything other than assignment. What about method calls? If you believe that the return value logically "goes to the left" then shouldn't x = Foo().Bar() be written as x = Bar().Foo() ? Because the returned value of Foo() gets "pumped to the left" into Bar, and then the return value of Bar gets pumped to the left into x. You like this? With chained method calls, the result "goes to the right". When you have a method Foo:A --> B, and a method Bar:B --> C, you chain them together as Foo().Bar() because (A --> B) --> C, to the right.

    What about multiplication? When you say x = Foo() * Bar(), again, the returned result of Foo gets "pumped towards the right", where the multiplication is about to happen. It doesn't get pumped to the left, into the assignment.

    The issue highlighted here is actually that assignment is a weird operation; it would be more consistent if we had defined an operator x --> y which means "stuff the value of x into y". Then assignment would "go to the right" like its supposed to.

    -- Eric

     

  • The multiplication example is a false lead --- one can equally argue that return value of Bar() "pumped to the left" into the multiply which is than pumped into the assignment.  I know the C & C++ (and I assume C#) Standards allow compilers to evaluate either side of the multiplication sign first.

    C# strictly defines the order of evaluation. Subexpressions in C# are always evaluated left-to-right, period, regardless of associativity or precedence.-- Eric

    The chaining example is harder to fight, but it is based more on the semantic of the dot operator than on method return.

  • - Return type to the left of the function name makes it possible to text search for all functions that return type ABC as well as see the return type of function DEF.  This helps where opening a class browser or equivalent does not find all references to a particular type (a problem with legacy code or mixed language projects).   Large bodies of code ( more than 500,000 lines) benefit most from this when you also force each line of code onto a single line to facilitate text searching.

    - Return type on te left of the function name makes it easier to visually see functions that have the same signature or almost the same signature and are candidates for being combined.   Removing 10 and 20 lines of duplicate code at a time will, when done consistentantly over a many month period, will shrink the number of lines in an application by percentage points.  This helps redude the code bloat we've seen in offshore written applications which may have had productivity measured in lines of code instead of code quality.

    - .NET development tools in the near future should have easy 1 click convert code from format X to format Y or convert from C# to VB given the large amounts of metadata output by the compiler.   This would let VS 2014 have developers using a generic metadata as a programming language with C# or VB.NET silently generated and compiled into the application.  This would be a bonus to reusing/reworking old applications in that one could get them to compile under the new VS, run 'extract metadata code' and then work on the extracted code instead of the older format legacy code.

    - C, maybe pre-Posix, let you return and dereference a pointer on the left hand side of an asignment statment.

Page 1 of 2 (26 items) 12