Interface duck typing, and anonymous interfaces

Topics: C# Language Design
Jun 28, 2014 at 7:35 PM
Edited Jun 28, 2014 at 7:35 PM
After using TypeScript for a while, I'm really missing interface duck typing and anonymous interfaces in C#. To be clear, by interface duck typing, I just mean that if an object satisfies the contract defined by an interface, but doesn't explicitly implement the interface, it can still be used as if it implemented the interface. For example:
public interface IFoo
{
    string Prop1 { get; }
    int Prop2 { get; }
}

public void ConsumeFoo(IFoo foo)
{
    // do something with foo
}

public class Bar
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
    public bool Prop3 { get; set; }
}
In this case, I'd like to be able to pass an instance of a Bar to the ConsumeFoo method since Bar is contractually an IFoo. Obviously, this would work with anonymous objects as well. For example:
ConsumeFoo(new { Prop1 = "test", Prop2 = 5 });
From here, it seems a small step to support anonymous interfaces. For example:
public void ConsumeFooLikeObject({ string Prop1 { get; }; int Prop2 { get; } } foo)
{
    // do something with foo.Prop1 and foo.Prop2
}

public { int Prop2 { get; }; bool Prop3 { get; } } ProduceBarLikeObject()
{
    return new Bar();
    // - or -
    return new { Prop2 = 5, Prop3 = false };
}
Jun 29, 2014 at 8:58 PM
I too would like some way of introducing duck typing/structural typing into C# by choice where it fits. (dynamic gives me this in the same way that a 3D printer gives me a fork... it's a lot slower.)

Anonymous interfaces in the way you describe are a bit of a mouthful. Then again, so are anonymous types, although you always use them by example.

(I proposed something I called adapters, which would make them interfaces you could make types that weren't your own conform to, but it doesn't seem like anyone was interested in that.)

The C# team has mentioned that it's looking at, or at least preparing for new features not to clash with, pattern matching, which is all about structural typing. I'm very interested in hearing their initial ideas so we have a general direction to consider.
Jul 1, 2014 at 9:39 AM
I like the idea, but I can see one issue with such implicit interfaces - IMO marker interfaces (interfaces with no members defined) should be excluded from this. It would be allowed to pass literally anything to method which expects instance of such marker interface. And this would contradict the whole idea of marker interface.
Jul 1, 2014 at 10:56 AM
Structural interfaces should be separate from nominal interfaces but work as similarly as possible to avoid unnecessary confusion (and yes, I know conflating the two could itself be confusing).
Jul 1, 2014 at 5:44 PM
What I would like to see would be a means by which an interface could declare that any reference which satisfies one or more sets of constraints but does not implement the interface should be deemed as implementing it via static classes designated by the interface's attributes. For example:
[attribute designating EnumeratorEx<T>.ClassHelper<U> and EnumeratorEx<T>.StructHelper<U>]
public interface IEnumeratorEx<out T> : IEnumerator<T>
{
  int Move(int distance);
}
public static class EnumeratorEx<T>
{
  public static class ClassHelper<U> where U:class,IEnumerator<T>
  {
    static int Move(U it, int amount)
    {
      while(amount > 0 && it.MoveNext())
        amount--;
      return amount;
    }
  }
  public static class StructHelper<U> where U:class,IEnumerator<T>
  {
    static int Move(ref U it, int amount)
    {
      while(amount > 0 && it.MoveNext())
        amount--;
      return amount;
    }
  }
}
This feature would need runtime support, but it would allow code to cast any IEnumerator<T> to an IEnumeratorEx<T>; if the type implements the interface itself, those implementations would be used; otherwise the runtime would auto-generate implementations that call the static Move methods. This would allow things like the Count extension method to work much more efficiently in cases where e.g. IEnumerator<T>.Append is used to concatenate things which could skip over data without having to return every item. It would also be helpful in cases where code might need to hold a mixture of custom user objects and strings (strings could satisfy an interface constraint for an interface which included a "default" implementation that would work with strings)

