共用方式為


Duck Notation

I have been working with the C# and VB teams on design guidelines for LINQ. We started to talk about the so called Query Pattern, which describes what you need to do if you want a custom type to support the new query operators (select, where, group by, etc.).

The query operators use duck typing to determine whether they can operate on a type or not. This means that instead of implementing an interface (static typing) a queryable type will need to have a set of members that follow a specified set of conventions (naming, parameter and return types, etc).

For example, the C#’s foreach operator already uses duck typing. This might be surprising to some, but to support foreach in C# you don’t need to implement IEnumerable! All you have to do is:

Provide a public method GetEnumerator that takes no parameters and returns a type that has two members: a) a method MoveMext that takes no parameters and return a Boolean, and b) a property Current with a getter that returns an Object.

For example, the following type supports foreach:

 

class Foo {

    public Bar GetEnumerator() { return new Bar(); }

 

    public struct Bar {

        public bool MoveNext() {

            return false;

        }

        public object Current {

            get { return null; }

        }

    }

}

 

// the following complies just fine:

Foo f = new Foo();

foreach (object o in f) {

    Console.WriteLine(“Hi!”);

}

But, as you can see in the yellow highlight above, the describing the foreach pattern in English (or any other spoken language) is quite difficult and not very readable, especially if you contrast it with the simplicity specifying requirements based on static typing:

Implement IEnumerable.

… and having IEnumerable defined as:

public interface IEnumerable {

public IEnumerator GetEnumerator();

}

public interface IEnumerator {

public bool MoveMext();

public object Current { get; }

}

The english description gets much worse for something like the query pattern, which is way more complex than the foreach pattern. Because of that, I was thinking that there must be a better way to specify such patterns based on duck typing. But when I searched the web, to my surprise, I could not find any simple notations to do that. If you know of any, please let me know.

In the absence of an existing notation, I started to think about something like the following:

 

[Foreachable] {

    public [Enumerator] GetEnumerator();

}

[Enumerator] {

    public bool MoveMext();

    public [ItemType] Current { get; }

}

This seems much easier to parse than the English description of the pattern. What do you think?

