In non strict evaluation languages, what are the differences and advantages/disadvantages of using call by name vs call by macro expansion?
Could you provide an example that explains both evaluation strategies?
Thanks!
In computer programming, a macro (short for "macro instruction"; from Greek μακρο- 'long, large') is a rule or pattern that specifies how a certain input should be mapped to a replacement output. Applying a macro to an input is known as macro expansion.
The macroexpand function is the conventional means for expanding a macro call. A hook is provided for a user function to gain control during the expansion process. If form is a macro call, then macroexpand-1 will expand the macro call once and return two values: the expansion and t.
A macro definition is a named sequence of statements you can call with a macro instruction. When it is called, the assembler processes and normally generates assembler language statements from the definition into the source module. The statements generated can be: Copied directly from the definition.
Macro expansion is an integral part of eval and compile . Users can also expand macros at the REPL prompt via the expand REPL command; See Compile Commands. Macros can also be expanded programmatically, via macroexpand , but the details get a bit hairy for two reasons. The second complication involves eval-when .
Call By Name:
Call by name is an evaluation strategy where the arguments to a function are not evaluated before the function is called—rather, they are substituted directly into the function body (using capture-avoiding substitution) and then left to be evaluated whenever they appear in the function. If an argument is not used in the function body, the argument is never evaluated; if it is used several times, it is re-evaluated each time it appears. (See Jensen's Device.)
Call-by-name evaluation is occasionally preferable to call-by-value evaluation. If a function's argument is not used in the function, call by name will save time by not evaluating the argument, whereas call by value will evaluate it regardless. If the argument is a non-terminating computation, the advantage is enormous. However, when the function argument is used, call by name is often slower, requiring a mechanism such as a thunk.
An early use was ALGOL 60. Today's .NET languages can simulate call by name using delegates or Expression parameters. The latter results in an abstract syntax tree being given to the function. Eiffel provides agents, which represent an operation to be evaluated when needed. Seed7 provides call by name with function parameters.
Call By Macro :
Call by macro expansion is similar to call by name, but uses textual substitution rather than capture-avoiding substitution. With uncautious use, macro substitution may result in variable capture and lead to undesired behavior. Hygienic macros avoid this problem by checking for and replacing shadowed variables that are not parameters.
NOTE: In non strict evaluation languages
Example Call By Macro :
Call by Macro Expansion: many programming languages, including C, lisp and scheme, provide developers with a mechanism to add new syntax to the core language grammar called macros. Macros are expanded into code by a macro preprocessor. These macros might contain arguments, which are copied in the final code that the preprocessor produces. As an example, the C program below implements the swap function via a macro:
#define SWAP(X,Y) {int temp=X; X=Y; Y=temp;} int main() { int a = 2; int b = 3; printf("%d, %d\n", a, b); SWAP(a, b); printf("%d,
> %d\n", a, b); }
This macro implements a valid swap routine. The
preprocessed program will look like the code below. Because the body of the macro is directly copied into the text of the calling program, it operates on the context of that program. In other words, the macro will refer directly to the variable names that it receives, and not to their values.
int main() { int a = 2; int b = 3; printf("%d, %d\n", a, b); {
> int tmp = (a); (a) = (b); (b) = tmp; }; printf("%d, %d\n", a, b); }
The expressions passed to the macro as parameters are evaluated every time they are used in the body of the macro. If the argument is never used, then it is simply not evaluated. As an example, the program below will increment the variable b twice:
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) int main() { int a = 2, b = 3; int c = MAX(a, b++); printf("a = %d, b = %d, c = %d\n", a, b, c); }
Macros suffer from one problem, called variable capture. If a macro defines a variable v that is already defined in the environment of the caller, and v is passed to the macro as a parameter, the body of the macro will not be able to distinguish one occurrence of v from the other. For instance, the program below has a macro that defines a variable temp. The call inside main causes the variable temp defined inside this function to be captured by the definition inside the macro's body.
#define SWAP(X,Y) {int temp=X; X=Y; Y=temp;} int main() { int a = 2; int temp = 17; printf("%d, temp = %d\n", a, temp); SWAP(a, temp);
> printf("%d, temp = %d\n", a, temp); }
Once this program is expanded by
the C preprocessor, we get the code below. This program fails to exchange the values of variables temp and a:
int main() { int a = 2; int temp = 17; printf("%d, temp = %d\n",
> a, temp); {int temp=a; a=temp; temp=temp;}; printf("%d, temp =
> %d\n", a, temp); }
There are a number of lazy evaluation strategies
that avoid the variable capture problem. The two best known techniques are call-by-name and call-by-need.
Example Call By Name :
Call by Name: in this evaluation strategy the actual parameter is only evaluated if used inside the function; however, this evaluation uses the context of the caller routine. For instance, in the example below, taken from Weber's book, we have a function g that returns the integer 6. Inside the function f, the first assignment, e.g., b = 5, stores 5 in variable i. The second assignment, b = a, reads the value of i, currently 5, and adds 1 to it. This value is then stored at i.
void f(by-name int a, by-name int b) { b=5; b=a; } int g() { int i = 3; f(i+1,i); return i; }
Very few languages implement the call by name evaluation strategy. The most eminent among these languages is Algol. Simula, a direct descendent of Algol, also implements call by name, as we can see in this example. The call by name always causes the evaluation of the parameter, even if this parameter is used multiple times. This behavior might be wasteful in referentially transparent languages, because, in these languages variables are immutable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With