Raw notes for thinking about programming language productivity...will update as i get more thoughts and time to flesh out.

narrowing it down

Boy, all this sures sounds like Stongtalk. almost.

Schrödinger types

Let's say you're building an interpreter and have an Instr super class and Jump, Add, etc... subclasses.  A List<Instr> lets you make a list of any kind of instruction.  For efficiency you might want a switch on opcode

void exec(List<Instr> code) {
I = code[ip++];
switch ( I.opcode ) {
  case Jump :
      ip = ((Jump)I).targetAddr; // need cast
      break;
  case Add :
      ...
}

Of course I could alter each Instr class with an exec() method but let's say I didn't want to alter source to define exec().  When I'm at a particular case, I know for sure as the programmer what the type is.  To notify the compiler's type system I have to have a cast.  How can we get rid of this?  We can't allow arbitrary downcasting but perhaps we could define a type that was the set of valid instructions:

type Instrs = Add | Jump | ... ;

Then we could have List<Instrs> and then any element such as code[i] could be treated like any of the instructions.  It's somewhere between strict static typing and no static typing (dynamic typing).  Not ask flexible as pure subclassing relationship because we have to update type Instrs when we add new instructions subclasses.

Hmm...useful in other ways but perhaps here we should simply allow downcasting. Java inserts a checkast instruction anyway when you say:

public boolean equals(Object o) {
	T t = (T)o;
...
}

You get

public boolean equals(java.lang.Object);
  Code:
   0:	aload_1
   1:	checkcast	#2; //class T
   4:	astore_2
...

That might make refactoring less useful however.  We couldn't guarantee that we'd replaced all methods names for example.  As we refactored, the code could become more and more improperly typed, leading to lots of runtime errors.

Scala has compound types, which are they have additive types. So, you can say an arg to a function is both cloneable and serializble.

So combining AND and OR, I could define types like

type T = (ST | DebugST) & Cloneable;

What's the hook?

Why would anyone use?

State and side effects

In order to reuse some functionality, we have to be able to call the function or functions without side effects; well, at least without those functions altering something we don't want them to. For side effects, the less likely you will be able to reuse the functionality. I've been thinking about this as I build a compiler for StringTemplate v4. what I really want to say something like this:

Compiler c = new Compiler(...);
String template = ...;
CompiledST code = c.compile(template);

And, I don't want it to be messing with a global symbol table or anything like that. I just wanted to come back with the compiled template with all the bytecodes the necessary information to execute. One of the reasons for this is that there are nested templates and I want to launch a subcompilation on a subtemplate. I can't call c.compile(subtemplate) because all of the temporary data necessary to compile the template since in the Compiler object. So, do I create a new compiler for each template? seems weird. I think I should have one compiler, but with the ability to recursively compile things.

Ok, no problem. separating out the temporary data into some state:

state Compilation {
    byte[] bytecodes;
    StringTable strings;
    int ip;

    CompiledST compile(String source) {
        ...updates bytecodes, strings, ...
    }
    void emit(int opcode, ...) {...}
    void writeShort(int addr, short value) {
        bytecodes[addr] = ...;
    }
}

But now, this makes no sense from an object-oriented point of view, even if we are using objects to express it. do I really want to ask some temporary data to do some compilation? that does not seem like very good abstraction. we are co-opting objects to represent temporary state.

Another problem is that emit, writeShort, and so on are really just functions. They might be scoped within the compiler object but they are really just functions. They are not modifying the state of the compiler object because we've moved the temporary data outside of the Compiler.

What we really need to do is pass the state object around the functions:

Compiler c = new Compiler(...);
String template = ...;
CompilationState state = new CompilationState();
CompiledST code = Compiler.compile(state, template);

where compile() is now a static method of Compiler:

    static CompiledST compile(CompilationState state, String source) {
    static void emit(CompilationState state, int opcode, ...) {...}
    static void writeShort(CompilationState state, int addr, short value) {
        state.bytecodes[addr] = ...;
    }

Of course this is pretty ugly and inconvenient; it's like having to pass around the self for this pointer to simulated methods in a non-object-oriented language.

We could create a Compilation object and then call comp.emit(opcode) but then we're asking a temp data object to answer messages. What we want is emit(opcode) where the comp is implied like a this. Maybe an include that works on an instance not a class:

use c = new Compilation(...); // imports funcs with implied Compilation arg not "this"
emit(opcode);

'course we also need c to be a stack for recursive compilation of nested funcs or whatever. I'm simulating now with

public void emit1(CommonTree opAST, short opcode, int arg) {
	$template::state.emit1(opAST, opcode, arg);
}

So, instead of $template::state.emit1(...), I called just emit1(...). extra work on my part to set up, but use is less offensive.

Scala

Scala is ML+Java sort of. Integrated very nicely. What I like:

Negatives? Maybe:

Mainly I'm trying to figure out why a statically typed Smalltalk couldn't do this with much simpler syntax. Obviously, Scala is much more about immutable elements and has the mailbox/actor thread model etc...  Even the cool pattern matching can be done in smalltalk yielding reasonable syntax. Well, it couldn't do a lot of it w/o extra syntax. Smalltalk could do mixins via metaclass protocol (which I don't like).

Cause of nonreuse: