On Sat, 20 Jan 2001, Christian wrote:

> I am not interested in sugar -- I want substance. Perhaps the
> substance is the language as a whole, including the developmental
> process it encourages. But surely there is more. Or not. That's what
> I came here to find out. Arrogant? Sure. Stupid? No.

As you're a games programmer, you have the advantage of being in
control of all your code, and the disadvantage of needing the maximum
speed out of it. Ruby is designed for the opposite end of the spectrum,
trading speed for extra generality. As such, you may not get what you
want from Ruby.

Even if you were to use Ruby for prototyping, you would still need to
convert the prototype code to C++ to fit in with the rest of your code.
This is pretty much always going to be slower than prototying directly
in C++, so I think this may be why you're not seeing the benefits of
Ruby as a prototyping language.

I'm not really sold on the prototyping idea myself, so this isn't an
angle I'm going to push. I used to convert code from Fortran to Pascal
and it was a very painful exercise. I can see that converting some Ruby
constructs to C++ would be equally painful.

All I'll say is that I do find writing code in Ruby to be considerably
quicker and the option of rewriting the slow Ruby bits in C solves the
execution speed issue for me, but I have less stringent requirements.

A language provides an infrastructure that ties everything together.
Code communicates with each other through the infrastructure, whether
passing values on the stack, or through function calls. Types are one
form of infrastructure introduced to prevent ints being read as floats
and that sort of thing. OOP was introduced as a way of having private
namespaces and hidden implementation details. The two can be combined,
but they don't need each other.

The problem with infrastructure is that it makes some things very
easy at the expense of other things. If I choose to use a hammer as
my tool, I can do nails and contract killings, but I can't repair
broken watches. If I choose a screwdriver, I can do broken watches
and contract killings. Hmm, why did I become a programmer?

Combining different forms of infrastructure strengthens and limits
the code. Merging the hammer and screwdriver might give me a hammer
with a screwdriver head poking out the bottom of the handle. Works
well in most cases, but now I can't fit the screwdriver in tight
places because the handle is too thick, and I can't twist it very
well because the hammer head gets in the way. I'm also in danger
of stabbing myself with the screwdriver every time I hammer.

OOP with dynamic typing is more like writing template code. I no longer
have to make sure that Food and Drink are derived from the same base
class before I can issue the "consume" command. Objects can talk
directly to each other. This could result in little spiderwebs of
tightly coupled objects, so I'll have to apply the Law of Demeter a
little more rigorously, but the number of times I want to rearrange
objects is greater than the number of times static typing would have
saved me. Dynamic typing tells about my mistakes anyway.

When considering the question of compiler vs interpreter, I have to
ask... why did you write those interpreters? Why do you find ANTLR so
useful, rather than just using C++ everywhere? My guess would be that
an external call to the C++ compiler wasn't feasible, so you had to use
an embedded language. And while you're there, why not create a domain
specific language to handle stuff that would be difficult in C++?

(I'm guessing) Ruby was created for pretty much the same reasons. It's
designed to be easily embedded in existing C programs as a scripting
language extension. It can also be used as the infrastructure for a C
program for a higher level view. It can be the glue between C modules,
And, of course, it can be a stand alone interpreter.

Why didn't Matz write Ruby as a compiler from day one? My guess would
be that he was more interested in the language than in prematurely
optimising for performance. An interpreter is cross-platform by its
nature. An interpreter can be embedded in a C program. C extensions
can be embedded in the interpreter. These traits make Ruby more
immediately useful than a standalone compiler.

Another difference between a compiler and interpreter is the level of
hardware simulation. A compiler generates code that is expressed in
the language of the real CPU. An interpreter pretends to be a CPU
tailored for the language.

Ruby's interpreter understands higher level language constructs like
OOP, continuations, namespaces, and so on. To produce the equivalence
of this in real CPU code, a lot of wrapper code would have to be
inserted into the machine code. This wrapper code would do pretty
much the same stuff as the interpreter does anyway, hence compiled
versions of interpreted languages tend not to be much faster.

Just call me Analogy Boy, but I have a custom made guitar which I
designed so I could define exactly what I wanted. When I play it, I
have control over individual notes. My fingers directly touch the
vibrating strings, so I can do all sorts of things like bend notes,
pluck harmonics, tap with the other hand, add vibrato to a chord and
so on. On the downside, I can only play 6 notes at a time, of which
I can only modify 4 of them.

In comparision, a piano has very limited control. It takes skill to
play very quietly. It doesn't let you bend notes, it doesn't let you
tap harmonics, and it doesn't even allow you to keep notes going. On
the positive side, I can play 10 notes at a time, and modify all of
them within the span of my hands.

The piano manages this feat by presenting a simplified interface
more suitable for human use. Each note was given a fixed, standard,
behaviour. It uses equal temperament tuning to make it possible to
play in all keys and still retain an audience. Flexibility was
traded off for ease of use.

The consequence of this was that it became possible to play symphonies
on a single instrument. It didn't sound so good, but they sure did what
people wanted at the time. Lots more noise per bash.

Back to the real world. Ruby is the piano here. It's a predictable
language with standard behaviour. The programmer trades the ability
to control the code almost to the assembler level, but gains the
brainspace from not having to care anymore. That extra brain power
can be applied to program design or handling more functionalities
in the code.

As a practical example, programming problems are best addressed by
domain specific designs. The problem comes in when I want to include
libraries written by other programmers. In C, some libraries take care
of their own objects. Others I have to free. Some return structures.
Others take structure pointers and modify in-place.

In C++, it gets worse. Some use operator overloading. Others take
function calls. Some use homebrew functions, others use the STL.
I'm going to have to remember this stuff while coding. "This data
comes from libfrob.a so it needs to be passed to frobfree(), not
to delete(), or the program crashes".

I haven't found a GUI toolset I like yet, because all of them use OOP
and callbacks which force me to write my code differently. If someone
presses a button, I wanna ask when I wanna know, with a nice specific
"if button.pushed" where I care about it, not through some callback!

Using more libraries compounds this problem. Passing data from one
library to another involves lots and lots of mindless conversion
routines. Passing information is a headache - can I put that data in
this struct, or will it become a memory leak since this library frees
its own data and doesn't know about that library's objects...

Ruby lets me forget this and get on with solving the problem. I'm
willing to sacrifice other people's flexibility for a consistent model
that I can use in my own programs. I want external libraries to be
written with the same mindset as my code. I've got that now.

-- 
  steve / deaf.org