Monday, August 1, 2011

Teaching Javascript and other languages (Scheme, Python, Clojure, ...).

Recently, I started working more and more with Javascript, and it happened outside the browser with node.js.

So far so good, I love node.js and I will consider it as a strong alternative to twisted. I think that the language has some extremely interesting features which I would like to see in Java as well. I'm probably biased: after all, I'm cold towards inheritance to say the least. In fact, I'm quite used to doing encapsulation with closures in scheme, and the fact that in Javascript I can just return objects of functions solves the classical problem of having to return a single function[0].

So I was thinking about a discussion I had with some friends from the Europython (and Python Italia) organization group. Someone made a point about using Javascript as a teaching language. The idea has its merits: Javascript is everywhere. A browser and and editor is enough to get started with programming. Moreover, the language is quite nice: many interesting programming techniques can be taught in Javascript (and more easily than, say, C++).

Then I considered what I learned in "Javascript: The Good Parts". The subset of Javascript I'm using is roughly the one suggested in the book. And that is nice. I'm basically leaving out the bad parts. However, a student probably would meet those parts soon enough and probably would not have the wisdom to avoid the bad parts (some of them are "easier" in the short term and discipline is something often newbies lack).

Moreover, it would be naive to assume that Javascript would be "well" taught. Many languages are taught badly (and students are often not very receptive of good suggestions).

Somewhat, it reminds me of pre-standard C: the language itself had many nice features [as a domain specific language to manipulate von Neumann machines at rather high level], however, there were plenty of rough edges (I'm thinking about C Traps & Pitfals: nowadays, many suggestions are granted, but back then it wasn't really that common).

Scheme (Racket)

All considered, other languages need something to be installed. Racket/scheme ranks pretty high as a teaching languages. SICP is an excellent book and Racket offers an easy to install/easy to use (easy to debug/easy to *) environment. The language is small and orthogonal and has features and abstraction which are hard to find outside the Lisp world. However, some concepts need some mathematical understanding: probably it is ok to assume such maturity at university level, but not before. Moreover, everything which could be of interest (GUI, threads, web, whatever) is just bundled.

Python

Python on the other hand is easier to start with. The mathematical maturity needed is lower, the language is probably easier. The abstraction level is good and there are lots of interesting functionality in the standard library. IDLE is a decent basic IDE and it is probably easier to find open source projects to which contribute. I just love Python, so I may be biased. Moreover, Python is easy to use to script the system (both Unix and Windows) and I believe that using a language to automate common operations is a great way to increase programming skills [useful programs are just more fun]. Ruby essentially has the same merits and problems of Python.

Clojure

