Hacker News new | past | comments | ask | show | jobs | submit login

I like the convention in C# where you prepend generic type names with a T, clearly signifying that it's a generic while being more descriptive. E.g.:

public delegate TResult Func<in T,out TResult>(T arg);




On the contrary as someone who moves somewhat comfortably between Java, C# and TypeScript projects the tendency to use especially I in front of interfaces but also other letters in front of other things are an explicit pain point.

(Java has others like just like how every time you open a Gradle project you can expect someone has invented yet another unique way of structuring build files for this repo. :)


Even without its basis in the history of COM, the I-prefix for interfaces types in .NET makes sense because interfaces-types are very structurally and semantically different to "a type's interface" - so having some way to quickly distinguish them at-a-glance is necessary.

I started off in .NET and when I was using Java it wasn't immediately obvious what types were interfaces or not (e.g. "List" is an interface in Java, and also especially when the interface suggests what the implementation is).

C# can be described as a considerably better Java - but I wish they didn't inherit Java's interfaces vs. classes model and instead had something that enables some form of structural-typing: something like TypeScript's interfaces or Swift's protocols.


I too enjoy C# a lot and find Typescript close to perfect (obviously it is constrained by its relation to Javascript past, present and future).

That said, a bit more about why the I feel the I convention is unneeded and even a problem:

- If one doesn't use a powerful IDE when using either C#, Java or even Typescript there is a fair chance one is doing something wrong. (I allow for exceptions for people who are so insanely smart that they need the lack of support as a brake for their brains ;-) Powerful IDEs can tell you just fine what is a class and what is an interface.

- In what I think of as well written Java projects (and C# many projects for that matter) you'll often feel what is an interface and what is a list just by the name anyway: Javas List that you mention is a perfect example: List is the general concept of a List in Java. ArrayList, LinkedList etc are implementations of that. Once you get used to this convention it is hard to even recognize it until you sit down and try to explain it to someone.)

- Contrary, prefixing with I (typical .Net style) or suffixing with Interface (something I've seen in older Java projects) these days[1] encourages lazy naming and unnecessary duplication: If there is only one interface, just write a class and let the interface be implicit. Especially in Java, but also in C# and Typescript refactoring is so trivial (unless you are changing an external API that other people already depend on) that you can just create an interface with the same name and rename the class later if you need it; there's no reason to worry about it up front.

[1]: "these days" added as a qualifier since it used to be that certain older systems required you to write one or more interfaces for each class.


> Powerful IDEs can tell you just fine what is a class and what is an interface.

Yes, but if one is simply reading code from a book or code sample online no such IDE benefit exists. Program code should be comprehensible as plaintext. This is why I'm not a fan of overuse of the `var` keyword in C# - or `auto` in C++. (Many people feel forced to use `var` and `auto` in C# and C++ respectively when their program uses horribly complicated generics or template instantiations - this wouldn't be necessary if C# permitted more flexibility with type-aliases.

> List is the general concept of a List in Java

But that's a huge design-issue in itself: Interfaces should not describe what something "is": they should describe what something is capable of - this is doubly essential when using reified generics because of type parameter variance (Java skirts this one because of type-erased generics - which is another discussion). A distinct advantage of interfaces describing capabilities is the "I" prefix can be replaced with a rule requiring interface names to be adjectives (i.e. class/struct = noun, method = verb, interface = adjective+noun).

Rather than having this (invariant) interface:

  interface List<T> {
    void add( T item );
    void remove( T item );
    int length { get; }
    T get( int index );
  }
  
We'd have these variant interfaces:

  interface ReadableList<out T> {
    int length { get; }
    T get( int index )
  }

  interface MutableBag<in T> {
    void add( T item )
    void remove( T item )
  }
Which is great because then we can do type-safe stuff like this:

  class Vector<T> implements ReadableList<T>, MutableBag<T> {
     // etc
  }

  class Employee extends Person {}
  class Boss extends Employee {}

  Vector<Employee> list = new Vector<Employee>();
  ReadableList<Person> people = list;
  MutableBag<Boss> appendOnlyBossesFromHere = list;


Is `IThing` substantially worse than `ThingImpl`? ;-)


Both are bad:

If you have Thing as the Interface and ThingImpl as the class you should probably delete the interface and rename ThingImpl to just Thing.

If you really really know multiple implementations are coming down the road you can name the class DescriptionClass (like ArrayList and LinkedList), but generally these days I'll just cross that bridge when I get there. (Always be careful when working on external interfaces though.)

Historical note: In ancient (in a web perspective) Java enterprise edition (not to be confused with modern Java EE) this style was encouraged and even enforced. Today it is widely considered an antipattern. Last time I remember a project where this was how it was supposed to be done was probably back in 2008 in a Spring/JSF project. Both Spring and JSF have changed a lot since then.


But you know that there will be multiple implementations for every interface. At least, after IThing and Thing you will have MockThing


First:

Forget I<Whatever>. It breaks sorting and it is allows people to avoid thinking if the interface is necessary or not.

Second:

I'm not one of the hardliners wrt avoiding mocking, but if every class needs to have a corresponding mock then I'd suggest taking a step back because that shouldn't be necessary, and often in such cases I think one will also find lots of low value or even no-value tests.

I'll not judge you based on a comment on an internet forum, but I think the above is a good heuristic.

(With TypeScript and Java closures or anonymous types is also an option and something exist in C# as well.)


> Forget I<Whatever>. It breaks sorting and it is allows people to avoid thinking if the interface is necessary or not.

100%. Any "structure definition" should be assumed to be an interface, and any explicit separation of definition and implmentation should be deferred until multiple variants are required. My Java projects and files were corked up with Ifiles that only ever had a single implementation.

Caveat: I've only worked on internal projects, explicit interfaces are much more useful for something widely shared. Don't use the I<Class> convention though.


ThingImpl is better for searching and has the advantage that Thing/ThingImpl will be next to each other in an alphabetical list of your classes. It also means using the interface is more natural than using the implementation, which is usually what you want.


Another common convention is to select a suitable letter: for types with no particular meaning, go T, U, V, &c.; for types with meaning, see if there’s a better letter, e.g. Map<K, V> for a key-value map.

Rust convention is to mostly use this style of single-letter generics, but not to shy away from using longer names like regular type where useful (but not to prefix them like TFoo).


It seems most coders agree that variables, functions, files, columns, classes, etc. should have unambiguous, explicit, names. Writing `m[k] = v;` would not pass many code reviews. I don't see what makes generic types any different.


Context matters too though — As a general rule "i" is a terrible name for a variable but in a simple unnested loop it's expected.

All these shorthands (e.g. K,V = Key,Value) have to be learned so should be used sparingly but in some places I think having shorthands for things like generics & indices allow boilerplate code to "get out of the way" of the business logic.

You expend less mental energy trying to keep track of "i" (as you know it's a simple loop counter) than "index". And reducing cognitive load is the point of having descriptive names in the first place.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: