I found this hilarious. I am also rather underwhelmed (to be nice) with Nodejs and a little bothered at its wide adoption.
I have also been learning racket recently; my formal language and functional programming class uses it. I had some previous experience with common lisp but the raw nature of scheme still pleasantly surprised me a little bit.
EDIT: From what I remember, javascript was "inspired" by scheme. Obviously that when well...
The only thing Node has going for it is a lot of networking libraries. It's not fast, the async style leads to callback hell, JS is a terrible language, and their package management system is slow and bloated (or at least the packages are)[1]. Yet people think it's some new kind of thing that's just so awesome, despite MS doing server-side JS in the mid-90s.
Sure, people can use whatever makes them happy. I'm just confused as to why anyone is particularly impressed with Node or JS.
1: Seriously, to automate builds, I ended up saving the node_modules directory then symlinking it in to the build environment. Otherwise a simple CSS/JS/etc. grunt build took 15 minutes instead of 1. And it seems every module pulls in its own copy of dependencies, so you end up with 300-character deep paths. There's no apparent reason it needs to be like this.
"Node is popular because it allows normal people to do high concurrency servers. It's not the fastest or leanest or even very well put together - but it makes good trade offs in terms of cognitive overhead, simplicity of implementation, and performance. I have a lot of problems with Node myself - but the single event loop per process is not one of them. I think that is a good programming model for app developers. I love Go so much (SO MUCH), but I cannot get past the fact that goroutines share memory or that it's statically typed. I love Erlang but I cannot get the past the syntax. I do not like the JVM because it takes too long to startup and has a bad history of XML files and IDE integration - which give me a bad vibe. Maybe you don't care about Erlang's syntax or static typing but this is probably because you're looking at it from the perspective of an engineer trying to find a good way to implement your website today. This is the source of our misunderstanding - I am not an app programmer arguing what the best platform to use for my website--I'm a systems person attempting to make programming better. Syntax and overall vibe are important to me. I want programming computers to be like coloring with crayons and playing with duplo blocks. If my job was keeping Twitter up, of course I'd using a robust technology like the JVM. Node's problem is that some of its users want to use it for everything? So what? I have no interest in educating people to be well-rounded pragmatic server engineers, that's Tim O'Reilly's job (or maybe it's your job?). I just want to make computers suck less. Node has a large number of newbie programmers. I'm proud of that; I want to make things that lots of people use. The future of server programming does not have parallel access to shared memory. I am not concerned about serialization overhead for message passing between threads because I do not think it's the bottleneck for real programs." https://news.ycombinator.com/item?id=4310723
Low barrier to entry means anyone who used to animate jumping monkeys on webpages and now write distributed back-end systems (all web-scale of course).
Kids haven't seen anything else. Maybe took C++ or Java in college, then they see a kid with piercings and a messenger bag and tight pants talk about this awesome asynchronous callback-futures-based awesome frameworks. There is really nothing to compare it to. So they are impressed.
Next level up, write a benchmark. Compute fibonacci, compare with Java, hey not too bad. Even faster than Ruby! Clearly this is the framework of the future.
Maybe read some place about how threads are bad and callbacks are great. And so on.
> so you end up with 300-character deep paths.
Hehe, too funny. Actually it just needs to be 260 characters, so 'git checkout blows' up on your unfortunate colleague who happens to use Windows.
1. Using the same programming language on both sides of HTTP often appeals to a developers visceral sense of elegance, or tidiness, even though it this alone inherently solves no existing problems.
2. It's powered by Googles V8 engine, which means people already associate it with this "super fast JIT" thing that lives in their favourite browser. It must be efficient, right?
3. The crowned alternative is PHP which is slowly becoming Java and alienating, with no disrespect to anyone intended, the less experienced, the "let's just hack a script together" crowd.
There's no accounting for taste, but, it won't last. :)
As a result, when updating dependencies with npm, a same dependency is downloaded multiple times.
See http://blog.izs.me/post/1675072029/10-cool-things-you-probab...
Every package gets the correct versions for all of its dependancies at the cost of taking more hard drive space. Hard drive space is ridiculously cheap so it's a good trade off.
(And this is probably Lisp's greatest weakness as well – with this level of
possible diversity, everyone has to use the “common lowest denominator”
simply because nobody can agree on what alternative syntax / library / etc.
is better and should be used.)
Off-topic: it's not enough to give everyone opportunity to improve the
language; you have to choose the winners and promote them heavily. The rules
of free market don't work here; people won't use the best thing, they'll use
the one which is available out of the box and which is used by their peers.In the scheme world, the way they deal with this is by wasting a decade on making decisions about the language that should have been done in the 80s. The result is that scheme essentially split into scheme and racket. Now we have an awesome language and a nice little ecosystem thats good for teaching and research, and possibly even real work(tm). Classic scheme unfortunately is fragmented into implementations who all do things slightly differently and occupy their own niches.
In the clojure world, they have a) a BDFL who sets the course of the language. b) A very strong core community of very smart people who managed to build a nice culture based on common ideas about software and design.
In the common lisp world, because we have a very high-quality standard, implementations are almost completely compatible. Compatibility libraries make it easy to write very portable code, avoiding the scheme problem. At the same time implementations are free to experiment. The other problem of everybody developing their own little universe tends to be rare. Because common lisp is so old, we have a long history and traditions that guide future design, but don't constrict it. There is a subtle balance here. We have a lot of old examples to learn from, but we are not locked in by too many bad old decisions(not always the case, but good enough in practice).
A few examples where this does not work include utility libraries and things like json libraries, libraries for outputing html etc. Since we don't have a BDFL we are left to figure things out amongst our selves and sometimes, like with utility libraries(there are dozens such, which is ridiculus) it doesn't work. In other cases, it works very well, for example quicklisp, ASDF, bordeaux-threads, closer-mop, hunchentoot etc. are either de-facto standards, or sufficiently popular to be a very good default. As with clojure, there are a lot of common ideas about what is good design in the community, we have a lot of examples to learn from, as I mentioned.
In the end, at least in the case of common lisp and clojure, I see it as an advantage to have this "level of possible diversity", it's what has kept lisp alive for 50+ years! The fact that lisp can adopt to each new era of software development philosophy is a great reason to study it. It will be with us for many more decades because of this.
Adaption is neat, but I don't see this argument as being a very solid argument in favor of Lisp - it's just the norm.
Maybe what we need is to study the economics of software and come up with a system in which market outcome is promotion of good libraries. I think that the social/economic dynamics of software development play a huge role in building a successful product, both free and commercial. Has anyone studied the subject in greater detail?
On the other hand, such a language might just end up as an incredibly fragmented mess of different languages, with little interoperability between languages - some one makes a 'typed' version of the language, another takes that version and tunes it just enough for it to be incompatible with the original version and, in turn, all languages that are built on top of that original typed language, and so on.
Maybe incredible modularity is fundamentally at odds with (social) interoperability.
I agree that Common Lisp a very powerful language, but I can't live with all that power uncontrollably thrown on me. Common Lisp grossly lacks self-discipline and self-limitation when it's needed.
And it all goes back to car and cdr and the fact that they are (or rather were) embedded assembly language and there to give raw access to Lisp's linked memory model (as opposed to the sequential memory model of Fortran). A dotted pair has two efficiency advantages over a proper list and both stem from the fact that the last cell of the last pair of a proper list contains 'nil (or 'null in Racket).
Storing two values in a proper list requires two cons cells - the first with the first value and a pointer to the second cons cell and a second cons cell containing the second value and a null pointer. In contrast, a dotted pair holds two values in a single cons cell - halving the memory requirement.
The second advantage is that when there are only two values there's no need to walk the list and test for 'null (or 'nil) on the cdr. This saves an instruction step.
Philosophically, dotted pairs allow for car and cdr to be used symmetrically. Calling cdr on a dotted pair returns the second value directly just as calling car on any list returns the first value directly. Lastly, one of the things that is awesome about Lisp is the way in which lists can model data structures, and in the case of a dotted pairs their efficiencies are available to all those structures which consist of or rely on paired values.
Of course, this may be obvious and on a machine with 10+ GB of RAM not really applicable, but I find it fun to think about anyway.
Other than alists, simplifying the implementation[1], and tradition, I can't think of a good reason to keep improper lists and dotted pair notation around. They aren't really that useful, confuse newcomers, and encourage the use of inefficient data structures. I haven't written much Lisp, but when I see a dotted pair (usually in the context of an alist or some hideous approximation of a struct[2]), it comes across as a "code smell" to me, like "this person is taking SICP too literally."
[1] You pay for extra reader syntax, but gain uniformity in your treatment of the car and cdr cells. On the other hand, maybe you could optimize the cdr cell a bit if it only needed to address pairs and not arbitrary atoms.
[2]
;; barfage
(define (make-thing a b c) (cons a (cons b c))) ; barf
(define (thing-field-a x) (car x))
(define (thing-field-b x) (cadr x))
(define (thing-field-c x) (cddr x)) ; more barfcons is useful for dealing with linked memory structures, including singlely linked lists and particularly singly linked lists where the stored value in each list element is a pointer because it abstracts over all the pointers. Writing code to process and manipulate and pass pointers is idiomatic in C. Using pointers is idiomatic Lisp.
Your barfing code shows why cdr and car persist. They are also powerful abstractions due to their rules of combination and probably because their rules of combination aren't constrained by a bias toward English language. There ain't no synonyms for "caddr" and "cdddr" in English or Python.
The idea that lists should be everywhere replaced by structs is assembly in any language thinking (also known as C). Lists provide a standard interface. Each struct definition creates a new data type.
It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Alan J. Perlis, Epigram 9.
Also I have made use of improper lists in my code, usually when I need to do something with multiple values and I don't want to mess with multiple return values, or if I just want to create a list of tuples for some reason and don't feel like defining an actual struct (can't think of a good example right now...)
The cons pair is a a really powerful primitive data structure, it is the linked list of Lisp. You can build a lot of powerful structures given this great base. I really don't it matters what architecture your running on.
Do they really confuse people new to Lisp? car+cdr is a pretty simple concept, when talking to new Lispers they usually don't complain about this.
Hash Tables are part of the Common Lisp Hyperspec so that is a non issue, they have been first class citizens since before Clojure existed (and are nicely integrated into things like loop)
A good example would be simple key->value pairs. When working with Hunchentoot it represents HTTP Headers and form POSTs as a-lists. It would be easier to use a hash table though.
> (define (f g)
((car g)(cdr g)))
> (define my-val (cons sqrt 4))
> (f my-val)
4
...and go down the rabbit hole where code and data merge. (define (quicksort xs)
(if (null? xs)
xs
(let*
([hd (car xs)]
[tail (cdr xs)]
[smaller (filter (lambda (x) (< x hd)) tail)]
[bigger (filter (lambda (x) (>= x hd)) tail)])
(append (quicksort smaller) (list hd) (quicksort bigger))))
It's great to see a hugely superior implementation, and I love that people are writing about and using Racket like this because the more people do that the more resources there will be for people like me to learn from.I regret there's no Scheme or Clojure running on LLVM, which I think is a much better platform than the JVM. Julia, that resembles Dylan (another Lisp), is the perfect example.
I didn't know at the time that Racket was call-by-value.
Quote was confusing at first, yes.
Haskell does really create a big impact on some developers.
Just an anecdote ;)
Mostly, you need to have the data-structure it defines for the parse tree, and it's harder to convert than syntax->datum. Also there's the Q monad, to generate new safe names. But it works pretty well.
I suppose that makes Lisp the Principia Mathematica.
> Racket's default IDE is better than GHCi and probably on par with Emacs (you almost certainly can configure Emacs to be better than anything, but it's not trivial and people don't bother, while DrRacket provides autocompletion and documentation out of the box).
Last I used it (a few years ago), DrRacket was very laggy, so I would find it very hard to use for a serious project. YMMV, maybe it's improved.
I'm a Vim user and went through elaborate pains to get Emacs working almost like Vim just so I could go through SICP this way.
It's very useful for interactive debugging but dependent on the mouse and many Emacs key combinations simply cannot be mapped because of it's CUA interface...and so far as I can tell there is no key-combination that switches focus between the REPL pane and the Editor pane short of closing the other pane.
The syntax analysis gets to be a bit much too.
On the other hand, that's just the price of an IDE over a text editor, and for a batteries included IDE, it's pretty good even if I wish that the effort would have been put into Emacs support while knowing that doing so would not meet the needs of the PLT group's target audience of students.
tl;dr, open up the normal repl by typing "racket" and then type (require xrepl) and then ,install! (leading comma denotes an xrepl command).
Then when you want to interact with a file you can do racket -i $file to interact with it using xrepl.
If you're wondering why this isn't just the default, I think it has to do with the readline license.
racket -il xrepl
Will launch Racket with already xrepl loaded.But hey, I like people who call it like they see it, and there certainly are some drawbacks to Javascript too.
There is a Lisp-to-JS compiler (ParenJS). https://bitbucket.org/ktg/parenjs
But if I had to show one feature of Racked to make someone amazed, it wouldn't be any of those. It would be a simple program composed of a couple of files, and every file would start with different #lang. Like #lang racket, #lang lazy, #lang typed/racket, #lang datalog.
It's sufficiently mind-blowing that there are this many languages on top of Racket, but the real "killer app" is how seamlessly they integrate with each other.
The next thing I'd show would probably be Danny Yoo tutorial on how you can create even more languages like this: http://hashcollision.org/brainfudge/
"Tonight at 11: this rose gardener learns to plant tulips"