C++ sucks for games

G

Gerry Quinn

Matthew Danish wrote:
One point that should be emphasized: Common Lisp's standard object
system is the default (rather than the only) object system available.
Lispers seem to have reached consensus that it represents the best
design for general use. Since the object system is just more Lisp code,
people who desire C++ style semantics, or indeed any other style they
can dream up, can write or acquire an object system that suits them.

Contrast this with C++, where the object system is factory installed and
not user serviceable.

The trouble is, Lisp-ers don't always seem to understand why the above
is far from being an un-alloyed good.

- Gerry Quinn
 
G

Gerry Quinn

Can you be a bit more specific? For example, do you make
extensive use of the protected specifier? Or do you stick primarily
to the public/private specifiers?

I like to have as much 'protected' as practicable, while 'private' is
essentially unused. Of course that is suited to a one-man operation,
for team-work I would not so blithely share stuff with derived classes.
When you've written both base and derived class, 'protected' is enough
IMO.

Actually my latest project uses a lot of polymorphism and even (shock)
makes use of multiple inheritance, which I used to declare essentially
useless! So maybe with more extensive class hierarchies, I will find
myself starting to declare members as 'private'. Contrary to popular
belief, I do evolve my methodologies, albeit reluctantly ;-)

- Gerry Quinn
 
G

Gerry Quinn

The C++ style of protection is completely at odds with the very design
of the object system, because there is no notion of class scope. The
body of a method in Lisp is not in some special scope in which the
symbols denoting the variables of a class are magically bound to those
slots. The ordinary scoping rule applies.

You've lost me, I'm afraid...

- Gerry Quinn
 
G

Gerry Quinn

ROFL. CLOS fits seamlessly with non-OOP Lisp. There is a paradigm
difference between CLOS and non-OOP Lisp code, but the language is the
same, and it's quite possible to mix the two. In comparison, only in
recent years has C++ begun to even approach the level of integration
between OOP and non-OOP aspects that CLOS has, but there are still
areas where they just don't mix.

You just don't get it, do you? Object-orientation is about program
architecture as well as language features. It's not about mixing and
matching a multiplicity of 'paradigms'.

Somebody else proposed that everybody design their own object system.

OO as 'language feature' is cargo-cult OO. The bamboo airport looks
just like a real one, but the planes aren't going to land.

- Gerry Quinn
 
G

Gerry Quinn

Gerry, if you're actually interested in exploring what Lisp has to
offer, you might be interested in taking a look at the draft chapters
of my soon-to-be-published book on Common Lisp, available at:

<http://www.gigamonkeys.com/book/>

It looks like a good primer. One comment I would make, though, is that
at a glance everything seems to be a parser of some sort. A touch of
hammer and nail syndrome?

- Gerry Quinn
 
G

Gerry Quinn

Well, many of the boasts about Lisp seem to be about its ability to
create 'new languages' for a given problem domain. You mutter about
people not seeing the benefits, but you don't seem so keen to
elucidate them.

