C++ sucks for games

R

Rahul Jain

I don't find C's syntax particularly complex at all -- though I'm the
first to admit that this may be due (in part) to having used it nearly
as long as I have Lisp.

The description of the structure of C code is objectively more complex
than for Lisp code.
I have to admit this is an argument that surprises me -- while I agree
that C code generally has more (and better) structure that most Lisp
code, this is the first time I've seen even the most rabid advocate of
a language claim that its lack of structure was actually an advantage.

This is what allows Lisp to be what it is: a language where you add
features as you want them. With a complex microsyntax, the structure of
the parsed code won't be the same as the structure of the code itself.
Is a for loop really 3 individual syntactic elements one after the
other, or is it one element with three parts? if/else blocks become even
more complex. How would I define a new type of operator like that so
that the parser is able to cope with it? In Lisp, the parser already can
cope with arbitrarily structured code, because the code contains its own
structure.
The syntax of C obfuscates structure only to the least perspicacous of
observers.

My comment above about a for loop's syntax should explain what I was
talking about.
...or at the end, or in a property list, or just about anywhere else
you feel like putting it.

In that case, you're not writing Lisp code. You're defining some data
structure that's manipulated by some code into Lisp code, maybe. The
specification of Lisp's evaluation semantics is as I said.
I won't bore you with the (ancient) code
that did it, but I have some old Lisp here that accepts input in
postfix format (as non-list S-expressions, natch) and prints turns
them into normal infix notation.

It's not inputting Lisp code, then. It's using a custom parser.
I suspect what he's really missing is the fact that Lisp is almost
always written using an editor that's aware of Lisp syntax to at least
a fairly reasonable degree, so indentation and some sort of
highlighting to match parentheses is extremely common -- though in
fairness it should be pointed out that it's common primrarily because
machine help is nearly an absolute necessity to compensate for the
paucity of Lisp syntax.

Of course, but I find that I can't deal with Java code effectively
without the same features, either. I ignore microsyntactic noise in both
Lisp and Java, I suppose.
 
J

Jerry Coffin

(e-mail address removed) (Kaz Kylheku) wrote in message
[ ... ]
The function belongs with the arguments; enclosing it together with
them expresses this grouping.

Quite the contrary -- the function is something radically different
from its arguments. Even if a function accepts other functions as
arguments, the function being invoked is treated and used entirely
differently from the argument(s).

[ ... ]
But:

(obj.*pmemb)(x, y); // .* low precedence, parens needed!

Ooops.

If you move the function to the right of the parenthesis, these
ambiguities disappear.

First of all, you should be sufficiently aware of programming language
terminology to be well aware that this is NOT an example of an
ambiguity. It's true that the default precedence in C isn't always
right -- but it's NOT true that this leads to an ambiguity. The
meaning without parentheses may not be what you wanted, but that
doesn't mean it's ambiguous at all.

C defines operator precedence that works correctly (at least by doing
some looking through my code) well over 95% of the time. Lisp never
requires parentheses to override default operator precedence, but only
because it throws out the baby with the bathwater -- rather than
requiring parentheses to fix the precedence in a tiny minority of
cases, it requires parentheses in _every_ case, no matter _how_
obvious the right thing to do is.

[ ... ]
The text is in fact mostly words and spaces. The, punctuation, is,
quite, minimal, compared, to, C++, Pascal, Etc. What a waste of
keystrokes; what do the commas accomplish? They disambiguate things
like x, ++y versus x++, y.

The do considerably more than that -- they (along with C's plethora of
other syntactical clues) provide enough differentiation that the eye
tracks it much more easily.

Hopefully we can start by agreeing that to somebody who doesn't know
the language at all, that nearly every programming language is
_completely_ unreadable -- Cobol is the only thing that really even
tries, and one of its unique dangers stems directly from the fact that
people _think_ they understand it long before they really do. Neither
C, nor C++, nor Lisp, however, presents much danger in this regard.

As such, at least _some_ degree of familiarity with the language at
hand is required before "readability" really has much meaning.

With that given, tests have repeatedly shown that C is far more
readable than Lisp. Just for an obvious example, consider finding
problems in code, such as:

