There is something very intellectually satisfying about languages that derive all their power from just a handful of small, orthogonal concepts. The elegance of languages like Scheme and Forth is seductive, and it seems reasonable to assume that an elegant language leads to elegant solutions.
This doesn't play out as well in practice though. Most of the time when programming we're working at abstraction levels far, far above those established by the language kernel and it's the tractability of the language at those levels that matters. It's charming that you can implement your own object system in a few lines of Scheme but what's much more valuable in most cases is a single, standardized object system used consistently throughout a codebase and its dependencies. This is why we program today in Java and Python and not Forth.
Ironically, though, Java is often compiled to a stack-based virtual machine. "A number of instructions are provided for the direct manipulation of the operand stack: pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2, swap." (http://java.sun.com/docs/books/jvms/second_edition/html/Over...) CPython uses stack-based bytecode, too (http://docs.python.org/release/2.5.2/lib/bytecodes.html - POP_TOP, ROT_THREE, etc.), and Lua's C API also uses a stack interface (though its VM is register-based now).
Those stack manipulations still creep in, they're just not written by hand anymore.
It makes more sense to me to think of Forth as a kind of virtual machine. Writing its bytecode by hand seems like a bad trade-off, except in an extremely limited environment.
Hmm, so, in theory (and maybe in practice), instead of writing a new stack-based virtual machine, one could use Forth instead, and write a compiler (for a given language) that targets it. I wonder if this has actually been done?
VMs usually have a fixed set of instructions, because the compiler needs to know how to cleanly map the language semantics to them. It's just that most stack machine bytecode tends to overlap with a subset of Forth.
This doesn't play out as well in practice though. Most of the time when programming we're working at abstraction levels far, far above those established by the language kernel and it's the tractability of the language at those levels that matters. It's charming that you can implement your own object system in a few lines of Scheme but what's much more valuable in most cases is a single, standardized object system used consistently throughout a codebase and its dependencies. This is why we program today in Java and Python and not Forth.