Comments

  • Anonymous
    July 18, 2007
    I am super interested in these LINQ design patterns. Is there any chance you can publish them as a work-in-progress document to the community? I'm sure I can't be alone in wanting to read these - and am more than prepaired to be flexable and accept they're like to mutate a lot in the short term! :-)

  • Anonymous
    July 18, 2007
    What's the need for supporting duck typing at all?  Why isn't static typing sufficient?

  • Anonymous
    July 18, 2007
    You might want to look at Haskell type classes (http://en.wikipedia.org/wiki/Type_class). As far as I understand it, they are used to solve this kind of problems, but I'm not sure if it helps with finding good way for writing "easy to parse description".

  • Anonymous
    July 18, 2007
    Tom, yes, I would be more than willing to publish something that is not formally reviewed/approved yet, but we don't even have that. We are still in the stage of exploring what we want to have in the guidelines. For example, we do know that extension methods can be abused and we willo have some guidelines recommending where the boundary between good and bad usage of the extension methods is, but we don't yet know how to describe the boundary. We need duck typing cause static types are not expressive enough in some cases. For example, there is no way to say that a parameter can be either an Expression or a delegate. Tomas, thanks for the pointer to Haskell. I will take a look.

  • Anonymous
    July 18, 2007
    Here's an idea for C# 4 or 3.5 or whatever which would solve this problem, and make millions of developers the world over jump for joy. I'd hazard a guess and say it wouldn't be too hard with the type inferencing stuff that's already there: Define an alternative to an interface called 'requirement' It would work and behave exactly the same as an interface EXCEPT that it would use duck typing instead of static typing: For example: public requirement Closeable {   void Close(); } public void TestMethod( Closeable c ) { c.Close(); } TestMethod( new System.Windows.Forms.Form() ); A windows form has a public void Close() method so this should work. The typechecking is still just as good and can be done entirely by the compiler so there should be no speed or safety hit. 'requirement' may not be the best word, but the world would rejoice if C# had this

  • Anonymous
    July 18, 2007
    The comment has been removed

  • Anonymous
    July 18, 2007
    mharder :   Duck typing is used to allow objects that do not implement IEnumerable to be used with foreach. If it walks like a duck (implements GetEnumerator), then it is a duck!

  • Anonymous
    July 18, 2007
    The comment has been removed

  • Anonymous
    July 18, 2007
    Just out of interest, I can see that the added flexibility of duck typing might be necessary for the query operators, but what does it buy you in the case of foreach? IEnumerable and IEnumerator are trivial interfaces and it's surely more readable if you implement those interfaces, rather than just having a floating GetEnumerator() method on your object? What is the usecase for ducktyping on foreach?

  • Anonymous
    July 19, 2007
    I'll grant that interfaces are not always expressive enough, but I am confused why foreach doesn't just require IEnumerable? I mean the pattern is quite specific, where you are not likely to run into an object that has these members but doesn't implement IEnumerable and if you are creating your own type, implementing IEnumerable doesn't seem like an onerous requirement. But I could see the benefit of being able to do something like Orion suggested. Maybe it could be something similar to extension methods, i.e. the ability to decorate some object with an Interface. This way if you have several objects that have similar use patterns but no common interface, you could create one and decorate the classes with that interface. You could already achieve something similar with Attributes, but that does move the checking to runtime.

  • Anonymous
    July 19, 2007
    mharder:  The point that people are making is that sometimes a type -- for whatever reason -- doesn't implement an interface.  Perhaps it has all the methods but doesn't declare itself (eg, it's an older type that predates an interface), or it only partially implements an interface (eg, the interface may encompass too much). It also makes it easier when you're crossing service boundaries.  You (the client) can specify some interfaces that partition fields into interesting bits.  IId { string Id { get; set; }}, for example, and get access to things without having to explicity reflect over the type. That reflection is already commonly used for this sort of thing is a strong motivation, for me at least, to push for duck typing.

  • Anonymous
    July 19, 2007
    Krzysztof: I like the idea.. a lot.  In C#'s case I wouldn't use square brackets, because of the ambiguity that would cause with attributes.  I'd suggest parentheses instead (which has conceptual baggage in the casting department, itself related to the idea of duck typing). After all, maybe we'd like to extend attributes to other parts of the syntax at some point. Would this be limited to public APIs (like interfaces are today)? Also, your [ItemType] .. why not just use generics there? (Foreachable<T>) {    public (Enumerator<T>) GetEnumerator(); } (Enumerator<T>) {    public bool MoveMext();    public T Current { get; } } Orion's idea also has strong merits, and could be easier on the syntax.  But in the end, this is all dependant on the language it's being fit into.

  • Anonymous
    July 19, 2007
    Hi Krzysztof, Thank goodness you're getting involved with design guidelines for language-integrated query. There's a lot of work to be done in this area. The first thing you must surely do is get the awful decision to use the acronym "Linq" everywhere instead of the word "Query" reversed. This decision change goes against pretty much everything that the wonderful FDG book stood for. There's still time. http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1573951&SiteID=1 Thanks, Pete.

  • Anonymous
    July 19, 2007
    Dynamic nature of C# I bet you don't know :)

  • Anonymous
    July 19, 2007
    Hi, What's wrong with the interface description? It's well known, no need to come up with some new "syntax". The "duck" type doesn't have to "implement" the interface it just used to describe the contract. But seriously... what will happen if it doesn't implement all of the contract ;) Thanks /Jonas

  • Anonymous
    July 19, 2007
    Hi, Could you provide more examples like the IEnumerable one?

  • Anonymous
    July 20, 2007
    I think that that notation could be usable, however [ItemType] should be replaced with [Foreachable], or else something like this [ItemType : Foreachable] { ... } [Enumerator] { [ItemType] } Now, it's not clear that ItemType is actually the "foreachable" type.

  • Anonymous
    July 20, 2007
    Dynamic nature of C# I bet you don't know :)

  • Anonymous
    July 20, 2007
    This seems similar to the all the discussions of "concept"s in C++ generic libraries.

  • Anonymous
    July 23, 2007
    How about adding an [implicit] attribute to an interface definition?

  • Anonymous
    July 24, 2007
    Sorry for a late reply. I was on short vacation. Just to clarify, this is not about features in the compiler. This is just a paper notatio so it's easier to describe such patterns. And of course, thanks for all the pointers and comments!

  • Anonymous
    August 19, 2007
    Why Duck Typing Matters To C# Developers

  • Anonymous
    September 15, 2007
    Krzysztof Cwalina wrote a post detailing how have your class support the "foreach" loop without implementing

  • Anonymous
    October 18, 2007
    You are wrong :-) I have just sat and passed 70-536, there was a question on foreach and the "correct" answer was "IEnumerable".

  • Anonymous
    October 26, 2007
    Wayne, what was the question?

  • Anonymous
    November 02, 2007
    I really like Ian Horwill suggestion. The only problem is that to really duck typing we are not forced to "implement" all members of the interface. We have to implement only the ones used in a given method.

  • Anonymous
    November 08, 2007
    There was a question in the exam about what functionality must be implemented by a class so that it can support foreach, the answer being "implement the IEnurable interface". From reading your post the the correct answer implement MoveNext and Current was not available as an option. It is this type of inaccuracy/simplification in the exams that lessens the value of certification.

  • Anonymous
    November 17, 2007
    Dear Krzysztof Cwalina, There is some confusion about C#, duck notation and foreach on Wikipedia. Please see:' http://en.wikipedia.org/wiki/Talk:Duck_typing#C.23 Sincerly  Ulf L

  • Anonymous
    November 17, 2007
    If you take into account this comparison of Duck-Typing with Structural-Typing: http://en.wikipedia.org/wiki/Duck_typing#Comparison_with_Structural_type_systems I don't think you give enough information for me to determine which you intend to implement. COuld you say which? Thanks.

  • Anonymous
    December 13, 2007
    I'm sorry, but who decided that duck typing in C# was a good idea? The foreach decision is still incomprehensible, but since almost no one knows about it, and since its mostly irrelevant since everyone implements IEnumerable anyway, I can let that slide. But now you are pushing duck typing to the forefront in what has been (and should be!) a statically typed language. Not that dynamic language features can't be useful, but shoehorning them into a language that clearly wasn't designed for them is just asking for trouble. If the C# language team continues on this course, I am confident that C# as a language will be mostly unusable just one or two versions from now. Please, THINK about what you are doing, and get community feedback, before you throw yourself off a cliff. Compromising the integrity of a language for the sake of a "cool" feature is a recipe for disaster, especially when there are other ways to accomplish the same goal that could also be useful in other areas and would not compromise the language.

  • Anonymous
    January 13, 2008
    I have run into this situation in various occasions:

  1. Sometimes a common pattern is implemented without a formal interface.
  2. Looking for static methods which cannot be defined in an interface. I had never heard of "duck typing", but I used a similar notation with the concept of a "shadow" (with similar logic "If it casts the shadow of a signature, it has that signature."). I defined interfaces that contained the signatures for which I was looking. This process would be much easier if it was handled by the compiler. The syntax that would be awesome would be very similar to interfaces, but would allow all the generally possible modifiers. For the foreach example: shadow SForeachable {   public SEnumerator GetEnumerator( ); } shadow SEnumerator {   public object Current{ get; }   public bool MoveNext( ); } No object could actually "implement" the shadow, it would simply match it or not, but this syntax would be easy to use with type casting constructs. For example: foreach(MyObject obj in myEnumerable) {   // do something in the loop } would be converted into: SForeachable foreachable = (SForeachable)myEnumerable; while(foreachable.MoveNext( )) {   MyOjbect obj = foreachable.Current;   // do something in the loop } From my experience, the most useful aspect would be to have some way of dealing with static methods that are commonly implemented: shadow SParsable {   public static shadow Parse( string text ); } In this syntax I used the shadow keyword in place of the concrete type that is defined. The problem comes with merging instance syntax with static syntax. Something like the following might do: Type x = ...// some time gathered by reflection Decimal instance = SParsable(x).Parse( text ); I agree that this violates OO principles, but it drastically simplifies various tasks. At a minimum the "shadow" syntax could provide a representation at least in documentation for "duck typing".
  • Anonymous
    January 31, 2008
    I often rethink or have additions to my posts. This topic of what's coming in C# vNext is definitely

  • Anonymous
    February 22, 2008
    Yes, your example compiles. The following, however, does not. Does this make you agree that the duck typing choice for foreach was a bad one? If not, why not?    class Program    {        static void Main(string[] args)        {            // the following does not complile!            // all thanks to foreach duck typing            // rather than properly using interfaces...            YearCollection f = new YearCollection();            foreach (object o in f)            {                Console.WriteLine("Hi!");            }            Console.ReadLine();        }    }    class YearCollection    {        public Year GetEnumerator() { return new Year(); }        public struct Year : IEnumerator        {            public object Current            {                get { return new DateTime(DateTime.Today.Year, 1, 1); }            }            private int cursor;            bool IEnumerator.MoveNext()            {                if (cursor >= DateTime.Today.Year)                    return false;                cursor++;                return true;            }            object IEnumerator.Current            {                get { return new DateTime(cursor, 1, 1); }            }            void IEnumerator.Reset()            {                cursor = 0;            }        }    }

  • Anonymous
    March 10, 2008
    Hi, I guess in this particular case (foreach), duck typing is used so that CLR woudn't need to cast an enumerator to IEnumerator interface. Hence, we can use structs as enumerators (thus decreasing the pressure on GC). Generic List and Dictionary enumerators are implemented in that way. But the drawback is that we need to make such enumerator structs public nested (not private nested) - otherwise boxing occures anyway.

  • Anonymous
    July 22, 2008
    I thought I know how the foreach construct worked under the covers. I figured the runtime/compiler would

  • Anonymous
    June 29, 2010
    The comment has been removed