Draft Scheme Standard R6RS Released 235
Watson Ladd writes, "The new version of the official Scheme standard has been released as a draft (PDF)." From the draft: "[This] report gives a defining description of the programming language Scheme. Scheme is a statically scoped and properly tail-recursive dialect of the Lisp programming language invented by Guy Lewis Steele Jr. and Gerald Jay Sussman. It was designed to have an exceptionally clear and simple semantics and few different ways to form expressions. A wide variety of programming paradigms, including imperative, functional, and message passing styles, find convenient expression in Scheme."
Re:Qs (Score:3, Interesting)
Scheme's first-class continuations [wikipedia.org] are actually a very good match for web programming, where a user can re-submit an old form at any time. Basically, with languages that lack call/cc, you will have to break up your application in little pieces to account for this, whereas call/cc allows you to structure your application like a regular one, and the language implementation will deal with the issues.
When it comes to practicality, it all depends on the implementation. The Scheme report is (deliberately) very minimal, and you can't write many Real World programs using only standard Scheme. However, implementations often provide features and libraries that make Scheme practical for real projects, to some extent.
``Is it more or less powerful than, say, C++, Java, or Python?''
It depends what you mean by "powerful". In the programming language community, the answer would likely either be "Scheme is more powerful", because its constructs are very flexible (e.g. it has first-class functions and continuations, which the languages you mention lack or only have to a limited extent), or "No", because all these languages are Turing-complete and thus can do everything the others can.
However, outside the programming language community, the question probably refers not just to the language, but also to the libraries, and there Scheme could reasonably be said to lose out to at least Java. On the other hand, there are Scheme implementations that can call Java code, and thus access everything that is available for Java...
Re:For Language Enthusiasts (Score:3, Interesting)
Why should I learn Scheme? (Score:5, Interesting)
A few years ago I was doing a project that involved parsing the intermediate code that GCC generated while compiling a C program. Doing a bit of research I found out that one of GCC's intermediate stages was a language called RTL (register transfer language). To my surprise, RTL looked something like this:
(set (reg:0) (mem:blah blah))
But wait, I thought -- that looks like Lisp. Come to find out RTL was based on lisp s-expressions.
It was then I realized what the Big Deal with Lisp was - it has no syntax at all, and programs written in this parenthetical form are trivially converted into a parse tree. In fact, if you've ever written a simple interpreter or compiler, odds are good you'd use a list-like structure to store the parsed code.
The reason Lisp and Scheme are so "powerful" is that you, as a programmer, have direct access to the program's parse tree at all times. (You can even alter the parse tree at compile time with macros, which is really modifying the compiler to suit your program.)
But really, the best way to learn why Scheme or Lisp are so great is to implement them. Writing a Scheme to assembly compiler will give you an incredibly deep understanding as to how compilers and programming languages in general work.
If you were to try to write a compiler for any other language, you'd probably spend most of your time on the lexer and parser. With Lisp or Scheme, the program, as written, is already almost fully parsed for you. Once you understand that, you'll realize why it's so cool.
Re:an oxymoron (Score:5, Interesting)
That's highly debatable. I would agree that when you want to express the concept of a loop, it's best to use a looping construct, but other people would disagree. Also, tail calls are more general than loops; for example, they also work for mutually recursive functions.
Which is more elegant?
int gcd(int a, int b) {
return (b == 0) ? a : gcd(b, a % b);
}
or
int gcd(int a, int b) {
int t;
while (b != 0) {
t = b;
b = a % b;
a = t;
}
return a;
}
``Your problem is that Scheme can't do that.''
That depends on what you mean by "Scheme can't do that". It's entirely possible to implement looping constructs in Scheme, and several people have done so. Scheme can't do looping in the same sense that C can't compute factorials.
``When all you have is a hammer, everything looks like a nail.''
Except that, in Scheme, you can make your own tools on a much more fundamental level than in many other languages. Thanks to tail call optimization, you can _implement_ looping constructs, even though the language doesn't provide them.
``Needless recursion makes your code more convoluted and less readable.''
I think the example I gave earlier illustrates that, sometimes, recursion leads to less convoluted, more readable programs. The right tool for the job, ey? In a language that isn't properly tail recursive, the recursive gcd would be a bad idea because the recursion would eat memory, but in Scheme it's no problem.
``It's amazing how people can claim a deficiency as some kind of advantage. You just keep smoking...''
I'm not claiming a deficiency as an advantage. I'm claiming tail call optimization is a nice features to have. There is no deficiency here.
You could argue that the lack of looping constructs is a deficiency. However, the lack of looping constructs (1) is not at all implied by the language being properly tail recursive, (2) is easily remedied, and (3) actually has been remedied in many implementations.
And no, I don't smoke, though I do live in the Netherlands.
Re:Qs (Score:3, Interesting)
I won't argue this point further. I've already stated that languages without _extensive_ libraries can still be useful in specific domains. I still stand by that view.
``And languages like Scheme and Lisp are particularly crippled because they don't even have a standard extension mechanism.''
Huh? The package system is a standard feature of Common Lisp. R6RS may standardize a module system for Scheme (although this is not certain yet); in the absence of that there is SLIB, which provides a de-facto standard mechanism.
Re:Qs (Score:3, Interesting)
no it doesn't (Score:3, Interesting)
gcd:
cmpwi 0,4,0
bne 0,.L7
blr
mr 4,0
divw 0,3,4
mullw 0,0,4
subf 0,0,3
mr 3,4
cmpwi 7,0,0
bne 7,.L9
mr 3,4
blr
Continuation Crash Course (Score:3, Interesting)
A continuation is "the part of the program that hasn't been executed yet". If your code is
(foo)
(bar)
(baz)
and you've just executed (foo), the continuation is (bar) (baz).
In Scheme, continuations are first-class values, which means they can be stored in variables, returned from functions, passed as arguments, etc.
There is a single way to create continuations, namely through the call-with-current-continuation form (often abbreviated call/cc). You pass call/cc a function that takes one argument, and call/cc will call that function, passing it the current continuation as an argument. E.g., in
(foo)
(call/cc fun)
(bar)
(baz)
fun will be called with a continuation representing (foo) (bar) as an argument.
The continuation itself is a function that takes one argument, and calling that function will have the effect of returning from the call/cc that created it, with the value you pass to the continuation as a return value. E.g.
(display (call/cc (lambda (cont) (cont 42))))
will display 42, because that's the value you passed to the continuation.
Now, things get _really_ interesting once you don't immediately invoke the continuation, but you store it in a variable for later use. E.g.
(define cont #f)
(call/cc (lambda (c) (set! cont c)))
(foo)
Now, cont is set to the continuation that, when invoked, will have the effect or returning from the call/cc form (i.e. (foo) will be the next form executed). The great power of Scheme's continuations is that this cont can be invoked any number of times, just like a normal function.
What is it actually useful for? Well, it's definitely not something you use all the time. But you could use it to implement threads, for example:
(define *threads* '())
(define (schedule-thread thread)
(set! *threads* (append *threads* (list thread))))
(define (run-next-thread)
(if (not (null? *threads*))
(let ((thread (car *threads*))
(set! *threads* (cdr *threads*))
(thread))))
(define (make-thread thunk)
(schedule-thread thunk)
(yield))
(define (yield)
(call-with-current-continuation
(lambda (cont)
(schedule-thread (lambda () (cont #t)))
(run-next-thread))))
Now, you can create a new thread by saying
(make-thread (lambda ()
; your thread's code goes here
))
and a thread can give up its timeslice by calling (yield).
No time to check the code now, I have to go to a meeting.
Re:142 page PDF... (Score:3, Interesting)