4 ms·
> It is complicated, not easy to understand for other people and can require intense maintenance I think this code sample illustrates the exact opposite. Look
by peterkelly 5y ago
> It is complicated, not easy to understand for other people and can require intense maintenance
I think this code sample illustrates the exact opposite. Look how concise it is - you really don't need much code at all to implement a simple interpreter.
- alisonkisk 5y agoBut this interpreter does almost nothing. It draws mazes and prints text, and it only works in 80-char terminals. A hosted DSL in a modern high level language gives you the full power of a modern high level language to build the simple constrained language of your choosing, and it can be highly portable to different environments, including keyboard vs network play, and different UI environments.
- megameter 5y agoThe point is to not have abstraction power. The goal is to make the DSL be a direct expression of the intended thought, which usually means cutting it down to the point where it's reduced almost everything to preset categories. At that point, you don't need much of a language to build it in, so it ends up porting well to all of them.
- valenterry 5y agoJust look at the article. They define "SubProg%" precisely to have more abstraction power. At some point someone will want to have loops, conditions, recursion, ... where does it end? Because of that, it's much nicer to use the power of the host language directly, so you don't have re-invent all these things everytime - but with free monads you keep the property of having a well defined, business specific list of actions or instructions that can be interpreted in different ways.
- vidarh 5y agoWhat you're describing is mostly an implementation detail to me. The point of this example is not that you can't do it in a higher level language. The point is to illustrate the power of being able to create a list of code as data. Everything else is implementation detail.
- valenterry 5y agoWell, I totally agree with you! My whole point was to offer an alternative that makes this implementation easier and offers some nice extras (while sacrificing free choice of syntax). :)
- vidarh 5y ago> But this interpreter does almost nothing. It draws mazes and prints text, and it only works in 80-char terminals. That is part of the point, though: An interpreter does not need to be complex. Adding "instructions" that does more complicated things does not need to complicate the interpreter much. Nor does the implementation language make much difference here - fundamentally the interpreter is only the part up to the definition of SubProg. Everything else is just implementation of specific instructions. Don't get hung up on the use of assembler here - unless you need blazing speed from the interpreter itself, it's trivial to do in pretty much any language. Conceptually, the interpreter is basically just a table of instructions, a bytecode buffer, and a loop that keeps fetching the next instruction, looks it up in the table, and calls it. The "SubProg" instruction could reasonable be considered part of the generic aspect of the interpreter, but the rest are the domain specific bits, no different You can abstract out the instructions easily enough. E.g. here's a trivially dumb interpreter written in Ruby where the instructions are separate from the interpreter, that works conceptually not much differently to the linked assembler: class Interpreter attr_accessor :pc def initialize program, instructions @program = program @instructions = instructions @pc = 0 end def getnext n = @program[@pc] @pc+=1 return n end def run while i = getnext @instructions[i].call(self) end end end # We could make this an array and insist on numeric instructions, # but that's an implementation detail - the interpreter doesn't even need to know # if you pass it a hash or an array. instructions = { :myarg => ->(interp) { puts "My argument is #{interp.getnext}" }, :helloworld => ->(interp) { puts "Hello World" } } program = [:myarg, 42, :helloworld, :myarg, 3] Interpreter.new(program, instructions).run Because the program counter is settable, "SubProg" can just as easily be implemented externally to the interpreter, btw.. Adding variables would really just be a matter of exposing another array or hash in the interpreter so that you could add instructions to read/write them.