7
$\begingroup$

Let's consider a function with evaluated and unevaluated arguments inside:

list1 = {1,2,3}
list2 = {x,y,z}

fun[list_]:={list,ToString@Unevaluated@list}

When I use Map over fun I expect to get result like this:

{fun[list1],fun[list2]}
(*{{{1, 2, 3}, "list1"}, {{x, y, z}, "list2"}}*)

But instead I get this:

fun /@ {list1, list2}
{{{1, 2, 3}, "{1, 2, 3}"}, {{x, y, z}, "{x, y, z}"}}

This is surely connected with my misunderstanding of evaluation behaviour, but no obvious answer was found in Robby Villegas notebook https://library.wolfram.com/infocenter/Conferences/377/ , nor here in StackExchange. How to solve this problem?

ver. 12.1, Windows 10

EDIT
Setting attribute Listable help a bit making unnecessary to use Map:

SetAttributes[fun, {HoldAll, Listable}];
fun[{list1,list2}]
``
(*{{{1, 2, 3}, "list1"}, {{x, y, z}, "list2"}} *)
$\endgroup$

2 Answers 2

7
$\begingroup$

It is the Map command which needs to use HoldAll. This is one way to make it work. There might be other ways

SetAttributes[fun, HoldAll];
SetAttributes[Map, HoldAll]
fun[list_] := {ToString@HoldForm@list, list}
list1 = {1, 2, 3}
list2 = {x, y, z}
fun /@ {list1, list2} 

enter image description here

If you do not add SetAttributes[Map, HoldAll] then this is what you get

enter image description here

This shows that Map is the one which evaluated its argument, so too late by the time it reaches your function.

$\endgroup$
1
  • $\begingroup$ Thank You for Your answer. $\endgroup$
    – Lechuu
    Feb 11 at 14:12
7
$\begingroup$

The keypoint is, if a function doesn't have a Hold* attribute, its argument(s) will always be evaluated before going into the function. So in your case, you need to stop the automatic evaluation 3 times. One for Map (/@), one for fun and one for ToString. You've already properly handled ToString with an Unevaluated and fun with the HoldAll attribute. For Map, you just need one more Unevaluated:

list1 = {1,2,3};
list2 = {x,y,z};

SetAttributes[fun, HoldAll]
fun[list_]:={list,ToString@Unevaluated@list}
fun /@ Unevaluated@{list1, list2}

Setting HoldAll attribute on Map as shown in Nasser's answer is another way to go, but generally it's not a good idea to modify built-in functions. BTW, if you don't want to set HoldAll on fun, you can introduce a pure function as the wrapper as follows:

ClearAttributes[fun, HoldAll]

Function[a, fun@Unevaluated[a], HoldAll] /@ Unevaluated@{list1, list2}

Notice the HoldAll inside Function stops the automatic evaluation of argument of pure function, and the Unevaluated inside Function stops the automatic evaluation of argument of fun.

$\endgroup$
1
  • $\begingroup$ very simple and clear explanation, thanks for Your help! $\endgroup$
    – Lechuu
    Feb 11 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.