Message Boards Message Boards

GROUPS:

Is Defer[] like single-quote from LISP?

Posted 2 months ago
401 Views
|
14 Replies
|
15 Total Likes
|

I have been looking for something like LISP's single-quote. Is Defer[] it?

14 Replies

I don't have experience with LISP, but I do know there are several options for controlling the evaluation process. You want to look at functions like Defer, Unevaluated, Hold, and HoldComplete.

There seems to be good discussion of the matter in this post. You can find out about one more, Inactive, in this post.

Posted 2 months ago

Thank you. I have had little success comprehending the docs for those functions though.

Defer is useful but very often omitted in topics about 'holding' functions. I don't have experience with LISP either but here is what should explain it to you.

Let's take a trip from an input to the output (I will ignore not related steps):

  • Input: Column[{1 + 1}]

    • What it really is, in a notebook, is a cell expression RowBox[{"Column", "[", RowBox[{"{", RowBox[{"1", "+", "1"}], "}"}], "]"}] but FrontEnd renders it for you and you see Column[{1 + 1}], when sent to the evaluation RowBox will be parsed and the Kernel will know it really is Column[{1+1}]

(* Parsing step ignored*)

  • Evaluation: ends up with Column[{ 2 }]

(* Formatting step ignored *)

  • Typesetting: MakeBoxes applied to out Column produces TagBox[GridBox[{{"2"}}, ...], "Column"]

  • Rendering: FrontEnd renders our GridBox so that you can see a grid with number 2.

Hold for example, affects Evaluation step by preventing its contents from evaluation.

Defer too, but it is also special for typesetting, MakeBoxes[Defer[x_], _] will be reduced to MakeBoxes[x_, _].

You can use it when composing a notebook from expressions, e.g. ExpressionCell[Defer[1+1], "Input"] will be ready to evaluate it while with Hold the Hold wrapper would still be there.

I would say, avoid Defer for now, because it is mixed up with notebooks and typesetting. As far as Mathematica-the-language is concerned, independently from the notebook interface and how things are displayed/copies/pasted/interpreted in a notebook, there is no difference whatsoever between Defer, HoldForm or Hold. Their special property is that they have the HoldAll attribute. (To put it another way, if you use the command line interface instead of notebooks, they're the same thing.)

So let's stick to Hold for simplicity. It simply prevents evaluation of its contents. You can create symbols like this yourself by simply assigning the HoldAll attribute.

There is Unevaluated, which also prevents evaluation, but it has another property: it is removed when its enclosing expression is evaluated. It is not possible to create a symbol with exactly this property. Unevaluated is unique and special.

To obtain a deeper understanding of how they work, you must read Evaluation of Expressions up to and including Nonstandard Evaluation, which discusses these cases.

Posted 2 months ago

Thanks for the replies.

To me it is looking like Defer[] is the Wolfram version of LISP-quote.

For anyone curious, if you removed everything from Mathematica except Evaluate[], Apply, Function[], and Defer[], you would essentially have LISP left over.

Mathematica has a superficial resemblance to Lisp, but that will mislead you. Lisp is an imperative functional programming language. Mathematica is an expression rewriting system. Lisp gets upset when it tries to evaluate an expression whose car isn't bound to something evaluatable. Mathematica doesn't care: it just looks deeper for some pattern to match. Evaluation doesn't get upset if it can't find a way to rewrite an expression: the evaluated form is just the input expression. One consequence of this is that you can use any construction that doesn't have a rule that applies as a quote. One nice way to defer matrix computations is to wrap each matrix in MatrixForm[]. That makes pretty output, and prevents evaluation since Plus[], Dot[] et al. have no rules that apply to MatrixForm as an argument. expr/.Matrixform->Identity lets evaluation proceed.

Mathematica can, with suitable rewrite rules, imitate functions and procedures. You can, however, confuse yourself if you take this imitation too seriously.

Posted 2 months ago

... you can use any construction that doesn't have a rule that applies as a quote.

I don't understand what you're contemplating here, but sometimes I want to quote an expression that does have applicable rules, hence my search for a LISP-quote analog. An undefined symbol as function-head does not emulate LISP-quote / Defer[]; this demonstrates their difference:

{undefinedSymbol[1 + 1], Defer[1 + 1]}

Mathematica can, with suitable rewrite rules, imitate functions and procedures. You can, however, confuse yourself if you take this imitation too seriously.

Coming to Mathematica from LISP, I have found the similarities clarifying, not confusing. I understood LISP once I understood its Apply, Eval, Lambda, and Quote forms. It is simple enough that I was able to implement a small LISP in Java quickly. Without knowing how LISP works, I would be baffled by Mathematica, even after studying the docs.

Edited to add: When I'm debugging my Mathematica code, I guess it helps me to think of Mathematica as an imperative functional programming language, so I can pin down what process is happening to what data in what order.

Posted 2 months ago

But Defer isn't like Lisp single quote. Single quote is a simple thing, while Defer is actually quite complicate and subtle.

FullForm[Defer[1 + 1]]

yields

Defer[Plus[1,1]]

Note that Defer has not actually gone away, unlike Lisp single quote. Normally, the front end hides it: here, FullForm has revealed it. But its really odd behavior is that the front end hides it from cut and paste, unlike HoldForm which gets picked up by cut and paste.

The most fundamental difference between Lisp and Mathematica is that the Lisp evaluator evaluates an expression once when invoked. The Mathematica evaluator re-evaluates the result, and continues until the result stops changing. This makes quoting a more complicated problem in Mathematica.

Apply isn't really doing what you imagine. Apply[f,expr] is essentially equivalent to (cons f (cdr expr)) in Lisp. Now, in Lisp, this wouldn't do much. It works in Mathematica because the evaluator then evaluates the resulting expression, so its behavior is superficially like apply in Lisp. Once you understand how this works, you can do Apply-like things more surgically. For example, expr/.List->Plus to apply Plus to every List in an expression.

The flip side is that Lisp gets upset with undefined functions, but Mathematica simply ignores expressions for which it has no rewrite rules.

Symbols bound to rewrite rules may look similar to Lisp functions, but rewrite rules much more flexible than lambda bindings.

f_[whoCalled] ^:= f
Sin[whoCalled]

yields

Sin

You can, of course write things that superficially resemble Lisp using things like Apply or things that superficially resemble C using For, but you can also trip yourself up doing that, and you won't be exploiting Mathematica's special capabilities.

Posted 2 months ago

Thanks.

I appreciate the warnings not to infer too much about Mathematica from LISP. If you're a programmer who didn't start using Mathematica until version 11 like me, it is not easy to get off the ground to where you're writing Mathematica programs with as much ease as you write LISP programs (even after reading Wolfram's introduction-for-programmers). The choice in practice is do I (1) try to leverage off of my C++ and LISP experience to get Mathematica working for me, or do I (2) keep reading Wolfram's description of their evaluation process over and over trying to understand it, but at the end of the day I still do not understand it, and the work I've wanted to do still is not done. Or maybe other new users have done better than I, but this is my story.