About Clojure, basically it depends on Clojure Box. Without it, setting up a (free) easy to use environment is not as easy (I have seen newbies finding hard to make differences between the OS specific command prompt (cmd.exe or bash) and a REPL -- in python, I'm not talking about clojure --). That is the kind of ease of use we must reach (something like racket or IDLE) and the only way to get it with clojure is clojure box. Still it is a somewhat more complex -- though more powerful -- environment. The other problem I found about teaching Clojure is that it is hard to do without some prior knowledge of Java and also the official documentation is somewhat not very newbie friendly -- lack of examples are a [pun intended] clear example of that --.

On the other hand, it has all the advantages of scheme (a part that the language is somewhat bigger) with some further merits (IMHO swing >> wx, easy stuff to use from the Java world). The dependency from Java leads me to think that Clojure is more an excellent second language to "teach programming" to people exposed only to Java (and probably to the "bad parts of Java"). In this case requiring prior knowledge of Java is not a weakness but a point of strength.

----

[0] ok, there are other solutions in scheme, still I quite like to return {function: function, functionB: functionB}

4 comments:

Anonymous said...

About your comment on swing and wx, I think racket has switched from wx to gtk, win32, cairo, pango and cocoa (http://blog.racket-lang.org/2010/12/racket-version-5.html).
Beside the underlying technology, racket has it's own API for the gui, that's not so bad IMHO.

PS: what's wrong in returning (values function functionB) or `((function . ,function) (functionB . ,functionB)) ?

Unknown said...

> About your comment on swing and wx, I think racket
> has switched from wx to gtk, win32, cairo, pango \
> and cocoa (http://blog.racket-
> lang.org/2010/12/racket-version-5.html).

Agreed. I did not check that out recently.

> Beside the underlying technology, racket has it's own
> API for the gui, that's not so bad IMHO.

No, the API is *not* bad at all. I'm just not sure if being /so/ object oriented is a plus or not. But that's a different story (I mean... I don't find surprising that GUI framworks born inside OO languages are object oriented... still, I would rather see something more declarative in Scheme).

I don't consider this a defect, though. OOP is widely accepted to make GUIs, is natural for that. Moreover, from a teaching perspective, being able to re-use acquired knowledge when using other languages is good.

> PS: what's wrong in returning (values function
> functionB) or `((function . ,function) (functionB .
> ,functionB)) ?

I've nothing against values in Racket. Unfortunately I have some issues in Gambit, in the sense that as far as I can see only values and call-with-values are supported and the "natural" let-values is not unless you somewhat import SRFI-11.

I understand that mentioning a perceived problem with gambit (that is not a problem) as a problem with racket is hilarious. This is related to a problem of mine: I really do not want to rely on racket to much and would rather use just scheme: it's only that racket is just easier to setup for a beginner (here I was considering the various languages for teaching to beginners).

The essential problem is that I did not choose a reference scheme implementation and stick with it (while I did that almost by default for Python and Clojure, and for Common Lisp I just chose SBCL).

Since racket fully supports various user friendly forms over call-with-values, that is really not an issue. And that is why I explicitly mentioned that "there are other solutions" (and values was just the first one which came to my mind).

The second one was something like

(send obj functionB args)

where obj was the value returned from the costructor which was basically a hash table and send a form. I somewhat prefer the second one in the sense that keeps all the functions together (so it is easier just to pass around obj and use "names" instead of using positions with values.

Still, the point is that Javascript solution is very straightforward and requires only minimal understanding of Javascript itself, while my second solution is basically building a minimal object model above scheme.

I do not believe that it is strictly necessary and I don't consider it a defect of scheme as a teaching tool -- on the other hand, building a simple object model is extremely instructive --. Still I quite like the way Javascript mixes functional and object oriented styles, which is unsurprising, as Javascript is a functional language with object oriented syntax. ;)

Anonymous said...

Hi,

(as a side note: Gambit has srfi-8 builtin for handling multiple values)

you seem to not consider my second solution, that is returning functions packed in an association list.
This is basically equivalent to the javascript solution of returning function in an hash-table. Obviously you could do the same in scheme, even in r5rs and even portably, thanks to srfi-69.

But I think that from a pedagogical point of view, an association list (or maybe just a list) is better than an hash-table, or at least it have the advantage of showing that with first-order functions you can return them packed in the most appropriate data structure.

I think the ubiquity of hash-tables in javascript are great for pragmatic reasons, but in a educational environment (and, IMHO, in high-quality production environments) it's important to choose the data structure that works better for the specific task.

Association list are often better than hash tables, in particular when the number of items is so small that the overhead of hashing make them slower than a linear access.
As you surely know better than me, students tend to be too religious about claims like "O(1) access time".

Unknown said...

After careful thinking, you are probably right.

I would just point out that the plain syntax of Javascript hash-table is what I found interesting. In this case the fact that it is a hash-table is relatively minor to the plain fact that the syntax is very intuitive.

Besides, I believe that any language should use the data structures it was built with. In scheme the list is almost ubiquitous and it's good to use it "almost everywhere" (besides, compilers have been written with this in mind). In Javascript "the thing" is the "object", which is this kind of hash-table.

I have mixed feelings about association lists. It is true that they are good with small inputs. However, as far as I know, typical real world of hash tables take this in consideration as well. I will make some experiments.

I was quite bitten when writing Prolog. In Prolog association lists are extremely popular and even though commonly available libraries offer various binary trees they remain the most popular choice. This essentially led to slow programs and the conclusion that Prolog is not efficient.

I understand this is not the case with Scheme.