What does lisp "list" function/keyword do? - list

I've seen it in An Introduction to Programming in Emacs Elisp, section 4.4.1:
(interactive
(list (read-buffer
"Append to buffer: "
(other-buffer (current-buffer) t))
(region-beginning)
(region-end)))
It's surprisingly hard to Google what "list" means in lisp, because every page is a hit. I'm guessing it just makes a list of its arguments, in which case, why is it better or clearer to use than just making a list "manually" with another set of parentheses?

That's because you cannot just use "another set of parentheses" to make a list.
Here's an expression that creates a list containing the numbers 1, 2, 3, 4.
(list 1 2 3 4)
However, you cannot use (1 2 3 4) or even ((1 2 3 4)) to express that.

list is an essential function that is shared between all major dialects of Lisp. While not itself being a primitive it's actual implementation is the ability of the implementation to have varargs. It has been around since LISP 1.5 from 1962 (page 47) (In 1960 LISP1 had it as a special form (page 121))
Its implementation in Common Lisp and Emacs Lisp is:
(defun list (&rest args)
args)
Emacs Lisp cannot replace it with syntax since it doesn not have reader macros but Common Lisp can introduce new syntax in place of writing (list ..). An example how to do it in Common Lisp:
(defun read-list (stream char)
(declare (ignore char))
(cons 'list (read-delimited-list #\} stream t)))
(set-macro-character #\{ #'read-list)
(set-macro-character #\} (get-macro-character #\)))
{(+ 4 5) 'test {1 2 3}}
; expands to: (list (+ 4 5) 'test (list 1 2 3))
; ==> (9 test (1 2 3))
The result would be confusion for other lispers when they get introduced to the code so it really have to be very valuable to counter the drawback. Clojure has introduced special syntax for arrays and maps but still use list to make lists.

Related

Clojure Core function argument positions seem rather confusing. What's the logic behind it?

For me as, a new Clojurian, some core functions seem rather counter-intuitive and confusing when it comes to arguments order/position, here's an example:
> (nthrest (range 10) 5)
=> (5 6 7 8 9)
> (take-last 5 (range 10))
=> (5 6 7 8 9)
Perhaps there is some rule/logic behind it that I don't see yet?
I refuse to believe that the Clojure core team made so many brilliant technical decisions and forgot about consistency in function naming/argument ordering.
Or should I just remember it as it is?
Thanks
Slightly offtopic:
rand&rand-int VS random-sample - another example where function naming seems inconsistent but that's a rather rarely used function so it's not a big deal.
There is an FAQ on Clojure.org for this question: https://clojure.org/guides/faq#arg_order
What are the rules of thumb for arg order in core functions?
Primary collection operands come first. That way one can write → and its ilk, and their position is independent of whether or not they have variable arity parameters. There is a tradition of this in OO languages and Common Lisp (slot-value, aref, elt).
One way to think about sequences is that they are read from the left, and fed from the right:
<- [1 2 3 4]
Most of the sequence functions consume and produce sequences. So one way to visualize that is as a chain:
map <- filter <- [1 2 3 4]
and one way to think about many of the seq functions is that they are parameterized in some way:
(map f) <- (filter pred) <- [1 2 3 4]
So, sequence functions take their source(s) last, and any other parameters before them, and partial allows for direct parameterization as above. There is a tradition of this in functional languages and Lisps.
Note that this is not the same as taking the primary operand last. Some sequence functions have more than one source (concat, interleave). When sequence functions are variadic, it is usually in their sources.
Adapted from comments by Rich Hickey.
Functions that work with seqs usually has the actual seq as last argument.
(map, filter, remote etc.)
Accessing and "changing" individual elements takes a collection as first element: conj, assoc, get, update
That way, you can use the (->>) macro with a collection consistenly,
as well as create transducers consistently.
Only rarely one has to resort to (as->) to change argument order. And if you have to do so, it might be an opportunity to check if your own functions follow that convention.
For some functions (especially functions that are "seq in, seq out"), the args are ordered so that one can use partial as follows:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [dozen (range 12)
odds-1 (filterv odd? dozen)
filter-odd (partial filterv odd?)
odds-2 (filter-odd dozen) ]
(is= odds-1 odds-2
[1 3 5 7 9 11])))
For other functions, Clojure often follows the ordering of "biggest-first", or "most-important-first" (usually these have the same result). Thus, we see examples like:
(get <map> <key>)
(get <map> <key> <default-val>)
This also shows that any optional values must, by definition, be last (in order to use "rest" args). This is common in most languages (e.g. Java).
For the record, I really dislike using partial functions, since they have user-defined names (at best) or are used inline (more common). Consider this code:
(let [dozen (range 12)
odds (filterv odd? dozen)
evens-1 (mapv (partial + 1) odds)
evens-2 (mapv #(+ 1 %) odds)
add-1 (fn [arg] (+ 1 arg))
evens-3 (mapv add-1 odds)]
(is= evens-1 evens-2 evens-3
[2 4 6 8 10 12]))
Also
I personally find it really annoying trying to parse out code using partial as with evens-1, especially for the case of user-defined functions, or even standard functions that are not as simple as +.
This is especially so if the partial is used with 2 or more args.
For the 1-arg case, the function literal seen for evens-2 is much more readable to me.
If 2 or more args are present, please make a named function (either local, as shown for evens-3), or a regular (defn some-fn ...) global function.

Clojure, unquote slicing outside of syntax quote

In clojure, we can use unquote slicing ~# to spread the list. For example
(macroexpand `(+ ~#'(1 2 3)))
expands to
(clojure.core/+ 1 2 3)
This is useful feature in macros when rearranging the syntax. But is it possible to use unquote slicing or familiar technique outside of macro and without eval?
Here is the solution with eval
(eval `(+ ~#'(1 2 3))) ;-> 6
But I would rather do
(+ ~#'(1 2 3))
Which unfortunately throws an error
IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing clojure.lang.Var$Unbound.throwArity (Var.java:43)
At first I thought apply would do it, and it is indeed the case with functions
(apply + '(1 2 3)) ; -> 6
However, this is not the case with macros or special forms. It's obvious with macros, as it's expanded before apply and must be first element in the form anyway. With special forms it's not so obvious though, but still makes sense as they aren't first class citizens as functions are. For example the following throws an error
(apply do ['(println "hello") '(println "world")]) ;-> error
Is the only way to "apply" list to special form at runtime to use unquote slicing and eval?
Clojure has a simple model of how programs are loaded and executed. Slightly simplified, it goes something like this:
some source code is read from a text stream by the reader;
this is passed to the compiler one form at a time;
the compiler expands any macros it encounters;
for non-macros, the compiler applies various simple evaluation rules (special rules for special forms, literals evaluate to themselves, function calls are compiled as such etc.);
the compiled code is evaluated and possibly changes the compilation environment used by the following forms.
Syntax quote is a reader feature. It is replaced at read time by code that emits list structure:
;; note the ' at the start
user=> '`(+ ~#'(1 2 3))
(clojure.core/seq
(clojure.core/concat (clojure.core/list (quote clojure.core/+)) (quote (1 2 3))))
It is only in the context of syntax-quoted blocks that the reader affords ~ and ~# this special handling, and syntax-quoted blocks always produce forms that may call a handful of seq-building functions from clojure.core and are otherwise composed from quoted data.
This all happens as part of step 1 from the list above. So for syntax-quote to be useful as an apply-like mechanism, you'd need it to produce code in the right shape at that point in the process that would then look like the desired "apply result" in subsequent steps. As explained above, syntax-quote always produces code that creates list structure, and in particular it never returns unquoted expressions that look like unquoted dos or ifs etc., so that's impossible.
This isn't a problem, since the code transformations that are reasonable given the above execution model can be implemented using macros.
Incidentally, the macroexpand call is actually superfluous in your example, as the syntax-quoted form already is the same as its macroexpansion (as it should be, since + is not a macro):
user=> `(+ ~#'(1 2 3))
(clojure.core/+ 1 2 3)

Why are apostrophes needed for a list with conj in Clojure?

In closure, if you run the following code :
(conj (1 2 3) 4)
It'll error and say :
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn
However, if you place an apostrophe before the list, it'll run fine
(conj '(1 2 3) 4)
My question is, what exactly does this apostrophe do, and why is it actually necessary for this list?
I've noted that this doesn't happen when you use a vector
(conj [1 2 3] 4)
In fact, even if you use an apostrophe (conj '[1 2 3] ), it'll still work fine and return the same result.
What exactly does the apostrophe do, and why does a list require it, but a vector does not? (Apologies if this is a beginner/stupid question, I'm still learning. A thorough answer would be greatly appreciated)
There is a fundamental difference betweeen list (the parentheses) and vector (the brackets), that lists are used for function calls, and vectors are simply vector literals.
An apostrophe is a reader macro for the quote, that suppresses evaluation. E.g., (+ 1 2) is evaluated to a 3 when read in from source file, but '(+ 1 2) is read in as a list, or clojure.lang.PersistentList, conforming to the idea of code is data in lisp. On the other hand, quoting a vector is simply the vector itself.
So in your case, (conj (1 2 3) 4) when read in, it will try to call a function named 1 with arguments 2 and 3, no wonder it fails. But a quoted list, or a vector can be appended with an extra element.

Easily adding trace statements to LISP dialects (without using do for example)

When one is programming in an imperative programming languages such as Java one can conveniently add trace statements. For example:
for (int i=0; i<10; i++) {
// do something
// do something
System.out.println("Some trace statement");
// do something
}
How does one accomplish this in a LISP dialect such as Clojure - for example say I wanted to add a trace just before recur:
(def fact
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
;; say I want to add a trace here
(recur (dec cnt) (* acc cnt))))))
Notes:
The method should be relatively as simple as adding a line
For example if I were to use a do block -- I have to reformat, make sure I close the brackets appropriately
Non-invasive tracing
Lisp environments generally provide interactive debugging environment and trace mechanisms.
For example, in SBCL, you could use the trace macro:
you don't even need to modify your code, like you did in your Java example.
For Clojure, look at the tools.trace library, or the following answer: clojure: adding a debug trace to every function in a namespace?
Custom functions and macros
See also the many answers to this question: Debugging in Clojure?
Most of them involve nesting the expression you want to debug/trace inside another expression, like Chiron suggested.
I don't think that "I have to reformat and close the brackets appropriately" is a good argument; everytime you edit your program you have to deal with the syntax, or else you won't ever modify your code.
Paredit
I personally don't use I am now a happy user of Paredit. Your editor keep track of parens and brackets while you code, which is quite handy.
Reader macros
I you really don't want to nest your expression inside another one, I suppose you could write a reader macro so that you could annotate an expression with a debug statement, but this is overkill, imho (edit: this is what spyscope does, apparently; see NielsK's answer).
The Spyscope library provides a simple option for putting in trace prints without having to change the original syntax, just in the way you (and many others) prefer.
spyscope.repl=> (take 20 (repeat #spy/p (+ 1 2 3)))
6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
There's also ways of including trace messages
spyscope.repl=> #spy/d ^{:marker "triple-add"} (+ 1 2 3)
spyscope.repl$eval3935.invoke(NO_SOURCE_FILE:1) triple-add (+ 1 2 3) => 6
6
and even (partial) stack traces
spyscope.repl=> (take 20 (repeat #spy/d ^{:fs 3} (+ 1 2 3)))
----------------------------------------
clojure.lang.Compiler.eval(Compiler.java:6477)
clojure.lang.Compiler.eval(Compiler.java:6511)
spyscope.repl$eval675.invoke(REPL:13) (+ 1 2 3) => 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
Use do block:
(def fact
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(do
(println "**")
(recur (dec cnt) (* acc cnt)))))))
user=> (fact 4)
**
**
**
**
24
In your REPL:
(doc do)
do
(do exprs*)
Special Form
Evaluates the expressions in order and returns the value of
the last. If no expressions are supplied, returns nil.
Please see http://clojure.org/special_forms#do
I have several macros that allow something like this (because it is handy not having to toss in a progn many times):
(my-trace ("hi mom" 1 2 3) recur (dec cnt) (* acc cnt))
my-trace simply expands into:
(progn
(imperative-trace "hi mom" 1 2 3)
(recur (dec cnt) (* acc cnt)))
a simpler variant is:
(echo hi-mom recur (dec cnt) (* acc cnt))
which captures the result of the wrapped form and prints it tagged with hi-mom.
A nicety is that I can insert/remove nil as the unevaluated first parameter to turn tracing off/on. On some debug out put I just leave the trace in place but disabled until the next time I need it.
-hk

LISP very simple list question

Im learning lisp and im pretty new at this so i was wondering...
if i do this:
(defparameter *list-1* (list 1 2))
(defparameter *list-2* (list 2 3))
(defparameter *list-3* (append *list-1* *list-2*))
And then
(setf (first *list-2*) 1)
*list-3*
I will get (1 2 1 4)
I know this is because the append is going to "save resources" and create a new list for the first chunk, but will actually just point to the second chunk, coz if i do:
(setf (first *list-1*) 0)
*list-3*
I will get (1 2 1 4) instade of the more logical (0 2 1 4)
So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?
One defensive tactic is to avoid sharing structure.
(defparameter *list-3* (append *list-1* *list-2* '()))
or
(defparameter *list-3* (append *list-1* (copy-list *list-2*)))
Now the structure of the new *list-3* is all new, and modifications to *list-3* won't affect *list-2* and vice versa.
The append function has to make a copy of its first argument, to avoid modifying existing data structures. As a result, you now have two list segments that look like (1 2 ...), but they're part of different lists.
In general, any list can be the tail of any other list, but you can't have a single list object that serves as the head of multiple lists.
You have to think of lists in terms of cons cells. When you define list 1 and list 2, it is like:
(defparameter *list-1* (cons 1 (cons 2 nil)))
(defparameter *list-2* (cons 2 (cons 3 nil)))
Then, when you append:
(defparameter *list-3* (cons 1 (cons 2 *list-2*)))
Basically, a cons cell consists of two parts; a value (the car), and a pointer (the cdr). Append is defined to not change the first list, so that is copied, but then the last cdr (normally nil) is changed to point at the second list, not a copy of the second list. If you were willing to destroy the first list, you would use nconc.
Try this:
(defparameter *list-3* (nconc *list-1* *list-2*))
Then observe the value of *list-1*, it is (1 2 2 3), just like *list-3*.
The general rule is that the non-destructive functions (append) won't destroy existing data, while the destructive functions (nconc) will. What a future destructive function does ((setf cdr)), though, is not the responsibility of the first non-destructive function.
quote:
So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?
I think that you are a bit harsh here with a subject that is quite a bit larger than you imagine. Lists are a rather elaborate concept in Lisp, and you need to understand that this is not some simple array. The standard provides a lot of functions to manipulate lists in every way. What you want in this case is:
(concatenate 'list *list-1* *list-2*)
So, why is there also append? Well, if you can omit copying the last list, and all symbols involved still return the correct data, this can be a significant performance boost in both calculating time and memory footprint. append is especially useful in a functional programming style which doesn't use side effects.
In lieu of further explanation about cons cells, destructive vs. nondestructive functions etc., I'll point you to a nice introduction: Practical Common Lisp, Ch. 12, and for a complete reference, the Common Lisp Hyperspec, look at Chapters 14 and 17.
So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?
By reading the fine manual? Hyperpsec explicitly states:
[...] the list structure of each of lists except the last is copied. The last argument is not copied; it becomes the cdr of the final dotted pair of the concatenation of the preceding lists, or is returned directly if there are no preceding non-empty lists.
Um, primarily we learn how it works, so that what we imagine isn't consistent makes sense.
What you need to do is find some of the old-fashioned block and pointer diagrams, which I can't easily draw, but let's figure it out.
After the first defparameter, you've got list-1, which is
(1 . 2 . nil)
in dot notation; list-2 is
(2 . 3 . nil)