2

I have often wanted the same feature which is asked for e.g. here and in many other questions on SO:

Being able to specify that something satisfies multiple interfaces without specifying the concrete type.

e.g. in C# pseudo syntax

(IEnumerable<string>, INotifyCollectionChanged) GetStringData() {
    return /* an object which implements both interfaces */;
}

It is possible to emulate this for method parameters using generics, but not for return values, fields, properties, etc.

Is there a name for this in type theory which I could use to find more information about this?

Are there any (strongishly typed) languages which implement this?

One example where this could be usefull would be an imaginary implementation of Stream.

Currently there is an abstract class with many methods / properties and feature-check properties which enable/disable functionality.

With this feature you could have many interfaces IReadStream, IWriteStream, ISeekable, IHasFixedLength, etc. and then say Ok, I need something where I can read and seek, so I take (IReadStream + ISeekable).

=============== (Too long for a comment)

I think the best way to implement this in C# would be a combination of returning object / explicit casting and checkers implemented with Roslyn which verify that you only cast to 'allowed' interfaces. e.g.

[MultiReturn(typeof(IReaderStream), typeof(ISeekable))]
object GetSeekableReaderStream() {
    var stream = new ConcreteReaderWriterSeekableStream();
    // stream actually implements IReaderStream, ISeekable AND IWriterStream
    // but I only want to expose the first two
    return stream;
}

This could then be used like

var stream = GetSeekableReaderStream();
(stream as ISeekable).Seek(5); // OK
(stream as IReaderStream).Read(...); // OK
(stream as IWriterStream).Write(...); // legal for the compiler AND at runtime, but the custom analyzer would scream

Something similar (but different) which is discussed here in roslyn would be "structural interfaces", but they are similar to ducktyping, they only enforce that specific methods are implemented, not that the object implements specific interfaces. Still this would be "near enough" that most of it would be possible.

  • 3
    Couldn't you simply declare a new interface that inherits from both interfaces, and use that as your return type? I suspect "multiple inheritance" is the closest we're going to get to a theoretical name for this. – Ixrec Jan 5 '16 at 19:24
  • 1
    @Ixrec the concrete type would have to implement that new marker interface, though. – MetaFight Jan 5 '16 at 19:25
  • 2
    @Ixrec, that's exactly what I want to avoid. Also, this wouldn't work with existing third-party types. – Lukas Rieger Jan 5 '16 at 19:26
  • 2
    This would require some form of type constraint expressions. These are available in C# for generic parameters (using the where clause) but not for regular variables, fields, and methods. In your example, you can, perhaps, return two objects, one of each interface, instead of requiring a single object to implement both interfaces. – Erik Eidt Jan 5 '16 at 19:36
  • 6
    Maybe you're looking for intersection types. – effeffe Jan 5 '16 at 20:20
4

Scala allows this and calls the resulting type a Compound Type. Your example would look something like this in Scala:

def seekAndRead(stream: IReaderStream with ISeekable) {
    stream.seek(5) // OK
    stream.read(...) // OK
    stream.write(...) // Not OK
}

IReaderStream with ISeekable is the compound type.

You're also allowed to say that a type variable is a subtype of a compound type:

class UsesStreams[A <: IReaderStream with ISeekable] {
    // use variables of type A in here
}
  • 2
    Thanks! I really need to learn scala. I am currently working a bit with F# and love the extended type system (DUs, etc) but scala seems to go still a bit farther away from mainstream. – Lukas Rieger Jan 5 '16 at 20:21
  • 1
    @LukasRieger Not really. The type system is vastly richer, but other than that, it's more similar to mainstream languages than F#. Most people by far don't use the advanced type system features -- they're mainly for designing an API. It also has far, far better IDE tooling with IDEA. I also feel that Scala as a language is better designed, though the syntax is probably worse. – GregRos Jan 5 '16 at 20:41
  • @GregRos I think one designer of F# said that it's greatness was not (only) in what it can do, but also in what it can't do. Because you can't really program in F# like you would in C# / Java, you are forced to learn functional programming. And yes, compared to C# / Java, the F# tooling is abysmal. The syntax is probably a question of whitespace versus { } – Lukas Rieger Jan 5 '16 at 20:50
  • @LukasRieger Not everyone agrees that the all the things it can't do are bad, though. It doesn't extend just to functional vs. imperative, but to all sorts of things. In Scala, union types are well-integrated into everything else. They're like normal types, with bonuses. In F#, they're highly restricted and only suitable for specific uses. The designers had the vision of a perfect union type, to be used only according to their commandments, but some people might want to use them a bit differently. – GregRos Jan 5 '16 at 21:07
-2

Just make a new interface that implements both IEnumerable<string> and INotifyCollectionChanged like this:

interface IBoth : IEnumerable<string>, INotifyCollectionChanged
{
}

And then have your method return this new interface:

IBoth GetStringData() {
    return /* an object which implements both interfaces */;
}

That's it.

  • 7
    There are two problems with that approach: 1) You need to modify the type to implement the interface (or use an adapter) and 2) It leads to an exponential explosion of interfaces | It's just a weak work-around for languages with a crippled type system. – CodesInChaos Jan 5 '16 at 21:16
  • 1
    At the very least, it should be IBoth<T>. – GregRos Jan 5 '16 at 21:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.