(2) wasn't working for me, so now I'm doing (1), and I have results to show for it. The replies here will be very helpful as I learn more about the differences between LISP and Mathematica; thank again to all respondents.

Edited to add:

the Lisp evaluator evaluates an expression once when invoked

Lisp includes something called "special forms" that are like Lisp functions except that when they are called, the argument's aren't evaluated before being passed to the special form. The body of a special form may explicitly evaluate the input arguments, at any time and in any order. Special forms are similar to Mathematica's HoldAll functionality, though you all may school me about the differences. If you wanted to implement Mathematica in Lisp, you could make a special form that evaluates input according to the Mathematica evaluation-process.

@Joe Donaldson: If you insist on Defer instead of Hold that may be a sign that your mental model for these symbols is not correct ...

Posted 2 months ago

I still can't make sense of Hold, but I no longer insist that Defer[] is the Lisp-Quote of Mathematica, thanks to the replies here. Your points above about "Mathematica-the-language is concerned, independently from the notebook interface and how things are displayed/copies/pasted/interpreted in a notebook" are especially helpful; you give me the idea to work some in the Mathematica command-line interface where I can focus on learning core language without the added complexity of the notebook-abstraction layer.

You're trying too hard to map Mathematica onto Lisp. Hold is actually the easiest kind of quote to understand: it simply persists, visibly, until some rewrite (usually performed by ReleaseHold) removes it. You don't need this persistence in Lisp, because of its "evaluate once" nature. The closest thing to Lisp ' is probably Unevaluated. Once its purpose of injecting a literal expression into a rewritten expression is accomplished, it disappears. But, of course, in Mathematica this leaves the result exposed to the evaluator, so the result is generally different from Lisp '.

Posted 2 months ago

Thanks. Unevaluated has been another I haven't been able to grasp, but maybe I will get it now in light of your explanations.

Yes, Defer can be confusing because in a notebook, evaluating Defer[1+1] seems to give 1+1. This is merely an illusion. On the command line you get Defer[1+1] as the output, i.e. Defer behaves just like Hold.

This is because Defer is formatted in a special way in notebooks, but formatting is entirely orthogonal to evaluation or the storage of expressions. Basically, the output looks like 1+1, but if you previously assigned it to a variable, and use Head on it, you will see that it still has the head Defer. However, if you copy that 1+1 and paste it back, or just edit the 1+1 in the notebook, it becomes something that is subsequently interpreted as Plus[1,1] and not Defer[Plus[1,1]].

This is an additional source of confusion that has nothing to do with the HoldAll property of Defer.

This is why I suggested to experiment with Hold only for now, and once you understand it well, you can look at the specially formatted constructs like Defer and Interpretation.

There is also HoldForm, which is basically an always-invisible Hold. It's invisible even in command-line mode.

You would normally use HoldForm and Defer when you care about presentation, and care about what happens when the displayed expressions are copied by a user.

You would use Hold when you simply want to manipulate expressions that may never be displayed to a user unfamiliar with the workings of your program (or when you want to see the expression as it really is and not in disguise).


I don't know Lisp, but I am aware of the single-quote feature of Maple, which is superficially similar to Unevaluated, but in reality very very different.

The Maple docs say,

After each evaluation, one level of single quotes is stripped off.

'x' becomes x and ''x'' becomes 'x'.

There is no single-step evaluation in Mathematica. Mathematica will always keep applying transformation rules for as long as the expression cannot be changed anymore. If we have an expression that removed one level of itself, e.g. f[x_] := x, then Mathematica will keep applying this rule again and again for as long as it can. So f[f[f[x]]] will always become x.

See also:

https://mathematica.stackexchange.com/questions/334/how-do-i-evaluate-only-one-step-of-an-expression

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract