a thoughtful web.
Good ideas and conversation. No ads, no tracking.   Login or Take a Tour!
comment by Cumol
Cumol  ·  1768 days ago  ·  link  ·    ·  parent  ·  post: GNU Guile 3.0.0 released

I don't know what relevance this has. Can someone explain?

I am pretty new to the programming world. Just started writing R scripts (and my first package) about 3 years ago.

I haven't learned python yet (beyond a few simple things to get me through the odd jupyter notebook).

I am, however, using emacs on a daily basis. So lisp, and specifically, emacs lisp has been on my to-do list to learn.

Why do people like lisp so much? And what does it mean for a language to be a dialect of lisp (scheme)?





user-inactivated  ·  1768 days ago  ·  link  ·  

Guile 3 is interesting because of the JIT. They've been working towards it for over 10 years. Guile is made for embedding in applications written in other languages, particularly C, the way emacs lisp is embedded in emacs. It wants to integrate more tightly with the application its embedded in than Python or Lua do, and that imposes constraints that have historically made it very slow.

But you're asking about lisp in general. I'll talk about Common Lisp instead of Scheme, because I know it better.

So every dynamic language has a thing called a repl, but it's not the same thing as the lisp repl. It's only a little facitious to say this is a complete Common Lisp implementation (that only works with sbcl):

    (sb-ext:save-lisp-and-die "dumblisp" :toplevel #'(lambda () (loop (print (eval (read)))) :executable t)

read with no arguments reads the next lisp object from standard input. Type (+ 2 2) and you get a list containing the symbol '+', the integer 2, and the integer 2.

eval takes a lisp object and, well, evaluates it. The language isn't defined in terms of text, it's defined in terms of how eval interprets its argument. (eval (list '+ 2 2)) is equivalent to typing (+ 2 2) at the repl, (eval "(+ 2 2)") is not.

A corollary of that is transforming programs is just manipulating lists, and you can teach eval new transformations. So, if you really miss while loops from some other language, you can write

    (defmacro while (test &body body) `(do () ((not ,test)) ,@body))

And now

    (let ((i 0)) (while (< i 10) (incf i) (format t "~a~%" i)))

works like while in C. You've added while loops to your lisp. That's how most control structures are implemented. If you expand that expression all the way (unfortunately there's no builtin function to do it in CL) you'll get something involving TAGBODY, which is equivalent to a set of labels and gotos in C and translates straightforwardly into jump instructions when passed to the compiler.

Common Lisp's support for object oriented programming, the Common Lisp Object System, was originally implemented as a library using the same mechanism, and in fact most implementations just incorporated the library with minor optimizations rather than modifying their compilers when it was incorporated into the standard. Here's an implementation of Prolog in around 400 lines.

There's other stuff of course. A lot of us just like the syntax. For greybeards it's synonymous with functional programming. Image-based persistence is really cool. A high-level language that can be about as efficient as C if you care enough to give it hints is handy for projects where that matters. Convenient metaprogramming is the feature that really distinguishes lisps from other languages now though, things like garbage collection and strong but dynamic typing started with lisp but have since become ubiquitous.

If you're interested, Practical Common Lisp and Paradigms of Artificial Intelligence Programming (in that order!) are both great books to start with.

bhrgunatha  ·  1768 days ago  ·  link  ·  

Sorry in advance, I'm verbose as fuck and don't know your background.

    Why do people like lisp so much?

I think it boils down to two things but everyone has their own point of view.

1. Homoiconicity (see later)

2. The ability to change both the way the program is read and evaluated.

It might be apocryphal but people say Lisp is named due to List Processing.

At its heart what seems to most annoy people about it - all those parentheses - turns out to be one of its killer features. As bfv pointed out the programs you write are exactly the same representation as the lists used by the language. This is called homoiconicity. Since bfv already highlighted the REPL - Read , Eval, Print, Loop - I want to expand a bit on what I think is the heart of Lisp the read and eval parts.

This is only conceptual,high level and very handwavy so the actual details vary wildly and factually.

Most languages - whether compiled or interpreted - need a pre-processing step - called parsing (READ!) - that turns the text representation (your program) into an internal representation called an AST (abstract syntax tree). An interpreted language will take the AST and run it by interpreting it (EVAL!). That is convert the into the actions of the program you wrote. It might have to interpret parts of the AST over and over again and each time has to figure out what to do.

A compiled language analyses the AST much more deeply and converts it into a representation the machine itself can execute - the computer's own binary machine code. Since this is only done once (at the time of compilation) there is no need to keep or interpret the AST over and over again to take the actions as the program runs - the computer does that itself. That is why compiled languages are generally faster than interpreted languages - because the compiler takes more time analysing the AST to see how best to convert it into the binary instructions the computer itself executes. . Also the interpreter is a program itself which the actual machine is executing and that extra layer has a toll. Note there are 2 phases for both interpreted and compiled - I like the diagram's use of pre-processing and processing to represent that.

Back to Lisp.

Lisp also has separate pre-processing and processing phases. BUT (and this is one of those killer features) your program can hook into the pre-processing phase using Lisp's infamous macros. The pre-processing phase of Lisp transforms your program (which thanks to homoiconicity is a just a bunch of Lists) and converts them according to both the macros provided by whatever flavour of Lisp your using and the macros you wrote as part of your program. These transformation use the full power of language too and because your program is just a bunch of lists, it's really straightforward to transform them. Yes, the full power of the language means your macros can also use the other macros at the pre-processing phase too.

What's more you can hook into the parsing part of pre-processing which basically means you can invent your own syntax - even if it's not based on lists - as long as the end result of that is in the format (lists!) the rest of the language understands.

The processing phase then takes the transformed code (which are now legitimate lists your favourite Lisp flavour can understand) and executes it.

For example this cheeky monkey takes C source code during the pre-processing phase and transforms it into a list - (run-c-program <c source code>) then during the processing phase "run-c-program" shells out to compile and execute the C-program.

You might cry foul about now and say "but then lisp is interpreted" but actually the choice of compiled or interpreted is truly and deeply an implementation detail and some exceptional computer scientists have converted the whole process into one that uses the compiled model.

I'm not going to waste more time to cover what a JIT compiler is but this announcement is notable because Guile now has a JIT compiler.

    And what does it mean for a language to be a dialect of lisp (scheme)?

I think if it conforms to the above model then it's in the lisp family.

There are many decisions and tradeoffs when you design a language. I think perhaps the 4 most interesting lisp languages are Common Lisp, Racket, Clojure and Emacs Lisp.

A big difference between Scheme and most other Lisps is the macro system and Racket has extended the Scheme model to the point where the designers call it a language laboratory rather than a language. For example when you install racket - it includes Scheme (both standards R5RS and R6RS), Datalog (logic programming), Typed Racket (static type checking), Lazy Racket (lazy evaluation), Algol 60 - no seriously!, Scribble (a language for making documentation), Slideshow, and many others.