## `hold` -- delay evaluation

#### Introduction

`hold(`object`)` prevents the evaluation of `object`.

#### Call(s)

`hold(object)`

#### Parameters

`object` | - | any MuPAD object

#### Returns

the unevaluated object.

#### Further Documentation

Chapter 5 of the MuPAD Tutorial.

#### Related Functions

`context`, `delete`, `eval`, `freeze`, `genident`, `indexval`, `level`, `proc`, `val`

#### Details

- When a MuPAD object is entered interactively, then the system evaluates it and returns the evaluated result. When a MuPAD object is passed as an argument to a procedure, then the procedure usually evaluates the argument before processing it. Evaluation means that identifiers are replaced by their values and function calls are executed. `hold` is intended to prevent such an evaluation when it is undesirable.
- A typical application of `hold` is when a function that can only process numerical arguments, but not symbolical ones, is to be plotted or to be subjected to some numerical algorithm. See example 6.

- Another possible reason for using `hold` is efficiency. For example, if a function call `f(x, y)` with symbolic arguments is passed as argument to another function, but is known to return itself symbolically, then the possibly costly evaluation of the ``inner'' function call can be avoided by passing the expression ``hold(`f`)`(x, y)` as argument to the ``outer'' function instead. Then the arguments `x, y` are evaluated, but the call to `f` is not executed. See examples 1 and 7.

- Since using `hold` may lead to strange effects, it is recommended to use it only when absolutely necessary.

- `hold` only delays the evaluation of an object, but cannot completely prevent it on the long run; see example 5.

  You can use `freeze` to completely prevent the evaluation of a procedure or a function environment.

- A MuPAD procedure can be declared with the option `hold`. This has the effect that arguments are passed to the procedure unevaluatedly. See the help page of `proc` for details.

- The functions `eval` or `level` can be used to force a subsequent evaluation of an unevaluated object (see example 2). In procedures with `option hold`, use `context` instead.

- `hold` is a function of the system kernel.

#### Example 1

In the following two examples, the evaluation of a MuPAD expression is prevented using `hold`:

    >> x := 2:
       hold(3*0 - 1 + 2^2 + x)

                                            2
                                 3 0 - 1 + 2  + x

    >> hold(error("not really an error"))

                           error("not really an error")

Without `hold`, the results would be as follows:

    >> x := 2:
       3*0 - 1 + 2^2 + x

                                         5

    >> error("not really an error")

          Error: not really an error

The following command prevents the evaluation of the operation `_plus`, but not the evaluation of the operands:

    >> hold(_plus)(3*0, -1, 2^2, x)

                                   0 - 1 + 4 + 2

Note that in the preceding example, the arguments of the function call are evaluated, because `hold` is applied only to the function `_plus`. In the following example, the argument of the function call is evaluated, despite that fact that `f` has the option hold:

    >> f := proc(a)
              option hold;
            begin
              return(a + 1)
            end_proc:
       x := 2:
       hold(f)(x)

                                       f(2)

This happens for the following reason. When `f` is evaluated, the option `hold` prevents the evaluation of the argument `x` of `f` (see the next example). However, if the evaluation of `f` is prevented by `hold`, then the option `hold` has no effect, and MuPAD evaluates the operands, but not the function call.

The following example shows the expected behavior:

    >> f(x), hold(f(x))

                                    x + 1, f(x)

The function `eval` undoes the effect of `hold`. Note that it yields quite different results, depending on how it is applied:

    >> eval(f(x)), eval(hold(f)(x)), eval(hold(f(x))), eval(hold(f))(x)

                                3, 3, x + 1, x + 1

#### Example 2

Several `hold` calls can be nested to prevent subsequent evaluations:

    >> x := 2:
       hold(x), hold(hold(x))

                                    x, hold(x)

The result of `hold(``hold(`x`)``)` is the unevaluated operand of the outer call of `hold`, that is, `hold(`x`)`. Applying `eval` evaluates the result `hold(`x`)` and yields the unevaluated identifier `x`:

    >> eval(%)

                                       2, x

