3 ms·
Obj when constructed contains a reference to the Dog class. Assignment updates the reference to be the Cat class. None of this has relevance to the ability to
by ohyes 16y ago
Obj when constructed contains a reference to the Dog class.
Assignment updates the reference to be the Cat class.
None of this has relevance to the ability to compile to machine code. It does however, have implications for the efficiency of said code. (Object has to dereference the speak function for its class, and then call it, rather than being able to call the class's speak function directly, as you might in Java. It is also harder to eliminate type checks with such a scheme).
- haberman 16y ago> None of this has relevance to the ability to compile to machine code. It does however, have implications for the efficiency of said code. This is a tautology. You can always "compile" a script to machine code by just making all the generated code call back into the interpreter. The only point of compiling to machine code is if it can be more efficient than interpreting the code. > Object has to dereference the speak function for its class, and then call it That's exactly what the interpreter would have to do. So what are you gaining by ahead-of-time compiling to machine code, again?
- ohyes 16y agoA tautology is when I say something that asserts its own truth. I don't believe that to be a tautology. I don't think I said anything about calling back to the interpreter. I merely meant to imply that the efficiency might not be as good as that of a statically compiled language. There is a large implementation difference between an interpreter and a compiler. It goes far beyond 'efficiency.' In an interpreter, generally, you are interpreting a data-structure and dispatching based on the symbols in that data-structure to do various things. The advantage of the interpreter is that you don't have to compile (you just have to tokenize/parse). Every function call and value dereference requires interpretation of this parsed structure. This means that for every function call you have to do some sort of lookup, and your flow control constructs (like if) are done by manipulating your position in the data-structure. In a compiler, you are operating at a much lower level, having already interpreted the data-structure, and transformed what you would have interpreted as a data-structure into a machine code representation of itself. Instead of manipulating a position in a data-structure to interpret the code, you use the program counter (machine code jmp instructions). In certain situations, you can prove things about the code that is being compiled, leading to more efficient code (similar to what one might do with a static compiler). In the example above, the interpreter might look up the proper function by finding the correct class object in a hash table, and then finding the pointer to the function. In a compiler, it might dynamically recompile a dispatch function (when you add an object that implements that method), and then simply dispatch based on a case statement of 'object type' (something more sophisticated is likely possible as well). The second way of handling it would likely be much more efficient, even in the face of dynamically changing classes. If you allow type hints in your compiler, you can prove even more things about various pieces of your program, meaning that, for example, you might be able to do straight up integer math in machine code (add, imul, sub, ash, etc) rather than having to go through a 'generic math' dispatch. There is a lot of interesting 1980s-early 1990s literature on efficient implementation of dynamically compiled languages. There are number of smalltalk, scheme, and common lisp compilers that are good examples of this technique (and proof of my claims!) It is a fascinating subject and I highly recommend reading about it. (The Henry Baker archives might be a good start).
- haberman 16y ago> In a compiler, it might dynamically recompile a dispatch function You're talking about JIT compilation ("dynamically recompile"). Obviously JIT compilation can be a huge win for dynamic language -- no one disputes this, especially in the face of successes like V8 and LuaJIT. However the subject of this article is an ahead-of-time compiler for Python, and I specifically was disputing whether ahead-of-time compilation for a language like Python would buy you much. > There is a large implementation difference between an interpreter and a compiler. The issue is not as black and white as you make it out to be. Most dynamic language interpreters do not interpret a parse tree directly; they compile first to some kind of byte code (perhaps performing optimizations in the process, just like a compiler would). For example, here is source code from CPython that performs AST->bytecode transformations and optimizations. So is CPython a compiler or an interpreter? http://hg.python.org/cpython/file/80e71592b114/Python/compile.c http://hg.python.org/cpython/file/80e71592b114/Python/compil... http://hg.python.org/cpython/file/80e71592b114/Python/peephole.c http://hg.python.org/cpython/file/80e71592b114/Python/peepho... Machine code isn't that different than byte code in concept -- it just happens to be runnable by a physical machine rather than a virtual one.