int fact(int x) {
if (x == 0);
return 1;
return x * fact(x-1);
}

and:

(define (fact x) (if (= x 0)
(1
(* x (fact (- x 1))))))

In my testing, C programmers find the problem in the C code an average
of more than five times as fast as Lisp programmers find the problem
in the Lisp code. In addition, C compilers almost universally give
SOME sort of complaint pointing (at least indirectly) to the problem
in the C code (typically pointing out the dead code) while Lisp
compilers (and interpreters, the last time I looked) generally accept
the latter code without so much as a peep.
One syntactic sdidiocy is patched over
with another. What a false economy; just so that you could have these
prefix operators and whatnot, you have to type extra punctuation even
when you are not using them!

Lisp hardly provides a suitable platform from which to launch such a
diatribe. Rather the contrary -- Lisp has more extra punctuation than
nearly any other language extant.

In any case, while counting keystrokes has some meaning when
evaluating calculators (yes, I still use HPs), typing in punctuation
on a normal keyboard rarely seems to be a real source of major
problems. If terseness is your measure of choice anyway, then any
choice other than APL seems nearly indefensible for you.
 
D

David Steuber

Computer Whizz said:
I shall investigate it pretty soon, as I shall be getting alot more free
time... I shall then just pop in to your lisp newsgroup to post my
thoughts... Don't expect anything of me though - the mindset is boggling!

I came from a C/C++ background myself. I've done a little Java, but
not much. Also, my C++ mostly predates templates. The last compiler
I used was the first one from the vendor to support STL. Lisp is
different.

