I was surprised to see that some developers would prefer to repeat code with different
names rather than using abstractions.
In other words they name types by the way they're used instead of what they are.
I always like to consider the extremes of always applying a rule or never applying it.
If we made every use its own type we'd have lots of conversion functions to write
to avoid code duplication (but of course those functions would be duplicates).
The other extreme would be only naming a type by what it is so you'd never have
NameAndAge and AddressAndYear, you'd only have StringAndInt (or Tuple[String, Int])
and the meaning would only be shown by the use.
Tony Morris generally seems to agree, but he would give names to types when the
typesystem cannot express important things about the type, such as associativity.
AssocF[Int] instead of F[Int, Int, Int]
I've seen people prefer names for types in all cases, so they would never want tuples or function types in their languages.
I think these programmers have a fear of abstraction. What do you think?
7 comments:
I point you to the first comment left here.
Functional languages are not used because programmers are not clever enough to appreciate them.
What they basically want is a C syntax language with C expressive power, it doesn't matter that it is named Java, and they are scared of anything abstract and powerful.
A lot of truth in few words.
I've thought about this issue a little more. Let's hypothesise for a moment that we have a rather extensive typing system.
A function is annoted with a variety of properties. For example:
one of: no side-effects whatsoever, only affects its own object, side-effects
documentation of how it works. Example: return negative to indicate B is 'above' A, positive if A is above B, and 0 if A is equal to B.
Various traits such as commutativity. Example: an identity equals method should certainly not care if it's a.equals(b) or b.equals(a).
Some of those can be considered part of the 'spec'; they are contractual obligations, and not implementation details. Commutativity is a clear example, and you can make a case that you ought to be able to enforce no side effects for certain things.
So, where do you 'annotate' these things? On each and every method that uses this thing? That seems like a bad solution.
In java, Comparators are used in many places. You can pass them to TreeSet or TreeMap's constructor, you can feed them to Collections.sort, to Arrays.sort, and more. It would be a violation of DRY if you were to repeat in documentation for each one of those that negative means B is above than A, and positive means A is above B. Copy/Pasting the following 4 times in a row into a bunch of different files seems like a really bad idea:
@SideEffectFree {T, T => int}
""" Compare two items to see which one is above the other. Return a positive number to indicate the first is above the second, negative for the reverse, and 0 to indicate the items are equal. """
That makes a pretty clear cut case that named types are a good thing to have. It doesn't make a case that it should be a requirement for everything.
A lot depends on the language.
No idea why my name disappeared from that last comment.
Another thing to note: It's not repeating types if you consider the name to be an intrinsic part of it. I also think you overestimate the overhead introduced by having ways to turn one type into another. You could even devise a system where such translations are completely automatic.
Naming types does reduce programming errors in many cases, errors that the compiler can't catch. Consider this example:
class Person {
string firstName;
string lastName;
}
This type can't really be replaced by Pair[string, string] as this would increase the probability of errors when using the type.
Creating record like types with named child elements is definitely valuable compared to using abstract types (like pair, tuple) where the order of child elements determines their meaning.
We(/I) don't want data structures masquerading as actual type-objects.
(I guess this makes me a java-programmer.)
It tosses type safety out the door; even if you try to hide it with generics. It's am array with extra sauce. Circumventing the idea that a method is suppose to return a single type object.
Sure in some cases where your working/operating on data structures themselves tuple's makes sense, but there your already working on that level of abstraction to begin with.
Simply saying 'abstraction' doesn't cut it imo. Named/'repeated' types are actually not that repeated at all they have a different contract. This seems like ducktyping-like construct. The pro for Named/'repeated' types is that the contract is in one place and one place alone: at the location where the type itself is defined('named') instead of being fragmentated around in method/function contracts. Which might conflict(yes there is a breach of the single point of definition here.).
see jeppe's reply above.
The common consensus seems to be that communication and maintenance aspects outweigh 'clever' code.
I don't get it. Why instead of increasing type safety should I decrease it?
That has nothing to do with programmers not being smart. On the contrary, a smart programmer would care to increase type safety.
See, for exaple, the Haskell solution at
http://blog.moertel.com/articles/2006/10/18/a-type-based-solution-to-the-strings-problem
P.S. The rule of good design is to model the domain space, not the technical side of things. Abstraction "BirthdayDate" might have a lot to do with the domain space, abstraction "Int" has nothing to do with it. At least that's what Grady Booch says in http://www.amazon.com/Object-Oriented-Analysis-Applications-Addison-Wesley-Technology/dp/020189551X/ - and I think that makes sense.
Post a Comment