C++ sucks for games

S

Sashank Varma

The reason to have a difference syntax is to better model how humans think in
certain situations.

[Let me return to this below.]
It is a mental organization thing.

Essentially, class scopes can indeed be useful.

Perhaps. I guess I believe you. Okay, class scope can sometimes
be useful.
Mind you, Lisp is consistent here too, calling a field accessor function/macro
on the data.

There is no question that Lisp's approach is more general, especially given
generic methods.

But what you (seem to) give up when you accept class scope is this
beautiful generality.

You claimed above that class scope models how humans think in certain
situations. Frankly, as a cognitive psychologist, I'm finding it hard
to think of data that support (or refute) your claim.

My intuition (and it is a just an intuition, not an established fact
about human cognition) is that taking a consistent, parsimonious
approach to function/method calls brings an increased economy to the
comprehension of Lisp code. Programming languages are formalisms, and
those who uses formalisms (e.g., mathematicians) have historically
worked awfully hard to eliminate special cases and unnecessary
redundancies.
 
S

Sashank Varma

Gerry Quinn said:
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?

On one hand, I agree with you. But all significant programs
develop their own "vocabulary," one that matches the domain
at hand. The hard part about understanding a large program
is rarely mastering the details of the language in which it
is written. Rather, it is understanding the underlying
model of the domain scattered throughout its functions,
methods, classes, etc.

On the other hand, though, I disagree. Someone once claimed
that Lisp is a "programmable programming language". It is
the best available tool for building new domain-specific
languages. Why settle for a tool that is less-featured in
this regard. (This is the heart of the admittedly trollish
tenth rule of programming due to Greenspun.)

Of course, we can respectfully disagree about whether this
is the right approach to building large programs. Perhaps
it is a defining characteristic of the Lisp community that
they think this way: When faced with a complex domain, one
"grows Lisp up" to meet it.
 
K

Kaz Kylheku

Gerry Quinn said:
You've lost me, I'm afraid...

In C++, a class is not just a blueprint for creating objects, but it's
also a lexical namespace. An identifier X declared within class C
(that is not nested in a namespace or another class) is actually C::X.
This is not class membership but scoping.

Within the body of a member function, C::X can be referred to using
the short name X, even though X is not declared anywhere in the
lexical scope: the body of the member function is not enclosed in the
class definition but is outside of it.

By manipulating the class definition, we can introduce names into the
scopes of function bodies that are located away from that definition,
and we can seriously alter their meaning.

This is a critical language design mistake; it's in no way necessary
for ``class scope'' to exist in an object system.

In Python, which has single dispatch like C++, members are referenced
using a self symbol, so that self.x means the x member of this object,
analogous to this->x, and x just has the normal meaning, denoting a
lexical variable or whatnot.

Lisp has multiple dispatch so that there isn't even a self object. A
method with three specializable parameters has three ``self'' objects.
They are explicitly named as parameters, nothing is hidden. Class
scope imposed into the Lisp paradigm would wreak even greater havoc,
because there would be clashes between identically named members from
the different classes.

Some dialects of the Pascal language have a much criticized WITH
statement that is similar to class scope. It introduces a block of
code in which the members of a record can be accessed using just their
short, unqualified names. Like C++ class scope, the WITH statement
has the problem that when fields are added to the record, they
suddenly become visible in all these scopes, potentially shadowing
existing references to those same names in those blocks of code.
Moreover, WITH statements themselves can be nested.

Modula-3 has a much more sane, completely different WITH statement, in
which the programmer explicitly declares unqualified names and the
expressions for which they stand, such as WITH X = A ... meaning
that X is like a local macro denoting A over this block.

Common Lisp has something similar, called SYMBOL-MACROLET. And it also
has something called WITH-SLOTS that is implemented on
SYMBOL-MACROLET. This WITH-SLOTS feature lets the programmer create
unqualified symbols that behave like variables over a scope, but which
are bound to the slots of a class object. For instance:

