Why Lisp is now an acceptable scripting language
fare.tunes.org
fare.tunes.org
But rather than read about me confess my love for Lisp, I recommend that people just pick a Lisp that looks fun and take it for a spin. You might find a lot to like.
How does it compare to using python for live coding?
How that compares to python, I'm not sure. But I don't think you can easily modify python code as easily as you can modify lisp programs.
When I was playing around with Franz Lisp some years ago, I saw this feature, and then soon after, I saw the Edit-and-Go feature in a new MS Visual Studio release. Think I read somewhere that it was ad{a|o}pted from Lisp.
I've done professional Python coding for 2.5 years, Ruby and JS for 7. I always believed that the REPL combined with those languages being dynamically typed can yield big advantages.
With a Lisp you can evaluate your code in real time in your editor. Also your app will re-load your code live without losing it's state. I've done that with Backends, Web Frontends and Mobile Apps. It's pretty awesome, not just compared to everything else^^
Take it for a spin and enjoy the ride(;
A good friend of mine works for one of those big consultancies, however, and they are currently rewriting the backend of the second biggest online retailer of the world and they are using Clojure, too^^
So either I'm getting too old for the sexy Startup business or Clojure is at the same time stable and fancy(;
It sounds great to say "live coding" and whatnot, but it turns out that you very quickly get the repl into an unknown state where you aren't sure what version of what has been eval'd, and what temporary cruft is lying around changing how your program behaves.
A number of times I would find out that a bug I thought I squashed was actually still there, just hidden by some temporary "what-if" I had eval'd. The only way to know is to clean the workspace and reload everything.
This was recognized as problematic by the clojure community and "Component" methodologies were developed, along with namespace refreshing. It would be far less useable if not for this.
However, in a live production system, a blanket reload of anything kind of defeats the purpose of keeping it running. I don't think I'd ever try loading code on a live production system unless all the alternatives were worse than possibly messing up the state of the system.
It's nice to have the repl for investigating what's going on in production, for inspection. But I avoid any code reloading in a live system.
Technically there's nothing about Lisp that makes it uniquely suited for this; it's just that people developing tooling for most other languages have a tragic lack of imagination. Smalltalk, Forth, Factor, and to a degree Lua all support the same styles idiomatically, while Racket is a lisp, but it discourages this style.
Something like IPython was how the REPL from Lisp Machines and Interlisp-D used to work.
Plus Lisp is a compiled language.
always glad to find another old Sawfish user. :)
(define (return x) x)
(define (! a b c) (not (b a c)))
(define (-- a) (- a 1))
(define (cout junk thing) (display thing))
(define << 'nothing)
;ignore this look at this
;........... ...............................................
(
define (__I_AM_HAPPY happiness) (
if(! happiness <= 0) (
begin ( cout << "happy happy joy joy";
) ( __I_AM_HAPPY (-- happiness);
) )
; else (
( return "yay! i'm done with 6.001 project";
)
)
)
; got that? now we can do this:
(__I_AM_HAPPY 10)Back I. The days of vacuum tube and electromechanical switches, when punch tape rules the world p, there were two languages: The Algorithmic Language, and the List Processor. The List Process, or LISP, to its friends was built around s-expressions. The other, also known as ALGOL, was built around a strange and unholy hybrid of text and formulae that only an insane mathematician would love, for almost every statement was a special case.
ALGOL syntax went on to rule the world.
Seriously kid. There are only two syntaxes, and procedural, structural, object oriented, aspect, imperative, and all the rest are immaterial to the syntax, for they refer to how programs are organized, not what keys you press on the keyboard. Don't they teach programming languages anymore?
Also, as someone who studies mathematics for fun, I disagree with your statement that a mathematician would love a system with lots of special cases. If anything, mathematicians are willing to pay a higher abstraction tax than most other people, just to unify special cases.
I think the Curry-Howard Correspondence is actually an intuitive result rather than a surprising one, and that any experienced programmer who understands formal proofs as well as they understand computer programming would eventually arrive at the Correspondence on their own, or at least a suspicion of it.
You've grossly oversimplified everything so nothing has useful meaning anymore.
I have no idea what you mean. Are you talking about lexical scope? If anything, determining the lexical scope of a variable is much easier from a syntax tree than from the original program text.
> A syntax tree is not syntax, it's a tree.
Pretty much all the literature on programming languages identifies “syntax” with “abstract syntax (tree)”. It is typically understood that textual concrete syntax exists primarily as a convenience for the programmer, because it's easier to edit, but the very first thing a compiler does is recover the abstract syntax tree, and from then onwards proceed as if the concrete syntax had never existed (except perhaps for error reporting purposes).
If you will, syntax is the vocabulary of the language. Also called "lexicon" and the syntactical tokens are sometimes called "lexemes". It's all about the text. Syntax is text. Or more technically, syntax is the format of the input.
Syntax trees are data structures that represent syntax, but they are not syntax. There are different types of syntax trees, too. The trees are built by the compiler, and never written by hand unless for exercise.
Syntax trees have already passed semantic analysis and contain context. They are much more than just syntax.
I'm pretty sure that most programming languages have their syntax defined in terms of two components: (0) a lexical specification [which lexemes are valid], (1) a grammar [how to arrange lexemes into well-formed programs]. And the latter tends to be richer and more complex than the former. So it isn't just, or even primarily about the individual lexemes.
> It's all about the text. Syntax is text. Or more technically, syntax is the format of the input.
Syntax is the (tree!) structure of utterings (declarations, statements, expressions, whatever). Textual representations of syntax just happen to be convenient to manipulate in a text editor.
> Syntax trees have already passed semantic analysis and contain context.
This is wrong. Syntax trees are the input for semantic analysis.
That's why we have syntax highlighting. Have you ever seen a tree in your syntax highlighting? I didn't think so. Do you have a tree when you have a syntax error? No, because it never gets that far. Syntax is independent and prior to your trees, the IR in a compiler doesn't HAVE to use trees, it can use whatever it wants. I see there is no point in continuing this, we are arguing over definitions. Not even sure what we are talking about anymore.
I'm honestly not. Also, let's try to keep this civilized.
> and it's the parsing of said syntax that _results_ in trees.
When did I say otherwise? As you say, parsing concrete syntax indeed results in abstract syntax trees. Then those trees are used as input for semantic analysis (type checking, data flow analysis, etc.). Parsing is not semantic analysis.
> Have you ever seen a tree in your syntax highlighting?
The whole point to syntax highlighting, as well as good code formatting practices, is to make it easy to see the abstract syntax tree from the concrete program text.
> Do you have a tree when you have a syntax error?
If you're using a modern production compiler, yes. Compilers don't stop at the first syntax error. They add the error to a list, and then continue trying to build the syntax tree.
> Syntax is independent and prior to your trees,
I'd argue otherwise. When designing a programming language, abstract syntax comes first. You can define the semantics of a programming language purely in terms of its abstract syntax, and only later come up with a concrete syntax for it.
> the IR in a compiler doesn't HAVE to use trees
IR is what the compiler's frontend hands out to the backend. Syntax trees are an internal data structure used by the compiler's frontend.
Take Vim or jEdit for instance (among the vast majority of others). When you want to make a color scheme, you do it by assigning colors to linguistic elements like "string literal", "character literal", "numeric literal", "boolean literal", "identifier", "comment", "keyword", etc. These elements are the sort of thing a lexical analyzer produces: a token type (in practice, lexers typically identify the associated lexeme as well). This is no coincidence, as most text editors with "syntax" highlighting use regular expressions to categorize and colorize the text. If you've studied any automata theory, programming language theory, and/or compiler construction, then you know that regular expressions are equal in power to finite automata and are therefore incapable of analyzing any syntax beyond the lexical for a great majority of programming languages. In fact, without a grammar of some sort beyond regular expressions, the "syntax" highlighting engine is provably incapable of actually understanding syntax.
I've heard that Scintilla's highlighter is fed parsing expression grammars to describe the syntaces of various languages (using Lua LPeg). Now that's a syntax highlighter -- but (perhaps unfortunately) it's the exception, not the rule.
So that's why you don't (often) see trees in your "syntax" highlighting, but instead see a string of tokens.
catnaroek is correct: source code as stored in text files is merely a linear string of characters. That is not syntax, per se; it's the parser's job to determine the (ostensibly tree-like) syntactical structure according to a collection of syntactic rules. The first result of parsing is the concrete syntax tree (also called a parse tree) deduced from the source text. This can be, and usually is, transformed into an abstract syntax tree (AST)[1] prior to semantic analysis. This is the case for every language; it's only so apparent in the Lisps because they belong to the tiny set of languages that explicitly specify their AST[2] -- for most languages, each translator has its own, possibly unique notion of abstract syntax for the source language.
So, back to your original question: what kinds of syntax exist beside "surface" syntax? Abstract syntax is one kind. You can think of the notion of compilers' internal representation (IR) as another kind, too.
[1]: Even in parsers that produce the AST directly, the concrete syntax tree is still constructed, but it's implicit in the process's state rather than explicit in a data structure.
[2]: See, for example, "The Revised^n Report on the Algorithmic Language Scheme" (RnRS). It specifies not only the concrete syntax (called the external representation in the Report), but, to the extent to which it's reasonable to do so, it specifies the abstract syntax as well (the internal representation per the Report, or even just data in some cases). It's this specification of the abstract syntax that enables the syntactic macros for which the Lisps are so renowned -- syntactic macros are essentially an API for manipulating the AST at compile-time.
note that I didn't say procedural programming is cargo cult, I said cargo cult programming often results in procedural style programs
> “Procedural programming is a programming paradigm, derived from structured programming, based upon the concept of the procedure call. Procedures, also known as routines, subroutines [emphasis mine], or functions (...)” [https://en.wikipedia.org/wiki/Procedural_programming]
Procedural programming is all about decomposing programs (descriptions of tasks) into subroutines (descriptions of subtasks).
Maybe you were thinking about imperative programming instead? Procedural programs are certainly imperative, but not all imperative programs are procedural.
[0]: https://github.com/roswell/roswell
https://web.archive.org/web/20160311062403/http://fare.tunes...
2. http://webcache.googleusercontent.com/search?q=cache:LWgWzFI...
I am also very interested in xonsh. I am increasingly of the opinion that as useful as a package manager would be like {bash,zsh} compliant shells written in host languages that allow you to merge bashisms/Unixisms into your host lang and vice versa.
I started looking into this, specificaly with Haskell. I founde chrisdone's Hell and tangentially turtle. Combining those would be insane!
IIRC, schs uses the MIT48 or a classic Scheme variant specific to it, and I do not know if there is anything "built" on top of that. Where as least other esoteric schemes (Chicken, Gambit, Gauche, etc.) are full environments people can code in, in theory.
Guile is fascinating to me because this concept, subsumed into Emacs (a la the guile->elisp super project), means a very interesting idea that I think moves beyond the IDE. Some people here will poo poo that, but I find it very interesting as I have moved away from tmux and screen and into Emacs as my combined editor AND terminal manager. I know I know, where's the Unix philosophy.
But, I like this. Soon I will play with this stack on top of Guix, and well, that is more Lisp Machine that I should be allowed to have.
In LISP, the common interface between functions in the list rather than text. To encourage data flow programming and discourage recusion, map, filter, and fold are provided.
JavaScript is in some sense an impure functional language, however hastily designed in various places.
In the context of CL, the canonical example would be "macros". But CL has a bag of other features that makes it appealing.
Most languages that are identified as some kind of Lisp are strictly evaluated, do not support partial evaluation (explicit currying only), and support mutable variables and mutable aggregate objects.
I really disagree with the 21st century fashion to make everything that isn't a Miranda derived language as not being a FP language.
The advantage that I see in switching to Lisp is that it's a nice, pleasant, powerful language in which to work. The standard is well-thought-out. The abstraction capabilities are wonderful.
Compared to JavaScript, there's no comparison:-) Just off the top of my head, Lisp has integers, ratios (e.g 1/2), big integers (e.g. (expt 2 2048) → a number so large it breaks the HN page wrap) and complex numbers (e.g. (sqrt -3) → #C(0.0 1.7320508)).
Lisp's error-handling system of conditions and restarts is profoundly powerful: one part of code hand signal an error, another can determine how to fix it, and execution can resume without unwinding the stack (as opposed to exceptions, where by the time a higher level of code determines what to do the function encountering the problem has already returned). Technically, one could implement this in JavaScript (it's all based on first-order functions), but since JavaScript doesn't have macros it'd be pretty hideous.
Which raises the point of macros, which are enabled by the uniform syntax. They're a powerful tool for syntactic abstraction, just like functions are a powerful tool for procedural abstraction. A language without macros is almost as primitive as one without functions.
There are also read-macros, which enable one to customise or even replace the parser (called a reader in lisp). Don't like Lisp? That's okay, you can write a reader which parses JavaScript and returns Lisp objects to be evaluated or compiled! Then there are compile macros, which enable compile-time optimisation. And there are symbol macros, which can make what looks like a variable access actually able to perform any operation. Imagine being able to write 'foo = bar' in JavaScript, and having that assignment be logged. In Lisp, you can do that.
One big advantage is that all of these macros systems are … just Lisp. They're not special: you can do anything in a read macro that you can do in your code. You don't have to learn a special syntax or a limited subset of the language: it's all there if you need it.
Then there's the object system (which is enabled by macros). CLOS is the most powerful, most convenient object system with which I'm familiar. JavaScript methods belong to a prototype, which means that in foo.bar(baz) only foo determines what bar is; in Lisp, methods are multimethods, and may be specialised on any and all arguments: (bar foo baz) could do different things depending on the type of foo, the type of baz or the types of both.
Places are an amazing concept. In most languages there's no way to say 'foo(bar) = baz', but in Lisp one can do (setf (foo bar) baz) with the right setf functions and it Just Works.
Lisp has optional type annotations, which can lead to extremely fast code.
Then there are little things like well-thought-out equality predicates, cleaner-looking code &c.
The advantage of JavaScript over Lisp is that because it is installed everywhere it has a much, much, much larger community, which means more libraries, wider support. But install base and community size are honestly the only real advantages I can think of.
Clojure solves that.
I think that gives the design of Clojure too little credit. I can imagine MUCH worse ways to do it than Clojure's well-designed Java interop.
Lisp is a homoiconic meta-language, and this is the only thing that matters, not all that functional-schmunctional crap.
Scheme rethought a number of things with the experience of a couple of decades in developing languages, and Common Lisp included one of its breaking changes, lexical scope (I think Gnu Emacs Lisp and AutoCad's AutoLISP are the only widely used dialects with dynamic scope, and they're obviously not primarily language implementations).
For example, following the little schemer books, they don't print scheme as the actual ascii you need to type, instead they use typography like bold, italic and greek symbols that you have to replace with the uglier reality to distinguish between things like s-expr from lists. It feels like they are embarrassed by the cludgy real syntax, so they hide it and push the difficulty on to the reader.
To give clojure a whirl, I was prepared for lispy paren extravagance but when translating a small program to my best guess at idiomatic clojure (I'm sure I failed) there were so many non-s-expr syntactic special cases for things like list comprehensions and common macros that the result felt like it was jealous of non-s-expr languages and didn't actually want to be a lisp.
In truth, I believe a strong contrast between different syntactic elements helps readability, so I don't think the s-expr regularity I expected is something I actually want.
My main complaint with Scheme is that it's hard to distinguish a "call" from a plain list. For example, in `(let ((foo 1) (bar 2)))`, `let` is a call, but `foo` and `bar` are just being assigned, not called. Clojure uses `(let [foo 1 bar 2])`, so you can tell that `let` is a call, but `foo` and `bar` are now unwrapped and placed inside square brackets to signify plain data, not a call.
> To give clojure a whirl, ... there were so many non-s-expr syntactic special cases for things like list comprehensions and common macros
Everything is still an s-expr. The square and curly braces still follow the same rules, but they just mean they are not "calls" like parens would normally signify.
For a list comprehension example: `(for [m messages] (str m))`, `for` and `str` are calls and `[m messages]` is just data. It could be unwrapped like `(for m messages (str m))`, but it's wrapped in square brackets as a visual indicator for what's important. But if it was wrapped in parens like in Scheme, it could be confused as a "call".
Clojure is very well-designed to solve visual problems of Scheme and other Lisps. I recommend looking at it again :D
CL is, and always has been, about getting stuff done and isn't above having some warts, especially historical warts.
Scheme was more academic and elegant, but not great for real world use until some implementations "fixed" that. Racket, for instance, isn't quite a proper Scheme any longer, but is definitely practical.
Clojure looks interesting, but I have absolutely no need or desire to use a JVM-based language.
Who wants simplicity? People learning the language, so they have less to learn and a lower cognitive overhead when starting out. People teaching the language, so they have less work. Language implementors, so they have a much shorter period of time between prototype and release of a language implementation.
Who doesn't want simplicity? People who've already learnt the language and are proficient with it and use it regularly, having grown accustomed to the features it provides that can't be properly added afterwards as macros or libraries.
As for Clojure? I'd like to get around to taking a serious look at it someday, but I do somewhat emotionally & irrationally blame it for stealing the momentum of the Lisp resurgence of the late 2000s. It probably has some good ideas, but it's also pretty radically different from Lisp, which is (under the parentheses) a fairly traditional language.
Looking at the download page, it looks like the OSX version is 32-bit only: "It should compile and run on 32-bit GNU/Linux, FreeBSD, Mac OS X (Darwin) and Cygwin/Win32 distributions, and on 64-bit GNU/Linux systems." Reading through the INSTALL file seems to confirm this. [0]
A while ago (2013), some one seems to have gotten a 64-bit version up and running on OSX through make emu (don't know what the emulation option exactly is, so I can't say if it is relevant). [1]
[0] = https://github.com/evanrmurphy/PicoLisp/blob/master/INSTALL [1] = http://picolisp.software-lab.narkive.com/oCd1TRXz/pre-compil...
Just use Common Lisp. I use SBCL. Read Practical Common Lisp [1] &/ ANSI Common Lisp [2]. Skim the CLQR [3].
If you're just starting out, you can get away with just using the REPL. You might get that ^]]A crap when you up-arrow to previous expressions, so install rlwrap and run 'rlwrap sbcl' instead of just 'sbcl'.
Once you get tired of that and want to use an editor, get Emacs (on OS X: see [4], on Ubuntu: apt-get install emacs24). Install SLIME [5] through package-install to send expressions down from a .lisp file to the REPL (with C-x C-e).
If you get stuck, try looking for youtube videos (or add an email to your HN about).
[1] http://www.gigamonkeys.com/book/
[2] http://www.paulgraham.com/acl.html
[3] http://clqr.boundp.org/download.html
I highly recommend using Racket with the DrRacket IDE and Neil van Dyke's SICP package from the Planet package repository. It's the easiest way to get up and running with an editor and a REPL, and it's got support for the "picture language" in chapter 2.
You could also give it a shot with MIT Scheme -- I've got a feeling it's the implementation SICP (2e) was developed with. It's also got a built-in Emacs-like editor called EdWin. Launch the MIT Scheme REPL and type `(edwin)` to get at the editor.
GNU Emacs has an extension called Geiser (available in MELPA) that works well to integrate with Racket and Guile. Otherwise, the cmuscheme package that ships with Emacs gives you rudimentary REPL integration with just about any Scheme interpreter (and quite a few Schemes, such as Gambit, come with packages to better integrate with cmuscheme and scheme-mode).
If you're not an Emacs user and you're not willing to invest the hour or so it takes to get a handle on the basics, you can, of course, use any editor you want and use the `load` procedure (which, iirc, was omitted from IEEE, but most Schemes have it) at the REPL to import definitions from a file (or there's always good ol' copy+paste).
If you want to learn Common Lisp rather than Scheme, you can check out "Successful Lisp" by David Lamkins and/or "Practical Common Lisp" by Peter Seibel, both of which are freely readable on the Web. You'll also want to keep the ANSI Common Lisp HyperSpec by Kent Pitman handy as a reference (also free on the Web). For this, you can grab virtually any ANSI Common Lisp implementation (GNU clisp, SBCL, Clozure CL, ABCL, etc.). GNU Emacs has excellent support via the SLIME package (available from MELPA), or the other-editor with the `load` function will work here, too.
If you want to learn Emacs Lisp, GNU Emacs comes with an introductory programming book that uses Emacs Lisp, "An Introduction to Emacs Lisp Programming" by Bob Chassel, and the Emacs Lisp Reference Manual. As you might guess, GNU Emacs has the best editor support for Emacs Lisp, though Vim at least has syntax highlighting for Emacs Lisp.
If you want to learn Clojure, the only freely-available book I'm aware of is "Clojure for the Brave and True" by Daniel Higginbotham, though I'm sure there are others. MELPA has the CIDER package available for Clojure support in GNU Emacs. I'm not familiar enough with Clojure at this point to know what the equivalent to `load` is, though.
At this point, I've pretty much exhausted my usefulness on this matter. If you want to learn T, ISLISP, EuLisp, Le Lisp, Arc, or any other Lisp dialect for that matter, I can't help you.
After all that, I highly recommend starting with SICP. It's really a great introduction to programming, and it also covers quite a few concepts that will likely be new to you even if you're already a programmer (mostly in chapters 3-5). (Disclaimer: although I like most of the Lisp dialects, my favorite is absolutely Scheme, so I may be biased ;)). Pick an (IEEE or R5RS) Scheme interpreter, have a glance over its documentation, and you should be good to go!