Let's pick a programming language, say C++. Out of the box, it's vocabulary consists of words like "class", "public", "double", and so on. Now you start to program in it. You start adding classes, methods, variables, etc. Now what is the vocabulary? It's the original one plus all the new words you defined. So to those who say "Who wants to climb the learning curve of a new language?" I say whenever you program you're defining a new language, like it or not, and creating a learning curve for others. The reason you climb or create the learning curve is to invest some up-front effort in order to save effort later. So I think it's important to understand how to get the biggest ratio of payback to up-front investment in the language that you're creating.
Here's my view of what a computer language should do. There is something you want the computer to do, so there is a model or set of requirements in your head. (In A.I. we have no problem talking about information that's in your head.) It is stated in terms (a language) that is, if you really know what you want, very good at describing what you want. You know it's a good language to represent your ideas, because you can easily change your mind without having it crash on you (i.e. be seriously invalid). You can see an approximation to this language by simply stating in natural language, to someone who understands the subject matter, what you want. If you are disciplined enough to write it out in a functional requirements document, that should do a fairly good job of capturing it. But you should not regard any such model or document as final. It will evolve, over time, like it or not.
Now suppose you had an autonomous program-writing machine that had a basic understanding of the subject matter (a smart, experienced human would do). You could convey the model to the program-writer and after some time have a working software. What this tells you is that your model contained all the information necessary, so it was the least redundant expression of what you wanted, and it was also an implementation language because you handed it to an autonomous program-writing machine (i.e. a code generator) and got back a working software that met your needs.
So, the ideal state we want to be in is to have a modeling language that matches as closely as possible the subject matter in our heads, so we can say what we want most directly. The test of that directness is when we change our mind about what we want, that change is easily expressed in the language (i.e. the language has low redundancy). Then we want that language to also be an implementation language.
This is not a new idea, of course. This is what domain-specific-languages (DSL) are supposed to do. This is what declarative languages are supposed to do. This is what universal modeling languages (UML) are supposed to do, but I suspect that is an oxymoron, because the very nature of a good modeling language is to be domain-specific, not universal. In any case, the redundancy concept gives a good measure of how specific a language is to any given domain.
Few of these existing domain-specific-languages are implementation languages in the sense that they can be handed to an implementation generator (program or human) and have working software come out the other end, without providing more information.
I would like to show a few techniques I've come up with for reducing the redundancy of source code, thus making the source code more like a DSL. Basically, it involves code generation. One way is to design a language, write a parser for it, and have it generate code in a language you already have, like C or C++. That's a simple form of automatic programmer. An even simpler one has been around forever, the macro preprocessor. I call it the poor man's automatic programmer. (That they ever decided it was a bad thing in Java and C# tells me how myopic this field can be.)
List macros
Suppose you have a list of things, and for each of them you may need to write some more or less predictable code. For example, a list of variables. Define a macro like this:
#define MYLIST \
DEFVAR(A, int, 6) \
DEFVAR(B, double, 7.0) \
DEFARR(C, double, 10) \
Now you can use this macro to generate various codes:
#define DEFVAR(nm, typ, init) typ nm = init;
#define DEFARR(nm, typ, siz) typ nm[siz];
MYLIST
#undef DEFVAR
#undef DEFARR
Generates
int A = 6;
double B = 7.0;
double C[10];
If you want to print all these variables, you could write a routine:
void PrintTheVars(){int i;
#define DEFVAR(nm, typ, init) \
printf("%s = %g\n", #nm, nm);
#define DEFARR(nm, typ, siz) \
for(i=0;i<siz;i++) \
printf("%s[%d] = %g\n", #nm, i, nm[i]);
MYLIST
#undef DEFVAR
#undef DEFARR
}
and it generates a routine to print the variables. There are other kinds of code you could also have it generate. Now the redundancy reduction comes because, if you want to add a variable to the list, it is a 1-line text edit, and all the rest of the code is generated automatically and correctly.
Many times I've seen things that would otherwise result in terribly repetitious code, that were very much simplified with list macros.
Please notice, this doesn't involve objects, at least not explicitly. Something is explicit if you the programmer have to think and write code about it. If it is naturally part of the model then it's a good thing. If not, it's a burden. On the other hand, if there are objects underneath, where you don't have to explicitly write code about or think about them, that's OK. The point is that anything that enters the source code, but is not part of the requirements, is a burden. If for example, "customers" are part of your requirements and you have to retain information about them, then it's natural to write code about customer objects. On the other hand, if you're writing a desk calculator program, and you want to parse numbers and operators, there's no particular need to be defining lots of classes.
Flyweight processes
Differential execution