I actually found that it is a bit easier to transition through another
language. Perl, while very much C based, helps to start breaking away
from some of the constraints that C forces you into. Python (I
haven't really done much with this either) probably goes even further.

I am still doing a lot of C like thinking. But the more problems I
solve in Lisp, the more I appreciate the language. It is a fun
experience for me.
 
B

Brian Downing

With that given, tests have repeatedly shown that C is far more
readable than Lisp.

I'm curious - do you have references for these tests?
Just for an obvious example, consider finding
problems in code, such as:

int fact(int x) {
if (x == 0);
return 1;
return x * fact(x-1);
}

and:

(define (fact x) (if (= x 0)
(1
(* x (fact (- x 1))))))

In my testing, C programmers find the problem in the C code an average
of more than five times as fast as Lisp programmers find the problem
in the Lisp code. In addition, C compilers almost universally give
SOME sort of complaint pointing (at least indirectly) to the problem
in the C code (typically pointing out the dead code) while Lisp
compilers (and interpreters, the last time I looked) generally accept
the latter code without so much as a peep.

I saw the problem right away, even with the screwy indentation, and I've
only been hacking Lisp for a year or two. But assuming I didn't see it:

First of all, let's indent the Scheme code properly. Note I didn't do this
by hand, I just threw it at emacs:

(define (fact x)
(if (= x 0)
(1
(* x (fact (- x 1))))))

Notice how the indentation below the if statement doesn't line up. This
is the first big clue that something is wrong. Any mildly experienced
Lisp programmer will see the "shape" of this code as wrong.

That aside, I seem to have very good implementations installed by your
standards:

Scheme48:
| Warning: non-procedure in operator position
| (1 (* x (fact (- x 1))))
| (procedure: #{Type :exact-integer #f #f})

I also translated your example into CL for tests with CL compilers.

(defun fact (x)
(if (= x 0)
(1
(* x (fact (- x 1))))))

OpenMCL:
| > Error in process listener(1): While compiling FACT :
| > 1 is not a symbol or lambda expression in the form (1 (* X (FACT (- X 1)))) .

SBCL:
| ; in: LAMBDA NIL
| ; (1 (* X (FACT (- X 1))))
| ;
| ; caught ERROR:
| ; illegal function call
| ; compilation unit finished
| ; caught 1 ERROR condition

CMUCL produces the same error as SBCL when compiling the function, or on
calling (fact 0).

Furthermore, gcc (the only C compiler I have available) doesn't produce
any warnings for your C example with "-ansi -pedantic -Wall". However,
a quality editor will indent the code unexpectedly just like emacs does
for the Lisp example, so the problem can be caught in that way.

-bcd
 
M

Maahes

((A || B) && C) &&
!(X || Y)


(and (and (or a b) c)
(not (or x y)))

I guess this just shows how pointless this entire thread is, since I find
the first example much cleaner and easier to understand than the second.
 
M

Maahes

I suspect what he's really missing is the fact that Lisp is almost
always written using an editor that's aware of Lisp syntax to at least
a fairly reasonable degree, so indentation and some sort of
highlighting to match parentheses is extremely common -- though in
fairness it should be pointed out that it's common primrarily because
machine help is nearly an absolute necessity to compensate for the
paucity of Lisp syntax.

That's been mentioned several times so far in this thread. I just don't see
it as an excuse to say the language is ok, coz an editor helps you program
it.
The same as saying obscure coding standards are ok, coz a debugger can be
used to show you whats going on..
 
D

David Steuber

Kelsey Bjarnason said:
* Garbage collection.

Never seen this one defended as being actually useful for anything but
hand-holding of incompetent coders.

Before I started programming in Common Lisp, I felt the same way. My
first introduction to GC was via Java. It looked exactly like a
system for holding the programmer's hand. New without delete?
Sacrilege!

Of course, scripting languages such as Perl, Python, and others all do
automatic memory management. It seems quite natural in that context.
With Lisp, the GC is even more natural. Modern Lisp implementations'
garbage collectors can even outperform manual memory management.

I am very familiar with this prejudice because I held it so long
myself.
 
M

Maahes

Ok, thanx for the heads up.
So Jak and Daxter isn't even a proof that Lisp is useful for games, since
even those programmers decided they needed a variant for it to be useful...

And the "bugs in GOAL's compiler" I guess shows that Lisp isn't even prooven
to be good for writing the compiler..

:)
 
C

Cameron MacKinnon

Jerry said:
(e-mail address removed) (Kaz Kylheku) wrote in message news:<[email protected]>...


First of all, you should be sufficiently aware of programming language
terminology to be well aware that this is NOT an example of an
ambiguity. It's true that the default precedence in C isn't always
right -- but it's NOT true that this leads to an ambiguity. The
meaning without parentheses may not be what you wanted, but that
doesn't mean it's ambiguous at all.

It creates opportunity for programmer error. Multiple levels of operator
precedence and direction leads to code which, for many everyday math
expressions, many people find more compact and (initially) readable. But
other expressions are tricky to correctly construct in the first place,
and difficult to read subsequently. Good C style suggests sometimes
inserting extra parentheses to make the code more easily understandable
to future maintainers. Lisp neither requires nor allows such practice.
C defines operator precedence that works correctly (at least by doing
some looking through my code) well over 95% of the time. Lisp never
requires parentheses to override default operator precedence, but only
because it throws out the baby with the bathwater -- rather than
requiring parentheses to fix the precedence in a tiny minority of
cases, it requires parentheses in _every_ case, no matter _how_
obvious the right thing to do is.

You make Lisp sound long winded, verbose, redundant and redundant. But
those parens replace all the punctuation in C, not just its parens. I
find Lisp code is typically much more concise than code in those other
languages. Further, that consistent use of parens is the key to the
powerful macro systems which make it easy to programmatically transform
large, complex blocks of code.

As such, at least _some_ degree of familiarity with the language at
hand is required before "readability" really has much meaning.

With that given, tests have repeatedly shown that C is far more
readable than Lisp. Just for an obvious example, consider finding
problems in code, such as:

int fact(int x) {
if (x == 0);
return 1;
return x * fact(x-1);
}

and:

(define (fact x) (if (= x 0)
(1
(* x (fact (- x 1))))))

Would you care to cite those tests?

As regards your code above, I can't help but notice that the Lisp code
is shorter than the C. Please don't feel compelled to create a
counterexample.

But they're both indented incorrectly. Lisp editors have been
automatically indenting code for decades now, a handy labour saving
device. So your Lisp typo above hasn't been a danger since the era of
"blue ribbon" coding practice, when computer time was so expensive that
programs were keypunched offline, then visually inspected for bugs
before handing the deck off to the guy in the white smock, who would run
it and give you the printout of the results.

I understand editors that automatically indent code have become popular
among users of other languages recently.
Lisp hardly provides a suitable platform from which to launch such a
diatribe. Rather the contrary -- Lisp has more extra punctuation than
nearly any other language extant.

Ooh, that gets me wondering. I compared a 460kloc CMUCL source tree
(.lisp files only) with a 377kloc VNC tree (.c files only). For the Lisp:

[cmackin@ode]$ find ~/lisp/cmucl-20031202/ -name '*.lisp'|xargs cat|perl
-ne 'foreach(split
//){if(/[a-zA-Z0-9]/){$_="ALPHANUMERIC"}elsif(/\s/){$_="WHITESPACE"}else{$_="OTHER"}$x{$_}++;$total++}END{foreach(sort
keys %x){print "$x{$_}\t". int($x{$_}/$total*100) . "%\t$_\n";}}'

