4
$\begingroup$

Could you explain the details for the following code?

foo[x_, y_] := {a, If[x === y, b, Sequence @@ {}]} foo[s, s] foo[s, t] 

Output:

{a,b} {a} 

Why isn't the output as follows?

{a,b} {a,} 
$\endgroup$
7
  • $\begingroup$ If you want {a,} as the result you could use Null instead of Sequence @@ {} in your If statement. $\endgroup$ Commented May 13, 2021 at 14:58
  • $\begingroup$ @JasonB Yes. I know it. Thanks. $\endgroup$ Commented May 13, 2021 at 15:00
  • $\begingroup$ Since Sequence @@ {} === Nothing evaluates to True, the definition is equivalent to foo[x_, y_] := {a, If[x === y, b, Nothing]} $\endgroup$ Commented May 13, 2021 at 17:20
  • $\begingroup$ @BobHanlon: Thank you very much. A good explanation! $\endgroup$ Commented May 13, 2021 at 17:30
  • 1
    $\begingroup$ @LeonidShifrin - In looking at it closely using Trace, the reason that Sequence[] === Nothing evaluates to True is that SameQ[Sequence[], Nothing] evaluates to SameQ[Nothing] and SameQ with any single argument evaluates to True, e.g., SameQ[2] == SameQ[a] == True is True. I would have expected SameQ to complain if given a single argument. $\endgroup$ Commented May 14, 2021 at 1:10

1 Answer 1

6
$\begingroup$

Summary

The behaviour we see is a consequence of the way Sequence[] expressions are processed. They are only expanded when they are explicitly present as arguments to a function. In the case at hand, Sequence@@{} only takes such a form after evaluation. But If has the attribute HoldRest so that evaluation does not take place until after the argument list has already been assembled and the If function invoked.

Details

We start from the observation that the following three expressions give differing results:

{a, If[1==2, unused]} // InputForm (* {a, Null} *) {a, If[1==2, unused, Sequence[]]} // InputForm (* {a, Null} *) {a, If[1==2, unused, Sequence@@{}]} // InputForm (* {a} *) 

If[1==2, unused] -> Null

The first result is explained by the following documentation for If:

If[condition, t] gives Null if condition evaluates to False.

Thus:

If[1==2, unused] // InputForm (* Null *) {a, If[1==2, unused]} // InputForm (* {a, Null} *) 

If[1==2, unused, Sequence[]] -> Null

The second result is a consequence of the attributes of If:

Attributes[If] (* {HoldRest, Protected} *) 

If does not have the attribute SequenceHold so prior to evaluation any explicit appearances of arguments with the head Sequence will be spliced into the argument list. Sequence expansion is independent of evaluation, so it occurs despite the presence of HoldRest which inhibits evaluation of the second and third arguments.

In this case, an empty sequence is spliced in so the net result is the same as the first example (i.e. as if If were invoked with only two arguments:

If[1==2, unused, Sequence[]] // InputForm (* Null *) {a, If[1==2, unused, Sequence[]]} // InputForm (* {a, Null} *) 

If[1==2, unused, Sequence@@{}] -> Sequence[]

The argument Sequence@@{} is not evaluated when it is passed to If due to the presence of the HoldRest attribute. This means that the head of the argument expression is actually Apply, not Sequence:

Head[Unevaluated[Sequence@@{}]] (* Apply *) 

Since the head is not explicitly Sequence, it does not get spliced into the argument list and so the If expression remains in three-argument form instead of two-argument form. Since the condition is false the third argument Sequence@@{} is evaluated to Sequence[] and returned:

If[1==2, unused, Sequence@@{}] // InputForm (* Sequence[] *) {a, If[1==2, unused, Sequence@@{}]} // InputForm (* {a} *) 

In that last expression the result is notionally {a, Sequence[]}. But since the second argument to List is explicitly Sequence[], an empty sequence gets spliced into the argument list.

$\endgroup$
1
  • $\begingroup$ +1, very nice answer. IIRC, one of the main motivating factors for introducing Nothing was to simplify this If[...] idiom and avoid using Sequence in such cases like this. $\endgroup$ Commented May 13, 2021 at 17:44

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.