4

This is a bit of a subjective question I guess but I was hoping for opinions on either side of the argument.

So take this for example in C#:

public interface IFactory<Tin, Tout> : IFactory
{
    Tout Create(Tin param);
}

Now as you can see we take an input type and an output type, however a lot of frameworks often have a raft of different generics in these sort of cases where you often end up having TinOne, TinTwo etc to cope with multiple use cases. So even in the C# source it generally does <all input types, output type> however when you actually go to look at the implementations of interfaces like this, you generally (again subjective) want to know what the return type is, as that is often the contract you care about for the rest of the program, so it often makes it harder to find these interfaces in code due to the output type being last in the list not first.

So why was this approach taken? as personally I would find output being first being more logical, but I am sure there is a good reason why.

  • Hold on a sec, the interface implements itself?! – kayess Dec 2 '16 at 9:37
  • 2
    @kayess no, it implements a base non generic interface that overloads the name IFactory. Many library interfaces that predate generics have a deriving generic interface with the same name – Caleth Dec 2 '16 at 9:37
  • Also for clearer reading you could make the first type argument contravariant using the in keyword and make the second covariant by using out keyword. – kayess Dec 2 '16 at 9:40
  • Ran out of edit time: also using in/out you cannot accidentally swap TIn/TOut since that would cause invalid variance by the compiler. – kayess Dec 2 '16 at 9:47
  • @kayess I don't think OP is talking about interfaces he is writing, but existing interfaces that he uses, that have invariant(or not) type parameters – Caleth Dec 2 '16 at 9:49
6

That's the way function types are normally written:

a → b

is a function that takes an a and returns a b.

(a, b) → c

is a function that takes a tuple of (a, b) and returns a c.

a → b → c

is a function that takes an a and returns a function that takes a b and returns a c (which is the curried version of the second function above).

In all these cases, the inputs come first, then the output. And it makes sense: data flows from input to output, types flow from input to input to output, and we read from left to right.

It also matches with declarations for subroutine prototypes in many languages. E.g. Scala:

// method:
def add(a: Int, b: Int): Int

// function:
(a: Int, b: Int) ⇒ { //body of the function, return type is inferred }

Go:

func add(a int, b int) int

Haskell:

add :: (Int, Int) → Int
-- or more idiomatically:
add :: Int → Int → Int

And so on.

  • 2
    maybe make a note that C is the odd-one-out with its return_t name (param_t param...) syntax and the .NET CLR supports multiple languages with differing declaration orders – Caleth Dec 2 '16 at 9:45
  • It also feels related to the way the vast majority of things in computing with a source and a destination/target come in that order by default (screw microsoft symlink tool thing…); at least i see in/out as the same concept. – StarWeaver Dec 2 '16 at 13:03

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.