10269697 63% ALPHANUMERIC
2503321 15% OTHER
3288547 20% WHITESPACE

And similarly, for the C:
6122166 61% ALPHANUMERIC
1410264 14% OTHER
2414824 24% WHITESPACE

As others have said, once one is used to looking at Lisp code (say a
week for the really slow learners), a lot of the parens tend to
disappear, especially the long strings of )))))) that terminate blocks.
 
B

Brian Downing

That's been mentioned several times so far in this thread. I just
don't see it as an excuse to say the language is ok, coz an editor
helps you program it.
The same as saying obscure coding standards are ok, coz a debugger can
be used to show you whats going on..

I think it's more accurate to say in Lisp's case that its more regular
syntax helps the editor be much better at manipulating it than it would
otherwise be capable of. For example, I know of no editors that support
manipulating C structure at the syntactic level the way emacs lets you
work with S-expressions. Once you learn how to use these features it is
/very/ powerful. (And it spoils you - editing C is not very fun for me
anymore because of the lack of editor support I'm used to with Lisp.)

So, even if raw C were better than raw Lisp, Lisp + emacs is still
better than C + any C-aware editor I'm aware of, including emacs.

(If anyone does know of an editor that allows manipulation of C in a
structured way in addition to as a bunch of characters, I'd love to know
about it - I write C for my job.)

-bcd
 
D

David Steuber

As such, at least _some_ degree of familiarity with the language at
hand is required before "readability" really has much meaning.

I agree with this.
With that given, tests have repeatedly shown that C is far more
readable than Lisp. Just for an obvious example, consider finding
problems in code, such as:

int fact(int x) {
if (x == 0);
return 1;
return x * fact(x-1);
}

and:

(define (fact x) (if (= x 0)
(1
(* x (fact (- x 1))))))

In my testing, C programmers find the problem in the C code an average
of more than five times as fast as Lisp programmers find the problem
in the Lisp code.

I expect a C programmer who is not familiar at all with Lisp will see
how the C function can be blown up. It certainly doesn't help that
the Scheme version doesn't even seem to be defined correctly. In
Common Lisp, the above fact function might look something like this:

(defun fact (x)
(if (< x 1)
1
(* x (fact (1- x)))))

I cheated a bit by not letting the thing blow the stack with a
negative x. I don't think a Lisp programmer would add unnecessary
parens. The compiler certainly wouldn't take them. The Lisp version
will also return the correct result for values of x where x! is larger
than MAXINT.

After typing in this reply and going back to proof read it, I only
just noticed the extra semi-colon in your if that breaks the C fact
function completely. I noticed the extra parens in your Scheme
version right off. Granted, a C compiler will warn you about not
returning an int (or is it an error?) with your fact function.

In Lisp, it is also fairly trivial to use a technique called
tail-optimization. Scheme guarantees this by definition. Most Common
Lisp compilers also support it. The tail-call version of fact looks
something like this:

CL-USER> (defun fact (x &optional (a 1))
(if (< x 1)
a
(fact (1- x) (* x a))))
FACT
CL-USER> (fact 0)
1
CL-USER> (fact 1)
1
CL-USER> (fact 2)
2
CL-USER> (fact 3)
6
CL-USER> (fact 4)
24
CL-USER> (fact -1)
1
CL-USER> (fact 5)
120
CL-USER> (fact 50)
30414093201713378043612608166064768844377641568960512000000000000

Tail optimization is simply a way for the compiler to turn a recursive
function into iterative code. Of course Common Lisp supports a large
number of iterative constructs. So you don't have to bend your brain
looking for a tail-recursive function.

I also typed this into a live REPL to make sure I didn't flub the
tail-call version. Even though the factorial function is a very
common example, the tail version is less so and I had to make it up
from scratch because it wasn't in my memory.

It also let me show off bignums ;-)
 