Such an approach would not automatically supply structural typing for interfaces, but an interface could designate adapter classes for any other interfaces which implemented suitable functionality (whether using the same or different method names).
Jul 1, 2014 at 10:12 PM
I'm guessing StructHelper's U in your code sample is meant to have the struct type constraint and not the class constraint.

Don't really like bringing this up again and again, but for my thoughts on that, see my previous proposal. Essentially, like your proposal I think some variant of Java's "defender methods" mixed with the ability to explicitly adapt a given class to the interface would be a very clean solution.

That said, I'm not sure it'd do anything for the typical duck typing scenario, where implemented interfaces would be sparse to begin with. I'm imagining the use case is a lot closer to data model classes (maybe internal to the middle of a calculation more than the canonical data model at the edge of a system) than to collections. That's why I'm pushing a structural variant of an interface - to create an entry point into all this stuff that is not part of the defined class.

Duck typing in other systems, as long as it's of the "see what I'm doing here? I want you to just do this; never mind the type hierarchy, check my objects and warn if it doesn't fit" variety, does not require much ceremony. TypeScript certainly walks a better middle way than either dynamic or inventing C#-like interfaces for everything. Like ryantrem, I'd like to have some "cookie cutter/if it fits I sits" second type of interface.
Jul 2, 2014 at 12:47 AM
JesperTreetop wrote:
That said, I'm not sure it'd do anything for the typical duck typing scenario, where implemented interfaces would be sparse to begin with. I'm imagining the use case is a lot closer to data model classes (maybe internal to the middle of a calculation more than the canonical data model at the edge of a system) than to collections. That's why I'm pushing a structural variant of an interface - to create an entry point into all this stuff that is not part of the defined class.
I guess I tend to be skeptical of the notion that things should be bound based upon member name alone; it's fine to say "I want anything which I can Add items to", but IMHO it should be necessary to have some affirmation somewhere that a class method which is called Add will, in fact, add items to a collection as opposed to, e.g., performing arithmetic addition. I think it's cleanest to define abilities in terms of interfaces, rather than method names [in a sense, interfaces act a little bit like a namespace, allowing the concept "add a T to a collection" to be expressed as ICollection<T>.Add() rather than AddToCollection()], but it should be possible for things other than a class to know how to do things with instances of that class.