(with-slots (first-name last-name number) employee-object
;; just use first-name, etc.
)

The construct explicitly names the symbols for which bindings are to
exist. There is no way to manipulate this from a remote location.
Adding more slots to the class of employee-object has no effect,
because this is all lexically scoped.

The WITH-SLOTS macro can also give alternate names, which is handy if
you have nested WITH-SLOTS forms for objects of the same class, for
instance:

(with-slots ((x0 x) (y0 y)) point0
(with-slots ((x1 x) (y1 y)) point1
(my-graphics-library::draw-line x0 y0 x1 y1)))

If you are really aching for some of the conveniences of class scope,
you can emulate it by developing some short hand over WITH-SLOTS or
SYMBOL-MACROLET.

Lexical scope means that if we see some reference to (apparently) a
variable X, we just search the enclosing forms for the innermost
definition. If we don't find one, then it's an unbound reference, or a
reference to a global/dynamic variable.
 
R

Ray Blaak

Sashank Varma said:
But what you (seem to) give up when you accept class scope is this
beautiful generality.

Yes, but in the situations where I want the class scope I do not in fact
need/want the generality.

Note carefully that I do not want to give up the beautiful generality if
required: it is always there if I need it.
You claimed above that class scope models how humans think in certain
situations. Frankly, as a cognitive psychologist, I'm finding it hard
to think of data that support (or refute) your claim.

Let me be precise: it is how *I* think when solving certain kinds of problems.

For other kinds of problems I think differently, and then the full generality
of multimethods can come into play.
My intuition (and it is a just an intuition, not an established fact
about human cognition) is that taking a consistent, parsimonious
approach to function/method calls brings an increased economy to the
comprehension of Lisp code. Programming languages are formalisms, and
those who uses formalisms (e.g., mathematicians) have historically
worked awfully hard to eliminate special cases and unnecessary
redundancies.

The class scope view is a more restricted and simplied one. That simplicity
aids in understanding when it is in effect.

Anytime one builds absraction layers on top of general components and
features, there is a similar "restriction" coming into play. Any specific
solution is by definition a restriction of the set of possible solutions.

Or not. My essential point is that I have used both approaches in the past and
have found that there are times when I want the class scope view since it
guides the solution a certain way. Other times I need the general way.

I want both.
 
G

Gareth McCaughan

Maahes said:
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...

Appearances can deceive. The way a programming language looks
to someone not used to it provides very little information
about subtle matters like how much commenting programs in it
require.

A very quick survey of a bit of my own code suggests that
I write slightly fewer comments in Lisp code than in C or C++.
Generally, the things I do in Lisp are harder than the things
I do in C or C++, which you'd expect to imply more commenting.
So I have some weak evidence that Lisp code doesn't need
such heavy commenting as C++ code does.
 
J

Jerry Coffin

(e-mail address removed) (Kaz Kylheku) wrote in message
[ ... ]
Ambiguous means I have to look up a bunch of hidden rules that were
added to an ambiguous language and arbitrarily resolve the parsing
conflicts.

Okay, so we no longer have to wonder: you clearly don't know what
ambiguous means!
By the way, look at the ridiculous grammar factoring that's needed in
order to avoid the use of explicit precedence and associativity rules.
The expression 42 ends up being a multiplicative-expression *and*
an additive-expression at the same time. Yet it neither adds nor
multiplies.

I find this a bit interesting, but I'm afraid I'll have to wait until
after work to look it up, since my copy of the standard is at home.

[ ... ]
I'm almost certain that Japanese is full of ambiguities, being a
natural language.

Probably true -- but the ambiguities are due to the definition (or
lack thereof) of the language, not my ignorance.
The difference is that there aren't any hidden rules about it.