Do you know what "functional programming" means? Some say it's
programming without side-effects, but really it's about writing your
programs in terms of functional abstraction. Or, in other words, in
terms of the function-call protocol your language provides in the form
of syntax and mechanism. Some people think this concept is the panacea
that should be applied universally to solve the software crisis. But
let's assume it's not. Still, the benefit of the clear, manifest,
predictable relationship between the program text and the result of
the program's execution is unquestionable (even if the hypothtetical
BASIC-programmer preferred his global variables). Bugs tend to creep
in when programs have side-effects (which might be called ad-hoc
protocols, contrasted to the language's function-call protocol) that
have consequences for the program that are difficult to spot by
looking at the program text. It is one of the very great benefits of
syntactic abstraction to be able to massage program text such that one
gets the best of both worlds: the clear, manifest relationship between
program text and program execution results, and also
application-specific, non-function-call protocols/side-effects
(i.e. one is not restricted to "functional programming"). (And this is
not to say that there isn't many cases of programming with
side-effects where the consequences are clear and simple without the
use of macros.)

[*] By linguistic abstraction I mean syntactic abstraction carried out
to such a degree that one cannot anymore be said to program in the
original language. Normally, syntactic abstraction integrates
naturally with the original language.

Let me point out something. If you do twenty projects in this fashion
you will have twenty new languages. Surely that vitiates any benefit of
a particular language, even if it is marginally better suited to the
problem domain of a given project?

- Gerry Quinn
 
P

Petter Gustad

Gerry Quinn said:
I disagree - it is in fact rare for code to be changed per se. What
would be the point in writing code whose only purpose is to be written
over? Whether original code is erased or not is completely irrelevant
to the issue of whether code is self-modifying. The *program* is still
self-modifying.

You are describing a program-generating program as self-modifying.
Using your discription every compiler which can execute its generated
code would be self-modifying (like a Common Lisp compiler). I think
many people (including myself) will disagree with you, e.g. see:

http://en.wikipedia.org/wiki/Self_modifying_code

I have only heard the term self-modifying in the context where the
program will modify the some of its following instructions. The
purpose is to implement adaptive algorithms and to save program
memory. Self modifying code has been known to cause problems with the
CPU instruction cache. However, self-modifying programs are not so
common since memory is cheap and almost all CPU's have instruction
caches where one would like to keep a high hit-ration as possible.

It's quite easy to write programs to generate and compile programs on
the fly in Common Lisp. I once wrote a microcode assembler generator.
It's a program which genereates a microcode assembler on the fly based
upon descirptions of instructions, bit-fields and mnemonics described
in Verilog (hardware description language). It will then run the
generated assembler and generate binary code for the microcode
sequencer. This is a program-generating program. It does not modify
itself. It does not overlay itself with the generated code, but it
will generate code on the fly which is executed in the assembler
stage.

Petter
 
P

Peter Lewerin

Gerry Quinn said:
What do you mean? Rabbit::Jump() seems a perfectly applicable example.

Ordinary function call:
C++: jump(rabbit, 3)
Lisp: (jump rabbit 3)

Method call:
C++: rabbit.jump(3)
Lisp: (jump rabbit 3)

Class method call:
C++: Rabbit::jump(rabbit, 3)
Lisp: (jump rabbit 3)

Well, *one* language has syntax that remains consistent throughout.
 
M

Maahes

Suppose that an alien encounters a message from Earth which looks like
A + B * C / D - E * F

The alien might be able to deduce what the operators are, and then it
is stuck.

Now the same alien receives:

(- (+ A (/ (* B C) D)) (* E F))

Clipped all the other funny stuff.

BTW, another fine example of why C is easier to read than Lisp.
Of course, if you wanted to write clean code in a team environment, you
would be expected to use:

A + (B * C / D) - (E * F)

so that there is no misunderstanding of your code. Certainly not needed, but
makes it easier to read at a glance, which is what I would say is the
highest priority when designing a language to be used in a team
environment..

Gee, I just love this thread... Will it ever end. I can see this stuff going
round in circles for weeks :)
 
M

Maahes

You misunderstand. Lispniks still edit text as so many characters. We
just have /at our disposal/ when we choose to call on them sundry mad
useful tools for editing blocks of code in one go.

QUOTE:
Yes, I am constantly irritated that I don't get a single conceptual
block (such as an entire for loop or a single addend in a summation)
with a single keystroke when I'm at work coding Java.

That sounds like the editor is inserting stuff to me.
 
G

Gareth McCaughan

Gerry Quinn said:
That seems clear. May it be assumed it does not claim to possess the
advantages of the C++/Simula model of OO?

Whatever advantages the "C++/Simula model" has over the CLOS
model, obviously (and trivially) CLOS doesn't possess. I assume
you're intending to ask a less uninteresting question, but I'm
not sure quite what it is :). Why don't you mention a few
specific advantages, and then those of us who are familiar
with CLOS can comment on them?
 