I just looked briefly at your adapt concept; it seems like it might be reasonable syntax if languages were aware of the adapters, rather than merely having them be a function of runtime "magic" [in a fashion similar to the way a SiameseCat[] is regarded as implementing IList<Animal>].
Duck typing in other systems, as long as it's of the "see what I'm doing here? I want you to just do this; never mind the type hierarchy, check my objects and warn if it doesn't fit" variety, does not require much ceremony. TypeScript certainly walks a better middle way than either dynamic or inventing C#-like interfaces for everything. Like ryantrem, I'd like to have some "cookie cutter/if it fits I sits" second type of interface.
The kind of duck typing I would like to see languages and framework support would allow a type to be declared which would mean "something that implements IEnumerable<Cat> and IPounceable", and have it satisfy an IEnumerable<Cat> and IPounceable constraints without having to know at compile-time any particular type from which the referenced object would derive and which satisfying both constraints [it's entirely possible that one might have a collection of things, all of which satisfy both constraints, but have no common ancestor which does so]. At least for class-type interface implementations, one approach would be to have the runtime include "special" interface
IDoubleDuck<T1,T2> { }
with two "magical" features:
  1. It would be regarded as implementing T1 and T2, and references of type IDoubleDuck<T1,T2> would implicitly convert to both T1 and T2.
  2. All types which implement T1 and T2 would be considered implementations of IDoubleDuck<T1,T2>, and references to such would implicitly convert to IDoubleDuck<T1,T2>.
If one needed to duck-type a combination of IEnumerable<Cat>, IPounceable, and IPurrable, one could use IDoubleDuck<IEnumerable<Cat>, IDoubleDuck<IPounceable, IPurrable>> or IDoubleDuck<IDoubleDuck<IEnumerable<Cat>, IPounceable>, IPurrable>, or any other such permutation [all would be implicitly convertible to each other].
Jul 2, 2014 at 9:44 PM
supercat wrote:
JesperTreetop wrote:
That said, I'm not sure it'd do anything for the typical duck typing scenario, where implemented interfaces would be sparse to begin with. I'm imagining the use case is a lot closer to data model classes (maybe internal to the middle of a calculation more than the canonical data model at the edge of a system) than to collections. That's why I'm pushing a structural variant of an interface - to create an entry point into all this stuff that is not part of the defined class.
I guess I tend to be skeptical of the notion that things should be bound based upon member name alone; it's fine to say "I want anything which I can Add items to", but IMHO it should be necessary to have some affirmation somewhere that a class method which is called Add will, in fact, add items to a collection as opposed to, e.g., performing arithmetic addition.
That's fair enough, but then it's a different feature than the one discussed at the start of this thread, where the interface is structural. Duck typing is structural and the feature ryantrem was missing from TypeScript is structural. The point of duck typing is that it's based on the behavior, i.e. quacking and walking like a duck, not on the species of the "duck".

That said, I wouldn't mind being able to specify objects that implement two interfaces. You can often get it done with type parameter constraints, but not always.
Jul 3, 2014 at 8:16 PM
i.e. quacking and walking like a duck, not on the species of the "duck".
I see duck typing as encompassing two related issues:
  1. Given a reference to a things which has a particular ability, make use of that ability without the authors of those things having had to coordinate on the naming of an interface.
  2. Provide a means by which code can demand a particular combination of abilities, without the author of the things to be used having to define an interface for every different combination of abilities that might be required.
In many cases, I would think the first goal would be best handled by allowing an interface to specify that types meeting certain criteria should be deemed to implement it. That wouldn't allow binding based upon member name alone, but IMHO the desire for such binding is generally a result of a lack of anything better. What's necessary isn't so much to provide a means by which any code which has something of a type that happens to implement a method with a certain name and signature will automatically be able to pass it to something needing that name and signature, but rather to have a means by which code which has something of a particular known type which includes an ability will be able to pass a reference to code needing that ability will be able to specify that things of that type should be passable to code needing that ability.

The "double-duck interface" would be intended largely to allow for the use of finer-grained interfaces than would otherwise be practical. If one wants a method which can add something of type T to a collection, one should ideally use something like IAddItem<in T>, but at present there's no interface just for such a fine-grained purpose. If such an interface existed, I would expect that most classes that would be acceptable to code needing something that receive a T would implement it. Such interfaces don't exist at present because, I suspect, it would be a nuisance for implementers to define interfaces for every combination of abilities a collection might need. If code which only needs particular abilities could ask for them more specifically, then such ability would favor the creation of separate interfaces for each ability an object may or may not have.
Jul 3, 2014 at 10:01 PM
Edited Jul 3, 2014 at 10:07 PM
That makes what you want clearer.

What you want isn't really classically defined as duck typing and doesn't need duck typing. Duck typing can be used to implement it, but duck typing can do a lot of stuff. If there were duck typing interfaces and classic CLR interfaces in C# today, a facility to invent a type or something type-like to say "an object that does these two, originally separate and non-entwined things" would be equally useful to both kinds of interfaces.

If I were you, I'd split the two issues in two feature requests for clarity. If the design to solve the first problem could turn out to solve the second problem - great.

Objective-C and Swift has the feature you're looking for in terms of being able to specify multiple protocols (their interfaces) in the type specifier for protocols. We've all heard of union types, maybe this should be named an intersection type? Types that don't really exist as "new me up one of these" are already in the language in the form of interfaces. Actually, how's this?
interface IAdd<T> { T Add(params IEnumerable<T> ts); }
interface ISubtract<T> { T Subtract(params IEnumerable<T> ts); }

shape IBasicMath<T> : IAdd<T>, ISubtract<T>;
or why not
void Mathy<T>(shape<IAdd<T>, ISubtract<T>> mathyParameter) { ... }