Hmm...your definition of "hidden" seems to be a strange one. The rules
(grammatical and otherwise) for C++ are all contained in a single
standard. For English there's no single standard specifying all the
grammatical rules, and probably not even one book directory to point
to all the other books that contain all the rules. To me, this seems
far more a matter of "hidden rules". I suppose Japanese may be better
than English in this regard (most languages are, after all) but I
still doubt that they're all in one place or anything like it.
An
ambiguity in natural language doesn't pretend to be something else.

That simply makes it sound remarkably as if you have little or no
experience with real life or natural languages at all. Just for
example, I'd guess that at least 60% of all the law suits filed on
earth are based on contrat language that pretended to be something
else, but was (in retrospect) clearly ambiguous.
If you utter something ambiguous that leads to a misunderstanding, you
can't claim superiority by referring the poor victim of your
misunderstanding to an associative precedence chart.

I see. I guess there are no contract attorneys (or probably any kind
of attorneys) in your world?

[ ... ]
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.

I probably shouldn't try to argue this one, since (based on your
comments above) you seem to be from a planet substantially different
from the one where I live, so you probably have considerably more
knowledge of being that are comletely alien, at least to me.

I guess in the end, if your position is that Lisp might be more useful
for aliens, so be, I won't try to argue the point. The fact remains
that by the time most people start to program, something like "a+b*c"
already has meaning for them, and completely ignoring that background
(and in fact basically requiring that they UNlearn the useful
knowledge they already posess) does NOT improve understanding.
Now the same alien receives:

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

Aha, it's obvious that the two symbols ( ) are special and that they
serve to enclose, as suggested by their shape. Moreover, they balance,
which reinforces that suspicion.

You start by postulating a being of which we know absolutely nothing,
but then postulate that you can provide an accurate prediction about
what it'll find obvious.

I'm not sure whether to believe that you're unbelievably coneited, or
merely insane.
What, like evaluation orders?

No, of course not. Which part of "syntax" didn't you understand?
That's not pure syntax, but semantics.

Oh, so it wasn't lack of understanding, just a lousy attempt at a
straw man.
That's a whole different pile of unbelieveable idiocy that should have
been fixed long ago.

You do nobody (especially yourself) any favors by claiming any
decision with you happen to disagree as "unbelievable idiocy".
Or, I'm guessing that perhaps by ``pure syntax'' you mean ``just the
raw grammar, with no symbol table information''. As in, what is this:

(a)(b)(c)+(d);

I'm not sure this is the simplest or best-chosen example, but at least
it IS an example of what I mentioned, yes.

[ ... ]
With my package, I could write the getc() macro call such that when
the first time it is evaluated, the enclosed expression is parsed and
classified. If it is found to have side effects, like getc(stream++)
then the program stops with an assertion. If it's suspected to have
side effects, a warning is produced and the program continues, and of
course if there are no side effects, it is silent. In both these
cases, the expression is stored into a hash table so it doesn't have
to be parsed again; the next time that same call is evaluated, the
hash will tell that all is good.

I guess I can see where this would be a stimulating intellectual
project, but I'm hard put to conceive of a lot of useful purposes for
it.
So as you can see, I have done some incredibly devilish things in
order to make a dumb language safer.

....or at least think you have. It sounds to me like an awful lot of
work with little real benefit, but if you're happy with what you did,
more power to you.
 
P

Peter Lewerin

Maahes said:
C: rabbit_jump(rabbit, 3)

C doesn't have classes and methods AFAIK. The Lisp call above is
really to a method of the rabbit class, but it looks exactly the same
as an ordinary function call.
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.

I assumed that even if the design decision was made to make jump a
class method, one would like to know which rabbit object the caller
wanted to have jump. Hence it is passed as an argument.
*two* languages.

Lisp and C? Well, in C's case that's because there is really one one
kind of call, so it's not all that surprising that the syntax for that
kind of call is consistent.
 
K

Kenny Tilton

Gareth said:
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.

Naw, I was just being a d*ckhead. That is what /I/ do at 1:30am when I
get back from the pub.