F

Frode Vatvedt Fjeld

Gerry Quinn said:
Let me point out something. If you do twenty projects in this
fashion you will have twenty new languages.

No, you won't. You don't use linguistic abstraction (a "new language")
unless there is a very good reason to do so. Using syntactic
abstraction (i.e. a small set of operators with special evaluation
rules integrated in the standard language) should be used with caution
in order to avoid the babylonic effect you mention---much as one must
be careful with the functional abstractions one introduces in a
program. That is, functional abstraction is "cheaper" in the sense
that the evaluation rules in this case is fixed and thus in some sense
more predictable/readable. But do recall what I said: use syntactic
abstraction when it is clear that the program semantics (side-effects
and associated rules of use--i.e. protocol) in question are unwieldy,
and a macro can capture this well.
Surely that vitiates any benefit of a particular language, even if
it is marginally better suited to the problem domain of a given
project?

You can make the observation that it is possible to write highly
obfuscated code using macros. But that's a very poor argument, I'm
sure you would agree. Using macros properly has the opposite effect as
what you're hinting at. And don't be blind to the concept that each of
the twenty projects by necessity will have a multitude of more-or-less
informal rules, idioms, etc. that cannot be captured in the
programming language, but which any programmer that is to work on that
code will have to internalize one way or another. Macros can be
understood as a mechanism that allows the programming language to
capture more such aspects of a project.
 
S

Svein Ove Aas

Gerry said:
You've lost me, I'm afraid...
C++ method call: foo.munge(bar);
Equivalent Lisp call: (munge foo bar)

The C++ function specializes on the first argument, that is to say "foo".
The language doesn't support multi-methods, so if you want to specialize on
bar as well, you'll have to do it by hande.

By the same token, however, foo doesn't have any special status in the munge
generic function. Attempting to add protection would be fairly nonsensical,
since functions aren't part of classes at all; they're in an entirely
separate namespace.

The code in munge is in munge's lexical scope, but it is *not* in foo's
lexical scope; the concept doesn't even exist.
 
S

Svein Ove Aas

Gerry said:
I'll take that as a 'yes'.
Can you list a few of those advantages, then?

I'm sure CLOS can do most of the same things, but it would be nice to know
what it lacks. Afterwards, we can do a similar comparison the other way.
 
G

Gareth McCaughan

Kenneth said:
Jeez, your eyes did not shriek aloud at that semi-colon at the end of a
line beginning "if ..."?!

Yes, they did. And at the "(1 ..." in the Lisp code.
I'm sorry if you think taking a couple of seconds to
spot a typo at 1.30am local time is proof of incompetence.
 
M

Maahes

Ordinary function call:
C++: jump(rabbit, 3)
Lisp: (jump rabbit 3)

C: rabbit_jump(rabbit, 3)

Method call:
C++: rabbit.jump(3)
Lisp: (jump rabbit 3)

C: rabbit_jump(rabbit, 3)
Class method call:
C++: Rabbit::jump(rabbit, 3)
Lisp: (jump rabbit 3)

C++: Rabbit::jump(3)
C: rabbit_jump(3)

I don't know lisp, but it doesn't look right. A class call would have no
concept of a rabbit object as there is no data related to the class call. So
what does the "rabbit" do in your line.
Its effectively just a namespace so you can have different Jump functions
without them clashing (as well as some other properties in C++ like being
able to access private sections of any Rabbit object it has access to).
Well, *one* language has syntax that remains consistent throughout.
*two* languages.
 
M

Maahes

So programs = algorithms + bookkeeping. Outsource as much of the
bookkeeping as you can, I say!

Oh god, its almost reads like your saying Lisp requires less commenting :)

I can't possibly mean this, so I'm sure I've misinferred here..
If anything, Lisp looks more like assembly, where you would expect a lot of
commenting on every single line...
 
