3
$\begingroup$

I have a head that holds a single argument, and I want to unpack the value inside it while also assigning a symbol to the value itself, for example:

f[y:g[x_]] := {y, x};

f[g[4]] (* == {g[4], 4} *)

This works fine, but when I want to give it a default argument, x does not bind to the inner value:

f[y:g[x_]:g[10]] := {y, x};

f[] (* == {g[10]}, rather than {g[10], 10} *)

How do I make x bind to the inside of the default value?

This is my real-world example:

squareLattice = lattice[{n1 -> {1, 0}, n2 -> {0, 1}}];

doRandomWalk[n_Integer, l:lattice[basis_]:squareLattice] :=
  With[{basisSymbols = First /@ basis},
   Echo[basis]; 
   randomWalk[
    Accumulate@
     Table[RandomChoice[{1, -1}] RandomChoice[basisSymbols], n],
    l]];

When I evaluate using the default argument: doRandomWalk[100], I get a message from Echo, as basis is not bound to anything

Thank you for any help!

$\endgroup$
9
  • $\begingroup$ maybe ClearAll[f, g]; SetAttributes[f, HoldFirst];f[y : g[x_] : Sequence[g[10], 10]] := {y, x};? $\endgroup$
    – kglr
    Commented Mar 23, 2020 at 9:57
  • $\begingroup$ I'll try that, but I'm hoping there's a way to do it without repeating the inner argument... $\endgroup$ Commented Mar 23, 2020 at 10:01
  • $\begingroup$ Why is the more straightforward doRandomWalk[n_Integer, l_lattice: squareLattice] := Module[{basis = First[l], basisSymbols}, basisSymbols = First /@ basis; Echo[basis]; randomWalk[Accumulate @ Table[RandomChoice[{1, -1}] RandomChoice[basisSymbols], n], l]] unsuitable for your needs? $\endgroup$ Commented Mar 23, 2020 at 10:37
  • $\begingroup$ @J.M. that is what I did initially, but I was wondering if this is possible, as it seems cleaner to me. I prefer doing the unpacking in the pattern matching rather than manually $\endgroup$ Commented Mar 23, 2020 at 11:28
  • $\begingroup$ My reasoning is that it makes it clear to the reader what the structure of lattice should be just by looking at the pattern and not having to read the function body $\endgroup$ Commented Mar 23, 2020 at 11:31

2 Answers 2

4
$\begingroup$

Well, here's one way to do it:

f[y : g[x_] : g[10]] := {y, Replace[Unevaluated[x], Sequence[] :> First[y]] }

f[]
(*{g[10], 10}*)

f[g[20]]
(*{g[20], 20}*)

Note that x_ can't bind to the default, because there's no requierment that the default match the pattern:

In[83]:= f[y:g[x_]:{1, 2, 3}] := {y, Replace[Unevaluated[x], Sequence[] :> First[y]]}

f[]
(*{{1, 2, 3}, 1}*)
$\endgroup$
6
  • 1
    $\begingroup$ Am I right in thinking that this behavior has changed? I think in version 7 the x_ in f[y:g[x_]:g[10]] := {y, x}; did bind to 10? $\endgroup$
    – Mr.Wizard
    Commented Mar 24, 2020 at 6:14
  • 3
    $\begingroup$ You're right, it did change. Probably in 10.x? I'd have go testing versions to be sure. In earlier versions, it was required that the default value match the pattern. It was often requested that we relax this requirement. When we did so, we lost the ability to bind x_ to the default. Based on the number of complaints we've gotten about the change, I think we made the right call with this compromise, but there are edge cases like this one where it is somewhat unfortunate. $\endgroup$ Commented Mar 24, 2020 at 6:36
  • 1
    $\begingroup$ (1) This is a potentially code-breaking change; is it documented? The relaxation was raised in (108636) and it made sense to me at the time, but I hadn't realized what was lost. (2) What is the reason that the pattern binding cannot happen when the default does match? That would seem to be the best of both worlds, without giving this the thought it is probably due. $\endgroup$
    – Mr.Wizard
    Commented Mar 24, 2020 at 6:45
  • 1
    $\begingroup$ Re (1) I'm not sure if this is documented. I'll put it on my doc review list to check. (2) I don't have a great answer for this. It is most likely possible in principle, though there is a question of whether the cost and risk is justified. One thing we did discover when this came up was even in the old code, if the pattern was sufficiently complicated, we might not get all the inner bindings correct (oops). Never doing it is certainly easier to implement, and it is much better to document "never" than "sometimes but we won't tell you when". $\endgroup$ Commented Mar 24, 2020 at 19:16
  • $\begingroup$ Thanks. On the second point where does a naive Replace[defalt, pattern | _ :> RHS] type of construct fail? Is it in handling multiple arguments? E.g. Replace[g[10], y : (g[x_] | _) :> {y, x}] and Replace[{1, 2, 3}, y : (g[x_] | _) :> {y, x}] both fill {y, x} in what to me seams a reasonable way. $\endgroup$
    – Mr.Wizard
    Commented Mar 24, 2020 at 20:58
1
$\begingroup$

This method doesn't work in recent versions. In/Out from version 10.1 below.


For the short example given you could specify it like this:

f[y : g[x_: 10] : g[10]] := {y, x};

f[]
{g[10], 10}

I just realized this introduces a behavior you probably don't want:

f[g[]]
{g[], 10}

The same (flawed) method applied to the longer example:

squareLattice = lattice[{n1 -> {1, 0}, n2 -> {0, 1}}];

With[{def = squareLattice},
  doRandomWalk[n_Integer, l : lattice[basis_: def[[1]]] : def] := {n, l, basis}
 ]

doRandomWalk[5]
{5, lattice[{n1 -> {1, 0}, n2 -> {0, 1}}], {n1 -> {1, 0}, n2 -> {0, 1}}}
$\endgroup$
5
  • 1
    $\begingroup$ doRandomWalk[5] gives {5, lattice[{n1 -> {1, 0}, n2 -> {0, 1}}]} in v11.30 (Windows 10/64b). $\endgroup$
    – kglr
    Commented Mar 24, 2020 at 6:17
  • $\begingroup$ @kglr Well that's a surprise! I checked again in a clean session and version 10.1 returns as shown. $\endgroup$
    – Mr.Wizard
    Commented Mar 24, 2020 at 6:20
  • $\begingroup$ @kglr But now I confirm that f[y:g[x_]:g[10]] := {y, x}; works as written in version 7. Lots of changes to these behaviors. Are they documented? $\endgroup$
    – Mr.Wizard
    Commented Mar 24, 2020 at 6:21
  • $\begingroup$ it also returns {5, lattice[{n1 -> {1, 0}, n2 -> {0, 1}}]} in version 12.1 (Wolfram Cloud). $\endgroup$
    – kglr
    Commented Mar 24, 2020 at 6:29
  • 1
    $\begingroup$ It works as expected in v9.0 (windows 10). Also f[y:g[x_]:g[10]] := {y, x}; gives the desired result in v9.0. $\endgroup$
    – kglr
    Commented Mar 24, 2020 at 6:36

Your Answer

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

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