Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any advantage in disallowing interface implementation for existing classes?

In static OOP languages, interfaces are used in order to declare that several classes share some logical property - they are disposable, they can be compared to an int, they can be serialized, etc.

Let's say .net didn't have a standard IDisposable interface, and I've just came up with this beautiful idea:

interface IDiscardable { void Discard(); }

My app uses a lot of System.Windows.Forms, and I think that a Form satisfies the logical requirements for being an IDiscardable. The problem is, Form is defined outside of my project, so C# (and Java, C++...) won't allow me to implement IDiscardable for it. C# doesn't allow me to formally represent the fact that a Form can be discarded ( and I'll probably end up with a MyForm wrapper class or something.

In contrast, Haskell has typeclasses, which are logically similar to interfaces. A Show instance can be presented (or serialized) as a string, Eq allows comparisons, etc. But there's one crucial difference: you can write a typeclass instance (which is similar to implementing an interface) without accessing the source code of a type. So if Haskell supplies me with some Form type, writing an Discardable instance for it is trivial.

My question is: from a language designer perspective, is there any advantage to the first approach? Haskell is not an object oriented language - does the second approach violates OOP in any way?

Thanks!

like image 737
Benesh Avatar asked Aug 11 '14 19:08

Benesh


4 Answers

This is a difficult question, which stems from a common misunderstanding. Haskell type classes (TC), are said to be "logically similar" to the interfaces or abstract classes (IAC) from object-oriented programming languages. They are not. They represent different concepts about types and programming languages: IAC are a case of subtyping, while TC is a form of parametric polymorphism.

Nevertheless, since your questions are methodological, here I answer from a methodological side. To start with the second question:

does the second approach [that of extending the implementation of a class outside the class] violate OOP in any way

Object oriented programming is a set of ideas to describe the execution of a program, the main elements of an execution, how to specify these elements in the program's code, and how to structure a program so as to separate the specification of different elements. In particular, OOP is based in these ideas:

  • At any state of its execution, a process (executing program) consists of a set of objects. This set is dynamic: it may contain different objects at different states, via object creation and destruction.
  • Every object has an internal state represented by a set of fields, which may include references to other related objects. Relations are dynamic: the same field of the same object a may at different states point to different objects.
  • Every object can receive some messages from another object. Upon receiving a message, the object may alter its state and may send messages to objects in its fields.
  • Every object is an instance of a class: the class describes what fields the object has, what messages it can receive, and what it does upon receiving a message.
  • In an object a, the same field a.f may at different states point to different objects, which may belong to different classes. Thus, a needs not to know to what class those objects b belong; it only needs to know what messages do those objects accept. For this reason, the type of those fields can be an interface. The interface declares a set of messages that an object can receive. The class specifies explicitly what interfaces are satisfied by the objects of that class.

My answer to the question: in my opinion yes.

Implementing an interface (as suggested in the example) outside a class breaks one of these ideas: that the class of the object describes the complete set of messages that objects in that class can receive.

You may like to know, though, that this is (in part) what "Aspects", as in AspectJ, are about. An Aspect describes the implementation of a certain "method" in several classes, and these implementations are incorportated (weaved) into the class.

To answer back the first question, "is there any advantage to the first approach", the answer would be also yes: that all the behaviour of an object (what messages it answers to) is only described in one place, in the class.

like image 61
Diego E. Alonso-Blas Avatar answered Oct 20 '22 18:10

Diego E. Alonso-Blas


Well, the Haskell approach does have one disadvantage, which is when you write, for example, two different libraries that each provides its own implementation of interface Foo for the same external type (provided by yet a third library). In this case now these two libraries can't be used at the same time in the same program. So if you call lack of a disadvantage an advantage, then I guess that would be one advantage for the OOP language way of doing this—but it's a pretty weak advantage.

What I would add to this, however, is that Haskell type classes are a bit like OOP interfaces, but not entirely like them. But type classes are also a bit like the Strategy and Template Method patterns; a type class can be simulated by explicitly passing around a "dictionary" object that provides implementations for the type class operations. So the following Haskell type class:

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m

...can be simulated with this explicit dictionary type:

data Monoid_ m = Monoid_ { _mempty :: m, _mappend :: m -> m -> m }

...or an OOP interface like this:

interface Monoid<M> {
    M empty();
    M append(M a, M b);
}

What type classes add on top of this is that the compiler will maintain and pass around your dictionaries implicitly. Sometimes in the Haskell community you get arguments about when and whether type classes are superior to explicit dictionary passing; see for example Gabriel Gonzalez's "Scrap your type classes" blog entry (and keep in mind that he doesn't 100% agree with what he says there!). So the OOP counterpart to this idea would be instead of extending the language to allow external implements declarations, what are the drawbacks to just explicitly using Strategies or Template Methods?

like image 21
Luis Casillas Avatar answered Oct 20 '22 17:10

Luis Casillas


What you are describing is the adapter pattern. The act of composing an object in a new type that provides some additional behavior to the underlying type, in this case the implementation of another interface.

As with so many design patterns, different languages choose different design patterns to incorporate directly into the language itself and provide special language support, often in the form of a more concise syntax, while other patterns are need to be implemented through the use of other mechanisms without their own special syntax.

C# doesn't have special language support for the adapter pattern, you need to create a new explicit type that composes your other type, implements the interface, and uses the composed type to fulfill the interface's contract. Is it possible for them to add such a feature to the language, sure. Like any other feature request in existence it needs to be designed, implemented, tested, documented, and all sorts of other expenses accounted for. This feature has (thus far) not made the cut.

like image 2
Servy Avatar answered Oct 20 '22 17:10

Servy


What you are describing is called duck typing, after the phrase "If it walks like a duck, swims like a duck, and quacks like a duck, then it's a duck".

C# actually does allow dynamic (run-time) duck typing through the dynamic keyword. What it doesn't allow is static (compile-time) duck typing.

You'd probably need somebody from Microsoft to come along and provide the exact reasons this doesn't exist in C#, but here are some likely candidates:

  • The "minus 100 points" philosophy to adding features. It's not just enough for a feature to have no drawbacks, to justify the effort put into implementing, testing, maintaining and supporting a language feature, it has to provide a clear benefit. Between the dynamic keyword and the adapter pattern, there's not many situations where this is useful. Reflection is also powerful enough that it would be possible to effectively provide duck typing, for example I believe it'd be relatively straightforward to use Castle's DynamicProxy for this.

  • There are situations where you want a class to be able to specify how it is accessed. For example, fluent APIs often control the valid orderings and combinations of chained methods on a class through the use of interfaces. See, for example, this article. If my fluent class was designed around a grammar which stated that once method A was called, no other methods except B could be called, I could control this with interfaces like:

    public class FluentExample : ICanCallAB
    {
        public ICanCallB A()
        {
            return this;
        }
    
        public ICanCallAB B()
        {
            return this;
        }
    }
    
    public interface ICanCallA
    {
        void A();
    }
    
    public interface ICanCallAB : ICanCallA
    {
        void B();
    }
    

    Of course, a consumer could get around this using casting or dynamic, but at least in this case the class can state its own intent.

  • Related to the above point, an interface implementation is a declaration of meaning. For example, Tree and Poodle might both have a Bark() member, but I would want to be able to use Tree as an IDog.

like image 1
Ben Aaronson Avatar answered Oct 20 '22 17:10

Ben Aaronson