:)

kenny
 
J

Jon Boone

Let me start by thanking you for your honest and thoughtful
response. I appreciate the opportunity to learn more about how
others program.

Gerry Quinn said:
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.

So, unless I misunderstand the symantics of the 'protected'
specifier, thinks marked this way are accessible to the class and
any sub-classes, but no one else. Is that correct?

I believe items declared 'public' are accessible to anyone, as
they would be in C. If one does not declare an specifier, do they
not default to 'public'?
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'.

Again, IIRC, 'private' items are inaccessible to everyone except
the class itself. Have I misunderstood the semantics?
Contrary to popular belief, I do evolve my methodologies, albeit
reluctantly ;-)

I have no doubt. It happens to all of us, sooner or later. :)

--jon
 
R

Rahul Jain

There are, however, ambiguities to be found, such as:

a b(c);

In C++, this could be either a definition of b as an object of type a,
with c as an initializer, OR it could be a declaration of b as a
function that returns an a and takes a c as a parameter. There are a
number of other examples along this line, but (at least from a
viewpoint of pure syntax) these are real ambiguities. C++ has a fairly
simple rule about how to deal with all of them, but even though the
rule itself is simple, implementing it is anything but, and is
semantic, not syntactical -- the syntax itself really IS ambiguous.

Didn't I already say that? Maybe I wasn't being clear, but this is why a
real description of the structure of a C/C++ program can not be very
similar to the source. It needs all kinds of annotations indicating what
each token really is in this semantic context.
 
R

Rahul Jain

Hartmann Schaffer said:
afaik, AMD used a lisp written system to verify the design of their 64
bit processors. search for ACL2 (sorry, don't have the url handy)

It's a debian package, if anyone cares.

The code is at:
ftp://ftp.cs.utexas.edu:/pub/moore/acl2/v2-8/
 
R

Rahul Jain

Gerry Quinn said:
I would say "self-modifying code" includes any program in which code is
generated and executed as part of the expected output of the program.
It's no longer running itself as originally written.

I know there are inevitable ambiguities and special cases, simply
because there is no real dividing line between code (to be executed) and
data (to be manipulated). I would augment my definition above to
exclude cases in which there is a 'hierarchy of execution levels'. I
would not consider code that runs a cellular automaton that performs a
computation to be self-modifying, for example. The generated code has
to have approximate 'parity of esteem' with the original code.

Then lisp code is usually not self modifying. Macros simply translate
from a simple declaration of semantics to a possibly complex
implementation of those semantics. You can modify a function's
definition from within the function, but that's rarely a useful
technique. I have used it to good effect in a timed genetic algorithm
search where I had the top level be a loop that kept doing the
select-breed cycle and I could redefine the functions that selected and
bred the population without having to stop execution. The new
definitions were simply picked up on the next iteration of the loop.
 
R

Rahul Jain

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

What "advantages" are you imagining here?

Lisp has 3 different constructs to represent 3 different concepts. C++
has one that tries to be all 3 at once. If you want to use one part and
not the other, you're screwed.

Types are defined using defclass.
Scopes are created using let/lambda.
Namespaces are created using defpackage.

Yes, C++ has namespaces separately, too, but you can't choose to not
inherit the namespace of your superclass. Since you are inheriting scope
as well as type, instance variables are shadowed instead of merged. The
language can't tell whether the name clash was intentional (attempting
to add declarations to the variable - such as making the type
declaration more specific) or unintentional.

In Lisp, you never get stuck in a situation where you want to extend a
type but maintain an independent scope so that variable names don't
clash.
 
P

Phlip

Rahul said:
Didn't I already say that? Maybe I wasn't being clear, but this is why a
real description of the structure of a C/C++ program can not be very
similar to the source. It needs all kinds of annotations indicating what
each token really is in this semantic context.

"Doc, it hurts when I go like that."

