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
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.
The GNU project adopted Scheme (with the Guile interpreter) as its official scripting language. Applications are not meant to be written in Scheme, but applications can expose functionality to the user through a Scheme interface. That is to say, plugins for extensible applications could be written in Scheme. The Gimp is one of the most noteworthy applications with a Scheme interface, and much of the lower-level functionality of GNU Lilypond is reached with Scheme.
..."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.
Hmmm... you will need libraries and such for that, though some people do use Scheme for all of those things (see http://www.plt-scheme.org/ and http://www-sop.inria.fr/mimosa/fp/Bigloo/). I have personally used it for web applications, though I usually use common lisp and ocaml for that. In fact, if you are looking for an alternative to C++, Java, or Python, I must recommend OCaml. Look at this book. In fact, I wrote an interpreter for R5RS in OCaml...
((((((((comment this) below) jokes) programming) functional) all) place) please)
``What about a web application?''
Scheme's first-class continuations 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...
Please correct me if I got my facts wrong.
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.
> 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.
Without extensive libraries, most programming languages are not very useful for many
common tasks.
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.
The name "Scheme" was picked in the 1970s, so I wouldn't say Scheme joins Source in "today's" bad naming practices.
"Oppression and harassment is a small price to pay to live in the land of the free." -- Montgomery Burns.
(lambda (parent) (display (list parent 'is 'this 'a 'joke?)))
``Without extensive libraries, most programming languages are not very useful for many common tasks.''
True, but that's neither here nor there. A language can make an excellent text processing, web programming, scripting, etc. language without needing extensive libraries.
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.
[...] 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?
Here, let me help you parse the replies to your questions:
No, no, no, no, no, no and no.
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
That's because you're an idiot. The parentheses are not a problem at all when reading code.
Yay, they standardized on a library system!
Short subjective answer: No
Scheme is a nice language and has some very interesting properties, so learning it is well worth it, but its really not the lanuage that you would want to use for your daily work, for that it simply lacks a lot of convenience features (++a becomes (set! a (+ a 1)) and it also lacks standardization bejoint its very core, which means that every Scheme implementation comes with its own set of libraries for all kinds of jobs, even trivial tasks like a for-loop you have to either code yourself or rely on non-portable extensions.
In terms of 'power', yes its more powerfull, with macros I can do a heck of cool stuff that isn't possible in most other languages, but then those tricks seldomly result in readable code and you can get very similar effects with much cleaner tools like Rubys blocks, generators and similar things provided by other language.
To make it short, Scheme is for most part an academic language, worth learning it is, worth using not so much.
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?
(define-macro (increase! var . rest) ,var (+ ,var ,(if (null? rest) 1 (car rest)))) ,var))
`(begin
(set!
(increase! a)
(increase! a (+ 1 2))
Obviously you could use (++ a), but that is not important.
ID: the nose did not occur naturally, how would we wear glasses otherwise? (apologies to Voltaire)
, 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.
Just the number of parentheses rules out Scheme for me
You're missing out then on the language with perhaps the easiest grammar in existence.
From Wikipedia: "Scheme started as an attempt to understand Carl Hewitt's Actor model.[1] Scheme was originally called "Schemer", in the tradition of other Lisp-derived languages like Planner or Conniver. The current name resulted from the authors' use of the ITS operating system, which limited filenames to two components of at most six characters each. Currently, "Schemer" is commonly used to refer to a Scheme programmer."
but its really not the lanuage that you would want to use for your daily work, for that it simply lacks a lot of convenience features (++a becomes (set! a (+ a 1))
This just shows a lack of fundamental understanding of how one typically writes Scheme programs. If you're incrementing variables to the point where that becomes a concern, you're completely misusing the language.
even trivial tasks like a for-loop you have to either code yourself or rely on non-portable extensions.
Again, this shows you have no experience with the language, or you've been using it horribly wrong. There's no reason you should ever need a for loop in Scheme. If you're going to use Scheme as mostly imperative language, you're better off with Python or similar.
I have worked on a database modeler and a complete IDE under Windows. It was a bastardized Scheme with objects, but still...
So I have to say: done, done, maybe, maybe. And yes, if only for the ability to execute any code while stopped in the debugger.
ID: the nose did not occur naturally, how would we wear glasses otherwise? (apologies to Voltaire)
Despite starting out with the usual languages of my generation, such as Basic, Pascal, C, and C++, I eventually chose Python as the jack-of-all-trades for my programming for the reason you mentioned. It is human-friendly rather than focusing on saving clock cycles, and I wanted to be able to work from one language for almost everything (it has bindings for almost everything). It's kind of a strange coincidence that Scheme was Slashdotted today, as I was browsing websites for learning LISP or Scheme as well. Maybe it's just intuition, but my gut feeling is that the simplicity of LISP/Scheme will help me re-think programming, as they say. My end goal is to improve my Python programming, and perhaps use Bigloo Scheme to write compiled programs for the JVM, .NET, and C that I will use in coordination with Python.
But I would be careful about being too quick to judge Scheme as a human-unfriendly language. There are lots of parentheses to type, but to me that doesn't hurt the readability because its uniformity maintains the same "flow" throughout the program. It's all just lists using parentheses. Normally, I would hate parentheses being a Python programmer, but at least Scheme doesn't require semicolons and tons of other types of brackets and dots and slashes, and so the elegance of parentheses-only makes up for the extra typing. From what I've seen so far, readability is more a matter of knowing the names of the procedure calls and keywords, so it has more to do with being unfamiliar with the language.
By the way, you must be thinking of a different language than Python. Python doesn't require a $ as a prefix for anything at all. Global variables are either already global or you use the "global" keyword. Perl uses a $ in front of variables, but if you want to avoid "straining your brain with the grammar side" and use a simple and readable language, I sincerely hope you aren't thinking of Perl. Not that it's a bad language, but readability is a deliberate effort in Perl that isn't really encouraged by the syntax itself.
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.
Sure, if you enjoy rolling your own FastCGI/HTML generation/string processing/database interface/ libraries.
Languages without extensive first/third parties libraries are useless. And languages like Scheme and Lisp are particularly crippled because they don't even have a standard extension mechanism. I say this having explored the idea of rolling some interesting applications in Lisp... why would I spend half my time writing libraries when I could switch to Perl/Python/Ruby/C/C++/Java/etc and just get things done?
Actually, Scheme/LISP was designed for "correctness" rather than for ease of compiler implementation. Take for instance something like
x = y + 1;
in C. That doesn't actually mean "x = y + 1" it means "x = y + 1 (mod 2^32)". Why is it done this way? Because it is a lot easier for a compiler designer to implement integers if they are always a fixed number of bits.
On the other hand, Scheme does it the correct way, so that (set! x (+ y 1)) actually does what it looks like it does. This is one of the reasons C compilers took off in the 70s while lisp compilers stagnated. I suggest you read the paper "Worse is Better"
---
Your resident MIT student
PS. Don't take this as support for Scheme. I hate Scheme.
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.
``Languages without extensive first/third parties libraries are useless.''
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.
Please correct me if I got my facts wrong.
Huh? The package system is a standard feature of Common Lisp.
Ah, sorry, as I recall it was the foreign function interface that varied from implemention to implementation.
As for your first statement, sure, any language can be tailored to be useful in a particular domain. Can you name such a domain for Scheme? I can't.
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?
[even trivial tasks like a for-loop you have to either code yourself or rely on non-portable extensions]
Umm, lots of stuff yes, for loop, no. R5RS specifies the syntax do, which is basically a for loop. For example
(do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i))
Produces #(0 1 2 3 4) and is equivalent to
int[] arr = new int[5];
for(int i = 0; i !=5; i++){
arr[i]=i;
}
return arr;
As the Americans learned so painfully in Earth's final century,free flow of information is the only safeguard against...
``As for your first statement, sure, any language can be tailored to be useful in a particular domain. Can you name such a domain for Scheme? I can't.''
Ah, but I was only refuting your claim that no language can be useful without extensive libraries. I wasn't talking about Scheme, specifically.
If you do want examples of what Scheme has been successfully used for, well, some examples:
- As a vehicle for teaching the fundamentals of programming, programming languages, and computers (see, for example, SICP and the courses based on it)
- As an extension language (in festival, for example)
- I've used Scheme for a few projects: a text processing language (transforming text with embedded code to multiple output formats), a sudoku-solver, a network simulator, an HTML-generation library. I've also used it to prototype algorithms that I later implemented in Java.
- Other people have implemented mail filters, web frameworks, wiki's, theorem provers, and probably countless other things in Scheme
And, of course, when you look beyond the standardized core language and consider the implementations, you can find some more impressive examples, like DrScheme (at least, I think that's written in MzScheme).
Please correct me if I got my facts wrong.
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!
DrScheme is written in MzScheme.
Please, for the good of Humanity, vote Obama.
You could define a ++ macro, but most Scheme programmers, who are functional programming zealots would consider it bad style.
Yes Scheme is a fine langauge for writing Windows gui programs, or at least it would be if it wasn't so hard to find a top notch development environment and professional cross platform gui toolkit that is ready to pick up and use.
You're thinking of Ruby and it's nothing, @, and @@ for local, class and global vars, but I don't remember which is which because I don't actually program in Ruby and I read this from the c2 wiki.
Please, for the good of Humanity, vote Obama.
Given the fact that Guile is more than just Scheme and that Scheme has very little traction as a scripting language, Guile is essentially "yet another scripting language" in competition with Python, Ruby and the rest. At one point there was a pipe dream that other languages would be compiled to Guile but that never materialized. In retrospect, Guile was a huge mistake for the FSF. Despite their long-term investment in Guile, Python is more popular for programming Gnome than Guile is. If the translation concept had worked then Guile might have been a reasonable project. But as your post demonstrates, since development actually began, Guile has essentially been just another Scheme implementation.
Look for DrScheme or Lisp in a Box, whether you want the Scheme or Common LISP flavor, respectively. DrScheme has Scheme tutorials among its help resources. I think Practical Common LISP (APress) is a good book for introducing LISP among those in print, and fortunately, I picked up Paul Graham's LISP books at the time they were current (and I was then looking to understand AutoLISP [once I was young]). Graham's texts are not for beginners, but once you get a sense of how Common LISP works, they provide perspectives that Practical Common LISP don't. I've been seeing used and out of print classic texts on LISP from the 90s showing up at the computing book store in Hollywood (CA); though once I see them, I grab them. "The Little Schemer" is also very illuminating about recursive thinking and bottom up problem solving; though it is written from a Scheme perspective, the approaches apply to LISP.
I also use KDevelop (multi-language) and the parentheses seem manageable. My experience suggest that getting lost in the parentheses is a sign to refactor.
I know do, but I hardly ever used it, since its pretty much unreadable, beside from that it just demonstrates the throuble with Scheme, in Ruby I can write (0..5).to_a in Python I can write range(0,5) in Scheme I have to write:
(do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i))
or
(let loop ((i 0)
(lst '()))
(if (vector (reverse lst))))
or similar equally obscure constructs. I know that I could fix it with a simple macro, but again, thats kind of the point, I have to fix all the flaws of the language myself, in Ruby and Python simple stuff works out of the box, in Scheme it doesn't.
How else would you write this?
for i in xrange(1,10):
print i
Sure I can sometimes use recursion, but for such simple jobs it really doesn't lead to readable code.
Yeah, GNU applications are mandated to be written in C, which (as a C programmer) I find a highly strange choice. They even expressly mandate against C++, which for appliction programming, while far from ideal, is worlds better than plain old C. So just because GNU mandates something doesn't necessarily make it a good idea. (like using a language that non CompSci students have difficulty with as the "end user" scripting interface).
First of all, that's not exactly a fair comparison -- You're using an xrange, and scheme does not have a similar thing out of the box (although there are certainly SRFIs that cover this). You're also omitting the function declaration -- You'd need this in Python (or similar), but not for the example. You do need it for the Scheme example though, which makes it look artificially long. Below is much nire fair way to compare a python-like language compared to scheme.
def numprint:
for i = 1 while i = 10:
print i
i = i + 1
numprint
(define (loop i)
(cond ((= i 10)
(display i)
(loop (+ i 1)))))
(loop 1)
Not really much of a difference, is there? It is still unbalanced because the Scheme function takes an argument, so it could count from 2, 3, 4, etc. Really, the python-like example should be this to match:
def numprint(n):
for i = n while i = 10:
print i
i = i + 1
numprint
In short, yes, the python version is much smaller... when you use high level constructions not available in R5RS. Big deal.
Sorry... those = things should be =. My key is rather stuck today.
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
First, Common Lisp does have a standard packaging/module mechanism. Moreover, there are a ton of libraries available for Common Lisp, as well as interfaces to C libraries.
A deep unwavering belief is a sure sign you're missing something...
How is 'do' any more obscure than '(0..5).to_a'. What the hell does the latter even mean?
A deep unwavering belief is a sure sign you're missing something...
... 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.
"Easy to read" and "Easy to write" are two entirely different things. Take the example of getting the 2nd element of the list 1, 3, 5, 7.
AppleScript is going to be something like "the second element of {1, 3, 5, 7}." Easy enough to read.
11:18: syntax error: Expected class name but found identifier. (-2741)
Let's try "the second item of {1, 3, 5, 7}".
3
So how was I supposed to know that the English word "item" works but the equally English word "element" doesn't?
Now let's try Lisp/Scheme: "(cadr (list 1 3 5 7))". I know that works, because that's practically the only way to write it. (The other ways are fully equivalent.)
3
To someone who's never seen Lisp or Scheme, "cadr" makes no sense. But there are only a few keywords like that in Scheme. And as far as syntax, the (almost) only special characters in Scheme are the two parentheses, and the space of course. You won't have to worry if a list uses braces or curly brackets. If a pointer-to-a-pointer-to-a-class uses *ptr->fn() or (*ptr)->fn() or (*ptr->fn)(). Whether a "char const *" has its address or value constant. Why "display dialog 'foo'" "button returned of the result" works but "button returned of display dialog 'foo'" doesn't. Whether it's "display dialog" or "show dialog", "button returned" or "button pressed". If you're allowed to use another indentation style without messing up your code.
It's very difficult in Scheme to write something you don't mean. That's why the parentheses are there. And if you indent approximately every parenthesis (except for, say, small math), it isn't that bad. Take this, for example. Let a, b, and c be numbers, and plus-or-minus be either + or -, depending on which result you want.Admittedly a little harder to read, but there's no way I messed up precedence, thanks to the prefix (Polish) notation and parentheses. Now if I had glomped the whole thing together as (/ (+ (- b) (sqrt (- (* b b) (4 a c))))), then yes, the parentheses would be a problem. But not properly indented like they are.
Or incf and decf.
My other car is a cdr.
Moderator hint: a comment is neither "Flamebait" nor "Troll" if it is true.
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.
Like COBOL, you mean ?
Although, curiously, GIMP uses the SIOD interpreter rather than GUILE.
BTW, RMS doesn't want to use GUILE for Emacs, which makes me wonder if he's serious about GUILE as the Gnu extension language.
Han-Wen Nienhuys -- LilyPond
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.
Huh, no kidding? Well, maybe you should tell reddit.com (one of Paul Graham's spinoffs). Because, you see, they switched from Lisp to Python a while back (sorry, I can't find the original block entry). And why? Because, to quote:
"If Lisp is so great, why did we stop using it? One of the biggest issues was the lack of widely used and tested libraries. Sure, there is a CL library for basically any task, but there is rarely more than one, and often the libraries are not widely used or well documented."
So, sure, there may be tons of libaries, but who wants a library that's poorly documented, poorly tested, and may not even work on your CL implementation?
Moreover, in addition to the ffi, things like sockets and threads, which I think we can all agree are important, aren't standardized across implementations, which just makes things harder.
Now, is this a problem with the language? No, it's the implementations. But the fact is that until you can grab a library off the net and drop it into your Lisp implementation of choice (assuming they've implemented the standard) and hit the ground running, it will, like Scheme, Smalltalk (my poison of choice), and others, remaining a niche language.
Unfortunately, this is, in part, also an issue of critical mass. Unless you can get people using your language, there won't be high quality, well tested modules for it. OTOH, Perl, Python, and Ruby have all proved it's possible... 'course, it helps that the people running those projects aren't sequestored in ivory towers (well, okay, Guido just deigns to join us down here, occasionally).
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.
That is because the great albatross of legacy EMACS code is written for elisp -- which is dynamically scoped instead of Scheme's lexical scope.
Weapons of Mass Analysis
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.
It's not such a simple dismissal. Reddit is an online, web-based program. Lisp is missing something in these areas, because Lisp is a pre-web phenomenon language. Most of the people who learned it did so before the web became a big thing, and as a result they're often not working in web-related tasks, and thus not creating web-related libraries. The same could be said for C, really. Python has better, easier-to-use, easier-to-drop-in libraries for a lot of web related programming than C, but that doesn't mean that C has unusably few libraries.
Whether Lisp has sufficient libraries for your task depends on your task. It also depends on your budget. A lot of the best Lisp libraries are in implementations like Allegro and Lispworks. Their price is very reasonable for a commercial outfit, but often out of the range of hobbyists and OSS programmers.
A deep unwavering belief is a sure sign you're missing something...
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).
See for example SISCweb.
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.
Isn't a central point of Scheme that it is properly tail-recursive, functional programming language? While I'm not all that familiar with Scheme, wouldn't you usually use a recursive function rather than a for-loop in such a language?
A for-loop isn't a task, its a construct that is natural in the idiom of certain languages for accomplishing certain tasks. Its not the idiomatic way to approach those tasks in all languages, though.
"You're also omitting the function declaration -- You'd need this in Python (or similar)"
not at all! it's a scripting language! You don't need no stinking declarations! ;)
anyway:
for-each is part of the R5RS standard library and of course can be fully emulated in terms of a recursive function wrapped in a macro for syntax sweetness...
you can also emulate python's print:
as well as a range function that returns a list:
so, now, you're code looks almost like Python's one
oh, ok, Scheme doesn't have a simple for, so let's do it:
so, again:
I could go on and on and end up having a completely customized Scheme.
With some effort, you can redefine for-each so that it takes iterators rather than lists. Or both. Remember, Lisp is a programmable programming language.
I don't feel like it...
And the name itself has a very interesting history.
In 1969, Terry Hewitt, at the MIT AI Lab, published a program called PLANNER, which used embedded high-level procedural knowledge to represent plans and used backtracking as a major source of control structuring. In the early seventies, McDermott and Sussman published CONNIVER, another program that used the concept of "continuations" to provide control flow. These systems comprised the proto-history of most logic programming languages, but were woefully innefficient. In the mid-seventies, Guy Steele (who was working with Sussman) constructed another system that embedded planning knowledge procedurally (including the retention of continuations), but could be implemented efficiently (thus the focus on proper tail-recursion, simplicity, etc.). Extending the various names given the previous system, he called this one "SCHEMER". The OS in use at the MIT lab at that time truncated filenames to 6 characters, so the user would type the command "SCHEME" (the "R" being elided by the file system) and the shortened name stuck.
That is all.
>>even trivial tasks like a for-loop you have to either code yourself or rely on non-portable extensions.
...
E C36
WRONG!
From the R5RS documentation:
'Do' is an iteration construct. It specifies a set of variables to be bound, how they are to be initialized at the start, and how they are to be updated on each iteration. When a termination condition is met, the loop exits after evaluating the s.
(do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i))
http://www-swiss.ai.mit.edu/~jaffer/r5rs_6.html#S
Of course you could write it via recursion, but does that lead to simpler or better readable code? I don't think so. Some problems are just linear in nature and a standard for loop is an easy and well readable way to solve those problems. Even Scheme has its for-each looping construct, since always using recursion would just get very ugly in such cases. However since Scheme lacks support for generators, function overloading or OO, the for-each construct stays rather limited, it doesn't even work with vectors, but only with lists, so you are kind of stuck.
One of the beauties of Scheme is of course that I now could start writing macros, functions and stuff to fix all those short commings. In Scheme a for-each construct isn't magic, its something you can easily build yourself. But for a lot of simple problems it just gets very annoying if you have to implement half the programming language yourself, which is the reason why I stopped using Scheme for serious work, its just requires more effort then worth it.
As you say, you could also do #(0 1 2 3 4 5). Also in languages like C/C++ or Java you have to do something equivalent to what you have to do in Scheme, and if you were doing it a lot in any of them, you would write a function to make it about as simple as in Ruby or Python. In Ruby or Python, there are a lot of nice shortcuts like this built in.
Second, about the ()s, are they really that much worse than (0..5).map{|i| -i*i}.sort.each{|i| puts i}? In there you have the (a..b) range notation, procedure calls via '.', and two closures with {...} enclosing the body and |...| enclosing the arguments.
Third, if you were using (display x) (newline) a lot, you should really (define println (lambda (x) (display x) (newline)), which simplifies the scheme examples to:
Which (ignoring the need to define the whole list outright) is not really any worse than the Ruby.
Finally, about the inside-to-out instead of left-to-right, its definitely different and it definitely requires a time of adjusting. I'm not sure whether I think its worse or not.
As the Americans learned so painfully in Earth's final century,free flow of information is the only safeguard against...
Better readable to whom? To someone whose spent 20 years mostly working with imperative languages? Clearly not. To someone whose spent the same time working with functional languages? Probably. To someone whose never touched a computer? Probably depends on the particular problem being addressed.
If you are calculating the Nth Fibonacci number, a recursive process (at least to me) seems more natural than an iterative one; 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.) At any rate, with a functional language its usually fairly easy to define both C-like for loops and for-each loops, though they often aren't core language function (though where they aren't, they are usually core library features.)
And, ultimately, I think that's the beauty of functional languages: control structures that imperative languages either have or don't have as language features become library features that you can define, add, and often extend as you need. If I want a more robust control structure in a functional language, I can build it like I would any other function. If I want a more robust control structure in C, my options are more limited.
There is a reason why popular languages like Java and C# are incorporating functional features: its hard to beat them for expressiveness and adaptability, which are important in practical applications. Scheme and other similarly "pure" languages (heavily focussed on one paradigm, more than is usually necessary or even desirable in many uses) are important, I think, both for their use in narrow domains, and for the concepts which migrate from them into more generally-used languages, not as general replacements for the more widely-used languages.
Yes, the () are much worse for that exact reason, in other languages you have =, {}, (), || and friends, all do their special jobs and are easy to spot with the eye, since they simply look different from each other. In Scheme on the other side everything is (), especially with macros this gets confusion since you can't even be sure how something will get evaluated or if it gets evaluated at all, you can't even tell if its a macro or a function without looking it up.
The throuble with Scheme isn't its lack of power, but simply that it has to much of it. Python, Ruby and Co. on the other side take a tiny bit of Schemes power and pack it into a human readable syntax, one that has dedicated syntax for dedicated constructs, an assignment looks different from a for loop and from a function call, in Scheme on the other side everything looks the same, only difference is the first name after the ().
To absolutly everybody, guess why Scheme has a for-each and why don't do a (let loop (l lst) ...code... (if (not (null? l)))(loop (cdr l))) each time you need to iterate over a list. Recursion is great for some algorithms, but lots of programming code aren't algorithms from the next best algorithm books, but simply library calls patched together. If I want to sort a list these days I write sort(lst) and don't start writing a recursive quick sort algorithm.
At any rate, with a functional language its usually fairly easy to define both C-like for loops and for-each loopsWhich again is my point, the throuble isn't that you can't define them, but that you have to define them. Scheme simply provides far to little in that area.
Yes, they are important, but mostly for academic study, I have yet to see a "pure" language that has any larger relevance in the real world.
Ah, sorry. Yes, I missed the operator there.
And yes, for someone who knows Scheme, your layout makes more sense. But the GP in particular said the parentheses turned him off - I'm just demonstrating that as far as the control structures, the parentheses in a Lisp-family language are essentially the same as other braces and the conventional indentation in an Algol-family language. It's just the GP's familiarity with indentation that makes him prefer that to parentheses.
thanks, man.
and if someone doesn't like the stupid name:
I don't feel like it...
It has nothing to do with zealotry. The reason mutable solutions should be avoided is because as long as you don't use them, your code is trivially parallelizable. The moment you mutate something, all code that touches that mutation must be run serially. This is a massive performance hit when you move your code to clusters and multi-core machines.
All machines are rapidly becoming multi-core, so this will become an issue very soon in every problem domain, not just high performance computing.
Will we see a
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?
;; Since you used a convienience function, I will define the same function ;; Now I will use it just like you did:
(define xrange
(lambda (min max f)
(cond ((min >= max) '())
(else (f min)
(xrange (+ 1 min) max f)))))
(xrange 1 10 display)