Another application of `eval` yields the value of `x`:

    >> eval(%)

                                       2, 2

    >> delete x, f:

#### Example 3

The following command prevents the evaluation of the operation `_plus`, replaces it by the operation `_mult`, and then evaluates the result:

    >> eval(subsop(hold(_plus)(2, 3), 0 = _mult))

                                         6

#### Example 4

The function `domtype` evaluates its arguments:

    >> x := 0:
       domtype(x), domtype(sin), domtype(x + 2)

                          DOM_INT, DOM_FUNC_ENV, DOM_INT

Using `hold`, the domain type of the unevaluated objects can be determined: `x` and `sin` are identifiers, and `x + 2` is an expression:

    >> domtype(hold(x)), domtype(hold(sin)), domtype(hold(x + 2))

                          DOM_IDENT, DOM_IDENT, DOM_EXPR

#### Example 5

`hold` prevents only one evaluation of an object, but it does not prevent evaluation at a later time. Thus using `hold` to obtain a a symbol without a value is usually not a good idea:

    >> x := 2:
       y := hold(x);
       y

                                         x

                                         2

In this example, deleting the value of the identifier `x` makes it a symbol, and using `hold` is not necessary:

    >> delete x:
       y := x;
       y

                                         x

                                         x

However, the best way to obtain a new symbol without a value is to use `genident`:

    >> y := genident("x");
       y

                                        x1

                                        x1

    >> delete y:

#### Example 6

Suppose that we want to plot the graph of the piecewise continuous function f(x) that is identically zero on the negative real axis and equal to exp(-x) on the positive real axis:

    >> f := x -> if x < 0 then 0 else exp(-x) end_if:

If we pass the symbolical expression `f(x)` as an argument to `plotfunc2d`, then an error occurs:

    >> delete x:
       plotfunc(f(x), x = -2..2)

          Error: Can't evaluate to boolean [_less];
          during evaluation of 'f'

The reason is that `plotfunc2d` evaluates its arguments, and the evaluation of `f(x)` for a symbolical argument `x` leads to an error:

    >> f(x)

          Error: Can't evaluate to boolean [_less];
          during evaluation of 'f'

A solution is to use `hold`:

    >> plotfunc2d(hold(f)(x), x = -2..2):

The same phenomenon occurs when we want to apply numerical integration to `f`:

    >> numeric::int(f(x), x = -2..2)

          Error: Can't evaluate to boolean [_less];
          during evaluation of 'f'

    >> numeric::int(hold(f)(x), x = -2..2)

                                   0.8646647168

#### Example 7

The function `int` is unable to compute a closed form of the following integral and returns a symbolic `int` call:

    >> int(sin(x)*sqrt(sin(x) + 1), x)

                                                 1/2
                          int(sin(x) (sin(x) + 1)   , x)

After the change of variables `sin(x)=t`, a closed form can be computed:

    >> t := time():
       f := intlib::changevar(int(sin(x)*sqrt(sin(x) + 1), x), sin(x) = y);
       time() - t;
       eval(f)

                                 /          1/2    \
                                 | y (y + 1)       |
                              int| ------------, y |
                                 |       2 1/2     |
                                 \ (1 - y )        /

                                       9210

                                        1/2 / 2 y       \
                         (y - 1) (y + 1)    | --- + 4/3 |
                                            \  3        /
                         --------------------------------
                                         2 1/2
                                   (1 - y )

Measuring computing times with `time` shows: Most of the time in the call to `intlib::changevar` is spent in re-evaluating the argument. This can be prevented by using `hold`:

    >> t := time():
       f := intlib::changevar(hold(int)(sin(x)*sqrt(sin(x) + 1), x),
                              sin(x) = y);
       time() - t;

                                 /          1/2    \
                                 | y (y + 1)       |
                              int| ------------, y |
                                 |       2 1/2     |
                                 \ (1 - y )        /

                                        20

#### Changes

- no changes

Do you have questions or comments?
Copyright © SciFace Software GmbH & Co. KG 2000