M

Maahes

What is it that you use access specifiers for?

For protecting your implementation from the other programmers.
If you don't protect your variables and strictly define your interfaces, and
limit their power, you'll find the other programmers will completely
misusing your code. Then when you go to rewrite it to provide new features,
you'll find you can't because all the variables you want to trash are being
used in critical parts of multiple projects.

I'd really like to know what team projects the Lisp posters have worked on,
and what team games they have completed this way? Some of these things are
only learnt from doing things in a team environment on a project with a
dozen people in a couple of projects all accessing your engine interfaces
without direct guidance from you.

If any Lisp programmers have worked on games in Lisp, please let me know who
you are so I know which posters are just being theoretical and which are
speaking from experience. This thread is getting to big to be reading
everyones articles when, I suspect, most of the Lisp programmers have only
ever experienced solo lisp programs, or never taken a group Lisp project to
completion...
 
P

Pascal Bourguignon

Maahes said:
A + (B * C / D) - (E * F)

so that there is no misunderstanding of your code. Certainly not needed, but
makes it easier to read at a glance, which is what I would say is the
highest priority when designing a language to be used in a team
environment..

Not sufficient:

1 + (2 * 2 / 4) - (1 * 1)

Do you expect: 1 or 0 ?
<source language=c>
1 + (2 * (2 / 4)) - (1 * 1), /* ==> 0 */
1 + ((2 * 2) / 4) - (1 * 1) /* ==> 1 */
 
P

Pascal Costanza

Maahes said:
Clipped all the other funny stuff.

BTW, another fine example of why C is easier to read than Lisp.
Of course, if you wanted to write clean code in a team environment, you
would be expected to use:

A + (B * C / D) - (E * F)

so that there is no misunderstanding of your code. Certainly not needed, but
makes it easier to read at a glance, which is what I would say is the
highest priority when designing a language to be used in a team
environment..

This might be easier to read for people who don't have much experience
reading Lisp code, but it's technically inferior. What I like about Lisp
syntax is its regularity, so:

(+ 5 6) => 11 - that's obvious
(+ 5 6 7) => 18 - that's nice
(+ 5 6 7 8) => 26 - just what you expect
(apply #'+ some-list-of-numbers) - if you don't know the number of
summands upfront

[apply takes a function and applies it to a list of arguments. So (apply
#'+ '(1 2 3)) is the same as (+ 1 2 3). #'+ is the function associated
with the name +.]

(+ 5) => 5 - even works for one argument
(+) => 0 - and for zero arguments

0 is the neutral element for addition. 1 is the neutral element for
multiplication, so:

(*) => 1

Especially the last three cases seem strange - why would one want to
write this? But in (apply #'+ some-list), some-list may be an empty
list, so you definitely want the neutral element as a result in that case.

This kind of regularity makes the Lisp syntax very convenient for
writing code generators (like macros, but also in other situations)
because you don't have to deal with too many special cases. Just emit an
operator like + and *, and then as many arguments as you need, including
none. Code generators are very valuable for tackling large projects.
That's true for almost all languages, as far as I can see. Furthermore,
it's good to be able to read such regular syntax because it helps in
reading and debugging generated code, especially because it really
doesn't take more practice than one or two weeks of actual coding in Lisp.

(Lisp syntax shares the advantage of regularity with, say, XML. One of
the problems with XML, though, is that XML eats up a lot of vertical
space on the screen because of the required end tags which makes XML
syntax relatively inefficient for coding since seeing more code at once
helps to better understand it. With Lisp syntax, all the closing
parentheses for a top-level expression can usually be lined up after the
expression's last token, which is extremely efficient and very
straightforward to handle when you have a good text editor that supports
Lisp syntax well.)


Pascal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,204
Messages
2,571,063
Members
47,670
Latest member
micheljon

Latest Threads

Top