The best kittens, technology, and video games blog in the world.

Monday, July 24, 2006

List of things that suck in Scheme

So here's another post in a great series that so far featured Ruby, Ocaml and various other things. So now the list of things that suck about Scheme, as defined in R5RS standard. You may want to take a look at the standard, because it's reasonably short and well-written compared to average standard.

  • No support for such basic data types as arrays (vectors are not resizable, so they don't count), and hash tables/dictionaries. Most programs need both, and in Scheme one needs to either reimplement them in every program or hack around this lack. Compare with handling of numerical types, which are overspecified far beyond needs of typical program.
  • The standard has horribly complex support for numeric types. Vast majority of programs require a single numeric type - bignum. Significant minority of programs require a second numeric type - hardware-native (typically IEEE754 double precission) floating point numbers. Number of programs that use rationals or complex numbers or the rest of the extremely complex type scheme is within statistical error equal to zero. I'd guess that regular expressions, XML handling, or POSIX file system operations are all >100x more common than those weird types, and resizable arrays and hash tables/dictionaries are like >1000x more common. One would think that the number system is at least any good if it takes significant portion of the standard ? Actually it sucks, it's impossible to easily extend it to add support for your own types (like vectors or matrices). In the end - huge loss.
  • Character type is totally broken. What a character really is is a very short string. If we use Unicode (and we do use Unicode), a string is a series of code points. Now the characters are one or more codepoints. For example U+0041 code point is simply Latin Capital Letter "A", but U+0065 U+0301 together make a single character Small Latin Letter "e" with Acute Accent (é). So no - code point is not a character. And Scheme explicitly prohibits implementing characters as strings - they are explicitly required to be separate types. This is totally broken. Most language suck at Unicode support in implementation level, but Scheme managed to do that at standard level. I know it's older than Unicode, but back thern i18n issues existed.
  • Scheme is not even minimally object oriented. So no generic comparisons (> a b), no shalow copy operator (dup x), no type queries (is_a? a 'blob). Half of that functionality is hacked in highly inelegant way, so you can ask objects whether they're strings by (string? x), but this is a new hack for every type. Goo gets it right.
  • Multiple value return. This is the semantic equivalent of Sam the ugliest dog. Functions can return (values 1 2) instead of (list 1 2) and then you can do some magic to get the extra values back. It was probably marginally faster than using lists/tuples/arrays to return multiple values. Oh yeah, assembly performance hacks and let's screw the semantics.
  • Highly excessive indentation levels. Compare Scheme:
    (define veclen
      (lambda (x y)
        (let ((x2 (* x x)) (y2 (* y y)))
          (let ((z2 (+ x2 y2)))
            (sqrt z2)
          )
        )
      )
    )
  • with RLisp:
    (defun veclen (x y)
      (let x2 (* x x))
      (let y2 (* x x))
      (let z2 (+ x2 y2))
      (sqrt z2)
    )
    You can be a Lisp without such excess (it seems that Arc is also saner here). Oh yeah, and there are let* and letrec to confuse everyone (Ocaml has the same problem with let rec) .
  • Macro system seems harder to use and less powerful compared to Common Lisp's and RLisp's. But in the real life, all implementations of Scheme seem to provide "real" macros.
  • car/cdr/lambda - come on, just throw away the historical garbage. The only true names are hd/tl/fn.

24 comments:

Anonymous said...

You may want to learn how to use the language before bashing it:

(define (veclen x y)
(letrec ((x2 (* x x))
(y2 (* y y))
(z2 (+ x2 y2)))
(sqrt z2)))

Many of your other points are bogus as well.

Anonymous said...

Arr, no fixed-width:

(define (veclen x y)
_(letrec ((x2 (* x x))
________(y2 (* y y))
________(z2 (+ x2 y2)))
_________(sqrt z2)))

Something like that.

taw said...

Anonymous: I think you're actually proving my point.

Scheme has multiple forms of let (I'm well aware of that), and no matter which form you use, the code still looks uglier than RLisp (which has just one).

In all languages except for Scheme and a few other Lisps and Lisp-alikes, assignment/binding does not require extra indentation, no matter where you do it. This includes languages with semantics and syntax as diverse as Ruby, Python, Perl, Java, Haskell, C++, Ocaml, Nemerle and RLisp.

I don't think it's a good idea to have too many forms of assignment (or binding if you prefer this name). Assignment is just assignment. If multiple variants of assignment at least improved expressiveness or something, but RLisp assignemnt (Ruby-inspired) seems much more powerful than Scheme.

I don't think any other points are "bogus" either. I don't think I'm alone here - the areas I pointed are changed in R6RS. Unfortunately they want to keep very high level of backwards compatibility, so they cannot completely fix these issues, and apply partial fixes instead.

PS. Blogger restricts html usable in comments a bit too much.   is probably the only way of indenting lines in comments.

cj said...

let, let* and letrec are for those puny mortals who fear the power of lambda!

(define veclen
 (lambda (x y)
  ((lambda (x2 y2)
    ((lambda (z2) (sqrt z2))
     (+ x2 y2)))
   (* x x) (* y y))))

; Yes, this is meant as a joke

Anonymous said...

There is a reason for multiple binding forms: they have different semantics.

Observe:
(define (foo lst)
(let ((lst lst) (x lst))
....
))

Here, you rebind lst in the block using the old value of lst *and* you keep it around for x to use. If everything was letreced, then this wouldn't be possible.

let* is intended to provide the sequential scope semantics you allude to at your RLisp blah.

letrec is fully mutually recursive bindings.

It would help you to think before you complain about these things.

Also, about your other complaints:
pretty much all schemes have the extra types you crave (and r6rs goes the fully monty there).

Also, the reason for only syntax-rules in r5rs was that the r5rs committee was not comfortable standarizing on any of the full-power macro systems available
at the time and left it for the next revision. The next revision is here, and r6rs includes Dyvbing's syntax-case.

For object orientation, most schemes again have the kind of support you desire and CLOS-like object systems that provide the generics semantics you want.
Besides, macros let you do everything else should you really want. Objects are just glorified closures, and scheme is the queen of closures, so your argument about "not even remotely object oriented" misses the boat -- in fact it misses the whole ocean.

Character types: take a look at mzscheme for example, read and learn.

Multiple value return: it is invaluable, unless you understand how to use something don't quibble about it, you only appear an idiot. (cf the let forms above)

lambda/car/cdr: they have historical significance and are deeply ingrained into the brain of every lisp hacker. Besides, hd,tl, and blah are every bit as arbitrary. Just because you learned ml before lisp, doesn't make them right.

As for the excessive levels of indentation, what can I say? Use emacs and learn how to place parens mate :p

jto said...

Under R6RS, you can say:

 (define (veclen x y)
   (define x2 (* x x))
   (define y2 (* y y))
   (define z2 (+ x2 y2))
   (sqrt z2))

taw said...

jto: That sounds great! I've heard mostly bad things about R6RS (too much Reddit) but it doesn't seem to be that bad :-)

jto said...

Multiple return values really annoyed me at first, until I saw what they were getting at, which was that continuations and lambdas are the same thing. If a lambda can take multiple arguments, then a continuation should be able to accept multiple values.

You might say the right answer is to make functions only take one value (like in the ML world). I suspect not enough people's brains are wired to understand currying, though.

Anyway, I think getting hung up on let/let*/letrec and so forth misses the point. That Scheme sucks for actually getting work done should be obvious to everyone by now. The question is what you can learn from the language.

(My takeaway from Scheme: 1-Lexical scoping is good; if you can't get it right please don't make a language. 2-Macros are interesting. 3-Continuations + mutable state = teh suck.)

Anonymous said...

I HATE SCHEME!!!

that is all.

Unknown said...

Sceme blows

Anonymous said...

and what do you use, java, i suppose?

taw said...

Anonymous: But most obviously RLisp !

Anonymous said...

general grevious is entirely correct. scheme does, in fact, blow. it's the most pointless programming language ever. of course, it's the only programming language i've ever been exposed to, but given the fact that when i was exposed to it the teacher even said "you will never use this anywhere but here" i feel pretty safe in this assumption...

Anonymous said...

(define (scheme)
(sucks))

result:
ERROR SCHEME IS TOO RETARDED TO DO WHAT YOU WANT IT TO DO.

Anonymous said...

All computer programs suck because they all make no sense. I mean come on some programs require this notation (+ x y) or some do this A+B = A. WHY WOULD YOU CREATE A NEW VALUE FOR A WHEN YOU CAN JUST ASSIGN THE VALUE IN THE FIRST PLACE! WHY DO WE NEED TO PUT A SYMBOL B4 THE INPUT! Seriously if someone made a really easy programming language then I will seriously like thank him for helping the computer-stupid people like me.

taw said...

Anonymous: Programming is an inherently nontrivial activity, in spite of a couple generations of people trying to make it accessible to everyone and failing miserably.

As far as "easy" languages go, Python would be a good choice for a first language. It has a good balance between being simple, powerful, and popular.

Anonymous said...

Dude I was so surprised I found a forum named for how much scheme sucks because I was like just randomly typing scheme sucks and I find this. So I dicided to post.

I suppose your reasoning for why scheme sucks is true. I really do not understand programming and I am currently failing my course (my average is um 13 I think). I really do not like how my courses assume you actually know how to like use a computer. I actually just bought a computer maybe a year before I started scheme. All I used it for was World of Warcraft and microsoft word. Seriously I never knew what a browser was or a desktop was. And now the teacher uses all these terms that I do not know and it gets frustrating. To make things worse, programming itself is impossible to understand. I am looking at this code

(define (veclen x y)
(letrec ((x2 (* x x))
(y2 (* y y))
(z2 (+ x2 y2)))
(sqrt z2)))

a reply and I have no idea what it means. Why is there a * before the x/y? What is letrec? What is veclen? Why would they name x2, y2 and z2 when they could (i think they want the distance formula) use the square root of (x^2 - y ^2)? None of this makes sense! I hate computers now!

Anonymous said...

To the last person who commented:

(define (veclen x y)
(letrec ((x2 (* x x))
(y2 (* y y))
(z2 (+ x2 y2)))
(sqrt z2)))

a reply and I have no idea what it means. Why is there a * before the x/y? What is letrec? What is veclen? Why would they name x2, y2 and z2 when they could (i think they want the distance formula) use the square root of (x^2 - y ^2)? None of this makes sense! I hate computers now!


My reply:
(* ______) means multiply.
x2 is the name he used for x^2.
So, x2 (* x x).
In more or less mathematical language, that would be:
x^2 = x * x

Kunjan Kshetri said...

I use PLTScheme/Racket and I think it can solve some of your problems.


Scheme is not even minimally object oriented.

Have a look at Swindle: http://docs.racket-lang.org/swindle/index.html?q=swindle


Highly excessive indentation levels.

This is how we actually do it.
(define veclen
(lambda (x y)
(let* ([x2 (* x x)]
[y2 (* y y)]
[z2 (+ x2 y2)])
(sqrt z2) )))



Macro system

What you said about Scheme's Hygenic macros is completely wrong. I have had this debate with a lot of people. A sample debate : http://www.reddit.com/r/programming/comments/aa9zf/why_i_chose_common_lisp_over_python_ruby_and/c0gl0nk

car/cdr

It is now first and rest.

Anonymous said...

As per your OO comment, there are beautiful ways to do generic dispatch in Scheme. See Scheme Mechanics at some point.

taw said...

Kunjan Kshetri/Anonymous: You can bolt object system on top of anything, that doesn't make it object-oriented. See for example: Perl, C++, CLOS, Python.

Kunjan Kshetri said...

What is your definition of Object Oriented Programming?

And also did you have a look at PLTScheme/Racket? I think will solve almost half the "problems" you have with Scheme.

Anonymous said...

I could only agree on Macro side in scheme. Indeed hygienic macros are less powerful/universal than CLISP macros.

Anonymous said...

Yes thank god we have less confusing languages

float InvSqrt (float x){
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}