"I'm writing you a prescription. 'Don't go like that.'"

A description of a program at the source level should be its source. If you
have a problem with that, spend more time cleaning up your code, so a
statement like a_t b(c_t) is more clearly a declaration, with _t on its
types, and not a function call.

A higher level description of a design shouldn't waste its time on low-level
details like those.
 
R

Rahul Jain

Steven E. Harris said:
Refining interfaces and enforcing invariants.

In CL, using package system deliberately can aid in /communicating/
these intents or desires, but there's a stronger non-enforceable trust
factor involved.

I assume Gerry is talking about this issue.



Gerry Quinn said:
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 ;-)

Didn't we just discuss why CL is bad and C++ is good because C++
_trusts_ the programmer to do his memory management and pointer
arithmetic perfectly?

Do you use a garbage collector and disable pointers when you are working
in a project team? If so, what's the magic compiler flag? ;)
 
R

Rahul Jain

Maahes said:
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.

Don't you document your code?

Even without documentation, Lisp does this fine. Stuff that's marked as
internal-only (prefixed with % or %% if it's really internal to the
implementation) and/or unexported from the library's package (needs to
be accessed with a double colon instead of a single one from the
outside) is stuff that isn't guaranteed to be stable, or at least won't
be announced as a change.

If you are using symbols like these from someone else's code, you
obviously know what you're doing and need to mess with the internals of
the library. You'll know to pay close attention to that code breaking
when you upgrade.
 
R

Rahul Jain

Gerry Quinn said:
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'.

Oh really? So you shouldn't use imperative code in an OO application?
Interesting.
Somebody else proposed that everybody design their own object system.

No. Actually, most features can be added to the object system directly,
since it can be extended using itself. If you want something radically
different, sure, you can design your own object system, just like you
can design your own control structure when that makes your program's
intended behavior easier to discern from the code.
OO as 'language feature' is cargo-cult OO.

Yes, we're not true cultists, sorry. We don't follow the One True Way of
typopace (type/scope/namespace) object orientation in every line of our
code. We'd rather write each section of code in a way that allows that
code to make as much sense and be as maintainable as possible. We're
horrible people.
The bamboo airport looks just like a real one, but the planes aren't
going to land.

Except that Lisp's object system is a complete object system, even more
so that C++'s (or Java's for that matter). Even before CLOS, objects had
types and functions/objects could be polymorphic. That's why CLOS can be
implemented as simply a set of macros on top of the older Lisp
features... except for one spot: being able to define new types needs to
be done specifically for each implementation, because this functionality
already existed in older Lisps and CLOS has to integrate completely with
the rest of the type system to be complete.

As I've gone over before, other aspects of the C++ object system that
don't have to do with object orientation are implemented directly and
independently in Lisp.
 
R

Rahul Jain

Gerry Quinn said:
Of course, this is true of any high level language. In C++,
customisation comes mostly from defining classes and methods - the
language syntax remains consistent throughout.

Which seems like a win to me...

The syntax of Lisp is consistent throughout and is consistent even
_before_ you extend the behaviors.

But classes and methods don't encompass all of the ways you want to
express your application's various behaviors. If they did, why do you
need any syntax other than classes and methods?

Why do mathematical operators need to be infix? Just call an add()
method on a number object, passing it another number object and you'll
get the sum back as the result.

Why do you need looping constructs? Just define a class that has methods
for the four parts of the for construct in C -- constructor for the
initialization clause, a step() method for the step clause, a done()
method for the termination test, and a body() method for the loop's
body. Isn't that cleaner?
 
R

Rahul Jain

Kenny Tilton said:
Naw, I was just being a d*ckhead. That is what /I/ do at 1:30am when I
get back from the pub.

Yeah, that's what happens when you leave the pub early and have time to
kill before you get your unemployed ass to bed somewhere around 5:00.
 

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

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,058
Members
47,668
Latest member
SamiraShac

Latest Threads

Top