J

Jon Boone

(e-mail address removed) (Kaz Kylheku) wrote in message
[ ... ]
The function belongs with the arguments; enclosing it together with
them expresses this grouping.

Quite the contrary -- the function is something radically different
from its arguments. Even if a function accepts other functions as
arguments, the function being invoked is treated and used entirely
differently from the argument(s).

How so? In Lisp, we have *expressions* which are evaluated for
their value and/or their side effects. Ommitting special forms and
macros for a second, we get:

1. Each argument expressions is evaluated to produce one or more
values.

2. The function expression is evaluted using the values of the
arguments obtained by the step above.

What's different about these two cases? In both cases, an
expression is evaluated after it's arguments have been evaluated for
their values, etc. With the recursive evaluation process involved
in lisp, there is no distinction between functions and arguments
because they are all just expressions.

In the case of *some* special forms, not all (or perhaps any) of
their arguments are evaluated prior to the logic of the form being
evaluated, which is useful for things like (quote ...) which
precludes evaluation of it's argument expression.

In the case of macros, which are source-to-source transforms, the
argument expressions are not evaluated prior to the macro
transformation taking place - and you can specifically control how
much of the argument expressions are evaluated *during* the
transformation.

I think that the distinction you're referring to is a by-product of
procedural languages, most of which have problems with functional
composition.

It is even possible to represent data as nothing more than closures
with accessors into the closure. Take, for example, this
implementation of the "cons" data type which uses the constructor
my-cons and accessors my-car/my-cdr (adapted from SICP):

(defun my-cons (x y)
(lambda (car-or-cdr)
(cond ((= car-or-cdr 0) x)
((= car-or-cdr 1) y))))

(defun my-car (a-cons)
(if (null a-cons)
nil
(funcall a-cons 0)))

(defun my-cdr (a-cons)
(if (null a-cons)
nil
(funcall a-cons 1)))


