One of the new language features in the upcoming Oxygene 5 compiler is support for duck typing and what we call “soft interfaces”.
The name “duck typing” comes from the old saying that if something walks like a duck and quacks like a duck, it is a duck – and applies the same concept to objects.
Imagine we have the following (a bit contrived) types declared, and let’s further assume that some of them are outside of our direct control – maybe they are declared in the core framework, or in a piece of the project we don’t want to touch:
type IFooBar =interfacemethod DoFoo;method DoBar;end; Foo =classmethod DoFoo;end; Bar =classmethod DoBar;end; FooBar =classmethod DoFoo;method DoBar;end; |
method Test(o: IFooBar); |
Enter duck typing
With the upcoming Fall 2011 release of Oxygene, we’re introducing a new generic Compiler Magic Function called duck that allows you to apply duck typing to let the compiler convert a FooBar into an IFooBar, where necessary. For example, you could write:
var fb :=new FooBar; Test(duck |
Once again, this works because FooBar implements all the necessary methods to satisfy an IFooBar implementation.
So what if that is not the case? What would happen if instead we write the following?
var f :=new Foo; Test(duck |
(E265) Static duck typing failed because of missing methods9 (N2) Matching method "MyApplication.IFooBar.DoBar" is missing
But what if you’re fully aware your object only satisfies a subset of the interface, and you want to pass it anyway? Maybe you know that Test only makes use of DoFoo, but not of DoBar?
Oxygene’s duck typing has a solution for you there as well, by passing an optional DuckTypingMode enum value to the duck() function. DuckTypingMode has three values; the default is Static, and you’ve seen it in action above. Static duck typing will enforce that the passed object fully qualifies for the interface, and will fail with a compiler error if any member (method, property or event) of the interface is not provided by the type.
The second DuckTypingMode is Weak. In weak mode, the compiler will match any interface members it can find, just like in static mode. But for any member it does not find on the original type, it will generate a stub that throws an Not Implemented exception. This enables you to write:
var f :=new Foo; Test(duck |
The third and final DuckTypingMode is Dynamic. Dynamic duck typing will not directly map methods of the source object to the interface; instead, it will create a wrapper class that will dynamically call the interface members, based on what is available at runtime.
You can think of these three modes of duck-typing as being on a scale, with Static (the default) being 100% type safe. If static duck typing compiles, you can rest assured that everything will work as you expect, at runtime. Weak mode trades some type safety for a model that is weaker typed, comparable to, for example, Objective-C (which essentially does weak duck typing everywhere by default – if an object has a method of a given name, you can call it). Dynamic is at the opposite end of the scale, completely resolving all calls at runtime, more like true dynamic languages such as JavaScript.
Soft Interfaces
So this is all good and well, but imagine you have a large (and untouchable) library with classes that implement the DoFoo/DoBar pattern, and you plan to use those all over your code base. Sure, you can declare IFooBar, and use the duck method to duck-type those objects all over the place, but that will get annoying quickly. The compiler knows that DoFoo and DoBar methods are enough to satisfy your pattern, so wouldn’t it be great if you could let the compiler worry about the duck typing where necessary?
That’s where soft interfaces come in. Instead of declaring IFooBar as above, you could declare it as a soft interface, as follows:
type IFooBar = soft interfacemethod DoFoo;method DoBar;end; |
method Test(o: IFooBar); |
var fb :=new FooBar; Test(fb); |
To give a more concrete sample based on real life objects, imagine the following scenario:
type INumberToStringFormatter = soft interfacemethod ToString(format: String): string;end; var d: Double :=15.2;var x: INumberToStringFormatter := d; console.WriteLine(x.ToString('m'));// money formatting |
More to Come
This is just a sneak peek at one set of new features coming this fall. Duck Typing and Soft Interfaces will be available in both Delphi Prism XE2 (a.k.a. Oxygene for .NET) and in Project Cooper, our upcoming Oxygene for Java.
Stay tuned for more.
Read more about Delphi Prism at remobjects.com/prism.
Read more about Project Cooper, currently in beta, at remobjects.com/cooper.