Draft Scheme Standard R6RS Released
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."
(I (for one (welcome (our new (Scheme (overlords.))))))
I will post comment after reading (give me a few days)
Who knows enough to condense it into a few sentences?
liqbase
Does using scheme make sense when building a windows GUI application? What about a web application? System scripts? Back end to a database?
Is it more or less powerful than, say, C++, Java, or Python?
Actually, I have been carrying around R5RS with me for a while; it will be nice to suck down more print quota on r5r6 (when it is released). Scheme is quite a fun language, though as far as Lisps go, I do prefer Common. Still, for people who want to learn how to program, I generally tell them to start out with Scheme, usually using the PLT stuff, and to look at SICP.
Scheme is a language that every programming language enthusiast should know. Being both simple and flexible, it's suitable for communicating and explaining all kinds of concepts. A lot of books and papers are about Scheme or use Scheme for examples or teaching (see Readscheme.org). Scheme also pioneered some of the concepts in modern programming languages (such as lexical scoping), as well as several uncommon features (such as hygienic macros and first-class continuations). There are many Scheme implementations, some tiny, some slow, some fast, some with extensive libraries, some which interface to other programming languages, etc. etc.
Please correct me if I got my facts wrong.
..."I came, I saw, I programmed in LISP." This is interesting, but what I'd like to see is a dual-core-optimized dialect of QBasic that will handle obscenely large arrays without kvetching.
Paleotechnologist and connoisseur of pretty shiny things.
lightning000rod@hotmail.com
((((((((comment this) below) jokes) programming) functional) all) place) please)
I've never heard about this language, but hopefully the new version will help it keep up with the latest innovations in programming languages, such as codeblocks and Web 2.0.
Yours truly,
Fictional stereotypical teenage Ruby fanatic.
"Oppression and harassment is a small price to pay to live in the land of the free." -- Montgomery Burns.
are both tools of beauty that have taught me more about programming and problem-solving than all other languages combined. SICP and PAIP are both classics in this regard that everyone should rush out and get now.
It's just such a pity that, since they're both standards which anyone can implement, lots of people do, and as a result, finding one you like and then getting it to talk to other languages and libraries can be a very frustrating experience. And languages like Python with one canonical implementation driven by a BDFL and with exceptional library support are just getting more Lisp-like, which can't be good news for for a renaissance in Lisp or Scheme. Pity really, since I really like 'em both...
--- Hot Shot City is particularly good.
I swear the people coming up with tech names these days are just trying to confuse everybody. I mean, come on... Scheme?? That's just as bad as calling the new Half-Life Engine "Source". A scheme is something which is used in programming, as is the source code. Why can't they call it something unrelated to technology so that people don't get confused by the scheme of a Scheme data set just like people got confused with the source for Source was stolen.
I rather program in an modern language that has invented closures and anonymous functions, like perl.
For those of you who don't know what "properly tail recursive" means, a quick explanation. Consider the following code:
:-)
(define (f x) (x x))
(f f)
This defines a function, f, which takes one argument, x, which should be a function (yay, first-class functions!), and calls x upon itself. Then, it calls f on f.
Of course, this will cause f to call f upon itself. Again. And again. Infinite recursion!
Now, proper tail recursion means that if a function call returns in tail position (meaning it is the last thing the surrounding function does before it returns), the activation frame for the surrounding function is replaced by that of the function it calls. Contrast this with normal recursion, where a _new_ activation frame would be created for the called function.
Tail recursion makes the example code above run in bounded memory...looping forever.
Please correct me if I got my facts wrong.
(let loop ()
(display "> ")
(write (eval (read)))
(newline)
(loop))
Please correct me if I got my facts wrong.
Despite having written/co-authored 2 booklets on programming (AppleScript for Absolute Starters and Become an X-coder), I've never mastered programming and I keep an eye out for easy to learn language, allowing me to focus on the programming instead of on keeping straining my brain with the grammar side of it. One of the earliest languages I learned was Pascal, which wasn't that bad, but doesn't appear to be as popular as it used to be (TurboPascal and all). But Scheme looks like one of the many programming languages developed for parsers and compilers, instead of for the people. Programming languages should be easy to read for humans too.
Recently I read about a programming language (Python?) where global variables and local variables had a different starting thingie ($ and, I forgot). Now things like that DO help to make sure you're not a writer of a collection of bugs.
Bert
Just the number of parentheses rules out Scheme for me
[...] reducing the number of primitives by creating a can of worms like CALL-CC is not good design.
Please argue for this point.
Are you adequate?
Since Scheme wasn't the one of the versions of LISP that I learned back in the dark ages, I couldn't really follow the subtleties of which changes are really significant, but it looks like it would make sense if you were following Scheme.
Bill Stewart
New Fast-Compression-only CPR http://preview.tinyurl.com/dy575ks
Yay, they standardized on a library system!
But Scheme looks like one of the many programming languages developed for parsers and compilers, instead of for the people. Programming languages should be easy to read for humans too.
Lisp syntax certainly does not attempt to look like the combination of English text and mathematical formulas that most languages shoot for, but this in fact has many advantages. The idea of making a language look like that doesn't change the fact that the language will work in a way very different from English or mathematical notation; your previous knowledge of those things will not necessarily help you reason about your code, and at worst, may confuse newcomers by tempting them to apply analogies that don't hold. And to achieve that "look" for your language, you always end up giving it a really complex and inflexible syntax, whose users are not going to have any systematic knowledge of. (Do you know many people who can give you a BNF grammar for Java, or tell you the exact precedence rules for it?)
Lisp makes no pretence at looking like English or mathematics. You're certainly expected to understand the syntax rules of the language more than in "friendlier" ones, but these rules are far, far simpler, and you can actually reason them through. Remember, Scheme oooks regularly include a section that shows you how to write a Scheme interpreter in one page of Scheme code; basic knowledge of how Scheme itself works is considered to be elementary Scheme knowledge.
That is, what I'm saying is that compared to other languages, Lisp dialects demand that you understand the language itself far more, but this is a good thing, which will make you program way better. Why? Because you're going to be able to reason about the execution of your program far better than your average Java programmer.
Plus, you can do macros.
Are you adequate?
;;;
;;; Macro to increment a variable by one.
;;;
;;; Usage:
;;;
;;; (define a 5)
;;; (inc! a) ; a is now 6
;;;
(define-syntax inc!
(syntax-rules ()
((inc! x)
(set! x (+ x 1)))))
Not that idiomatic Scheme code will do this very often. Langauges like Java or C use "++a" most often for loop indexes. Looping in Scheme is typically driven off data structures.
Are you adequate?
SML or OCaml are great lenguages, but if you're going to learn a functional language, Haskell is a great place to start. First because the syntax is very clean (I never quite liked the "let rec" bit) but also because in both SML and OCaml it is too easy to slip back into imperative styles. Haskell makes that substantially more difficult, which means to use it well you really, really have to get the whole functional programming idea.
I notice that r6rs has a lot more editors than r5rs, and another author...
Which isn't surprising. r6rs is twice as long as r5rs which had the entire table of contents on the first page.
I'm glad to see that a lot of the slib stuff is being merged into scheme, and that they're making a lot of progress on number types. r6rs is laying out a lot more information on ADTs, IO and Unicode support.
The explanation of continuations is still as clear as mud. How are they so easy in Python and so hard in Scheme?
Because managers are stupid fucks and don't know any better?
"The way we can tell it's C# instead of Haskell is because it's nine lines instead of two." -- wadler
Humans deal best with languages in which form matches function and there is a bit of redundancy.
Sure, you can go too far. Perhaps perl and C++ do.
In something like C or Pascal, a block is normally rather distinct from an expression. Humans rely on visual pattern recognition to read code. Scheme delibrately avoids anything that would be useful for pattern recognition.
With Scheme, it all becomes a blur. Every line is like every other line.
Yeah, because every peon is clamoring for Scheme and Smalltalk, while the ev1l managers are forcing C and Java on them. ::rolls eyes::
Well, I'm glad that we seem to agree that it is necessary (though not sufficient) for a language to have exceptions and threads in order to be a good language for communicating concepts in computer science. Right now, Scheme is defined by R5RS, and R5RS lacks those. That's one of the many reasons for Scheme's failure to catch on (contrary to popular opinion, syntax is probably not one of them; after all, XML and Perl succeeded despite their syntax).
Whether R6RS will be a good language for communicating concepts in computer science remains to be seen. From what I see in the draft standard, R6RS is a very different language from R5RS. I think R6RS ceases to be a good language for its original purpose (teaching), and it still remains a poor language for grown-up use (although for different reasons than before).
It's a real shame that the bungling of the Scheme and CommonLisp designers have effectively killed the entire Lisp family of languages. The world could really benefit from a nicely designed Lisp.
This always comes up, but if you're at all interested in programming languages, here's why you should learn Scheme.
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.
If moderation could change anything, it would be illegal.
I am sure there are a numer of ways to learn Scheme if you are interested. Here's one: follow the CS-61A course podcast of Brian Harvey's class at Berkeley.
meh.
``"properly tail recursive" is an oxymoron. Tail recursion is not proper. Decent programmers use loop constructs for looping.''
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.
Please correct me if I got my facts wrong.
The problem with looping as opposed to recursive, is that recursive is functional programming and thus it is easier to prove the code correct. Looping has side-effects, and thus it is more prone to bugs. None of this makes sense to the VB raised programmer for which for every nail, VB is the hammer.
Besides which Scheme has looping, it is just that it is implemented using the more primitive construct of tail recursion. Scheme gives you the power to invent your own control constructs.
Those are good things to have some standard way of doing, yes, but they don't actually support the point you made: "reducing the number of primitives by creating a can of worms like CALL-CC is not good design."
Why so? Because you can have it both ways: you can have call/cc as a language primitive, plus standard threading and exception facilities implementable with macros on top of call/cc. (And if you look at R5RS, you'll see that this approach is already taken for large parts of the language; the standard demonstrates how to define, for example, let, in terms of lambda and macros.)
Are you adequate?
I thought the Scheme language had been standardized by the IEEE a while back. Is this a revision of the standard, then?
We all know what to do, but we don't know how to get re-elected once we have done it
Clearly the second example is more elegant, given the assumption of a C-like language, as it is an in-place algorithm. As you point out, given the constraints of C the recursive implementation eats memory and thus doesn't scale well to GCD(large number); the tail-recursive feature of Scheme allows for an arguably simpler-to-visualize implementation.
Having written my own Scheme interpreter for a programming languages course back in my University days, I must admit I prefer C; however, each language has its strengths -- and that's why you use it!
VBS is a Microsoft Windows Scripting Host (WSH) language, while VBA is the Microsoft extension language for writing macros in Word or Excel. While they have similar syntax, the application is a little different. A scripting language is a glue language for building new applications that use objects, system resource, GUI widgets, and even entire applications such as MS Word or a Web browser as a piece-part of the application. An extension language is something for writing a plug-in or extension of a particular application like Word or IE.
Is this a distinction without a difference? I suppose you could extend Word if you had a way to script Word, but there is a difference in emphasis between a stand-alone script that does something with Word and a plug-in that is run from Word.
I suppose for Scheme as a plug-in language, it is probably like AutoCad and Emacs using Lisp dialects as extension languages. But is this the same as using Scheme/Guile for scripting? Or if Python is widely available on Linux for scripting, does this mean Gimp exposes a scriptable interface to Python (Word exposes a scriptable interface to Python under Windows, and many COM/ActiveX objects are usable from Python, but largely because of Windows-specific libraries that can be called from Python)? Does this also mean that Gimp can call back into Python to implement plug-ins in Python? So in this sense, I get the feeling that Scheme/Guile is limited to plug-ins and not much used for scripting?
What in Hell is that supposed to mean?
.p2align 4,,15 .L9: .L7:
C is as properly tail recursive as Scheme. I just compiled your code with gcc 4.1 on PowerPC. I see no recursion in the assembly output. Some of you Scheme people seem to have no clue about modern compilers.
In case you somehow have a clue about assembly:
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
It's time you looked at the output from a modern C compiler. I compiled the example for PowerPC using gcc 4.1 and got the assembly shown below. There is no recursion. There isn't even any memory access; everything is done in registers. (FYI, "blr" is the PowerPC instruction for "return")
.p2align 4,,15 .L9: .L7:
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
... for the same reasons why we must learn history (to know not to make the same mistakes ourselves)...
scheme code unevitably turns into an unreadable pile of ((((((((((()((() junk
Actually, I could not give an accurate representation of scheme, because slashdot kept saying 'Lameness filter encountered. Post aborted! Reason: Please use fewer 'junk' characters.'
And don't get me started in functional programming languages.
--- Hindsight is 20/20, but walking backwards is not the answer.
Are you looking for looping in the generated machine code or looping in the source code? If you "emulate" looping constructs with macros and tail recursion in Scheme, you get both. (and I'm not going to bother finding out, but I assume the standard requires such macros predefined)
My other car is a cdr.
Moderator hint: a comment is neither "Flamebait" nor "Troll" if it is true.
Short and simple explanation of continuations:
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.
Please correct me if I got my facts wrong.
Sorry, I don't usually post these, but how on earth is the parent post "flamebait"?
It seemed to be making the perfectly reasonable point that just because LISP dialects are very uniform, this doesn't necessarily make them easier for humans to use.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
Felix, the author of Chicken Scheme, a fairly popular Scheme implementation, doesn't seem very fond of R6RS.
:-)
I just thought my fellow Slashdot readers could want to hear his informed opinion.
Furthermore, one can access other libraries than those written in Scheme, using bindings to C/C++ libraries, or in the case of SISC, access to any and all Java libraries. This, combined with Scheme's flexible language constructs, has led to amazingly powerful programming frameworks such as SISCweb (think Web 3.0 if you want a buzzword).
The rule in lisps is that the first argument of a list is the executable, and the rest of the list is the parameters given to the executable. The normal mapping is from verbs to executables.
I think we've pushed this "anyone can grow up to be president" thing too far.
``The problem with looping as opposed to recursive, is that recursive is functional programming and thus it is easier to prove the code correct. Looping has side-effects, and thus it is more prone to bugs.''
Actually, that's not completely true. Since looping can be expressed as tail recursion and the other way around, they are both equally referentially transparent. Of course, this is more obvious in case of tail recursion, which is why you would probably transform loops to tail recursion before making proofs.
Please correct me if I got my facts wrong.
``Clearly the second example is more elegant, given the assumption of a C-like language, as it is an in-place algorithm.''
Alright, I should probably have made it clear that I was just using C syntax to make it easier for people to understand the code (assuming that more people read C than Scheme). I also obviously wasn't clear about the meaning of elegant; that was meant to refer to the source code, not whatever the compiler cooks up as a result.
Under these definitions, I think there's no denying that the first snippet is more elegant (although I've met people who claim that recursion is evil, always. I wonder how they write functions...)
Your statement that the second snippet is more elegant, because it runs in bounded space, is a nice example of how the lack of proper tail recursion can force you to go out of your way to come up with good (or even working) algorithms. A compiler _could_ transform the first snippet into efficient code, and thus probably _should_. If you think cluttering up your code to help the compiler generate efficient code, think about manually unrolling loops, pipelining your code, inlining functions, constant folding, etc. Yes, they make it possible to have simple compilers generate efficient code, but they don't increase the elegance or legibility of your programs. Tail call optimization is just like that, in my opinion.
Please correct me if I got my facts wrong.
Um, when did you last ever write that in a real program?
Anyway:
;;;
;;; The unless macro is not R5RS Scheme, but most implementations
;;; include it, so you might not need to define it. (The R6RS draft
;;; does include it.) Just in case, here's an R5RS macro system definition
;;;
(define-syntax unless
(syntax-rules ()
((unless cond expr . rest)
(if (not cond)
(begin expr . rest)))))
;;;
;;; Call proc for each number between lo and hi, inclusive.
;;;
(define (iterate-over-range lo hi proc)
(unless (> lo hi)
(proc lo)
(iterate-over-range (+ lo 1) hi proc)))
;;;
;;; this one is straightforward
;;;
(define (print-line x)
(display x)
(newline))
;;;
;;; print each number from 1 to 10, in separate lines
;;;
(iterate-over-range 1 10 print-line)
Are you adequate?
[...] if you are stepping through a list and transforming each member in a common way, an iterative description seems more clear (particularly a for-each kind of approach.)
Ever heard of map?
Are you adequate?