CL-USER> (setf the-cons (my-cons 'a 'b))
#<Interpreted Function "LAMBDA (X Y)" {4095B7C9}>
CL-USER> (my-car the-cons)
A
CL-USER> (my-cdr the-cons)
B
CL-USER> (setf the-list (my-cons 'a (my-cons 'b '())))
#<Interpreted Function "LAMBDA (X Y)" {4096BA79}>
CL-USER> (my-car the-list)
A
CL-USER> (my-cdr the-list)
#<Interpreted Function "LAMBDA (X Y)" {4096B9E1}>
CL-USER> (my-car (my-cdr the-list))
B
CL-USER> (my-car (my-cdr (my-cdr the-list)))
NIL

Contrast this with the default representation of a "cons":

CL-USER> (setf the-cons (cons 'a 'b))
(A . B)
CL-USER> (car the-cons)
A
CL-USER> (cdr the-cons)
B
CL-USER> (setf the-list (cons 'a (cons 'b '())))
(A B)
CL-USER> (car the-list)
A
CL-USER> (cdr the-list)
(B)
CL-USER> (car (cdr the-list))
B
CL-USER> (car (cdr (cdr the-list)))
NIL

--jon
 
J

Jon Boone

Hartmann Schaffer said:
iirc, one of the many texts on the history of lisp went into detail
abut that: the people working on and with this interpreter discovered
macros, used then heavily, and saw no way how to preserve their power
when switching to M-expressions

I have the Lisp 1.5 manual and I, for one, find M-exps to be more
difficult to read than S-exps. And, although I use emacs quite
regularly, I also about 20% of my time in a REPL w/out a
"lisp-aware" editor. I don't generally have problems with
mismatched () either.

On a related note, I'm also quite capable of taking a conversation
on multiple, nested tangents w/out losing track of how to complete
each tangent and get back to the conslusion of the main thread of
conversation.

Lisp just works more closely to how my brain works.

--jon
 
S

Szymon

Frank Buss said:
sorry, is a bit longer :)

(sqrt (reduce '+ (mapcar (lambda (x) (* x x)) '(-1.2 0.99 23))))

(sqrt (apply '+ (mapcar '* #1='(-1.2 0.99 23) #1#)))

;)

or less lispy, but more concise [and less memory
consuming]:

(sqrt (loop for i in '(-1.2 0.99 23) sum (* i i)))

Regards, Szymon.
 
G

Gerry Quinn

[...]
GQ> I am familiar with the basic concepts of Lisp, I am familiar
GQ> with the arguments in favour of Lisp put forward by
GQ> enthusiasts, and I am familiar with the generally weak profile
GQ> of Lisp enthusiasts in terms of actually delivering product. [...]

"Enthusiasts" who do things for fun are not expected to deliver
"product" regardless of what langauge they use. If you have reason to
say this about people who use lisp professionally compared to users of
other languages, then perhaps you could tell us that reason? (AI
that wasn't in the 80s doesn't count).

Take away the Lisp evangelists who are using Lisp professionally, and
you won't be left with a lot. This thread was posted on a games
development newsgroup, by somebody called NeoLisper, who I doubt has
produced a game in Lisp or any other language.

Lots of people develop games for fun in C, C++, Delphi, Java, Flash and
VB.

I think the nub of the issue is that, if you take away the relatively
useless ability to write self-modifying code, what Lisp is mainly good
for is representing complicated algorithms. But representing
complicated algorithms is a solved problem on any modern high-level
language, and any extra benefits from Lisp are likely to be minimal for
99% of programming tasks.

People who have just begun a project, or indeed who have never completed
a real project, are apt to overestimate the importance of algorithms in
programming. They tend to be only a tiny part of nearly all finished
products, particularly games. Any small win on algorithm definition
will be swallowed up by Lisp's many disadvantages.

- Gerry Quinn
 
G

Gerry Quinn

I am familiar with the basic concepts of Lisp, I am familiar with the
arguments in favour of Lisp put forward by enthusiasts, and I am
familiar with the generally weak profile of Lisp enthusiasts in terms of
actually delivering product. The success of one group of talented
developers despite a plainly idiotic choice of development tools
notwithstanding. [I even quoted them, the Lisp users, to justify my
opinions.]

How many times does this have to be said?! Go to the website of a lisp
vendor and look at their success stories. For example:
http://www.franz.com/success/. Most of the projects listed there are pretty
interesting. It's ridiculous to suggest that we're basing our arguments on
the success of one set of developers.

Only one set of game developers in that list.

- Gerry Quinn
 
G

Gerry Quinn

With all due respect to conflicting subjective points of view, all it takes
is a single counter example to refute the often voiced opinion that "lisp
may very well be <yada yada> but you cannot write real world applications
with it"

Indeed, it proves that games can be written in Lisp - but it does
nothing to refute the opinion that Lisp is not a good choice, or to
support the opinion encapsulated in the title of this thread.

- Gerry Quinn
 
F

Frank Buss

Szymon said:
(sqrt (apply '+ (mapcar '* #1='(-1.2 0.99 23) #1#)))

nice reader macro trick.
or less lispy, but more concise [and less memory
consuming]:

(sqrt (loop for i in '(-1.2 0.99 23) sum (* i i)))

and there are other useful reader macros. If all numbers are know at
compile time, you can write it like this:

#.(sqrt (loop for i in '(-1.2 0.99 23) sum (* i i)))

and it doesn't consume any time or memory at runtime :)
 

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,205
Messages
2,571,067
Members
47,673
Latest member
MahaliaPal

Latest Threads

Top