# SortBy does not work correctly when nest in Evaluate in With in Block

I have got some formula in Mathematica, then I want to pass value to verify them.

full code

vb[v_, a_] := v - 1/2*Sign[a]*(a*a)/Jm;
vf[v_, a_] := v + 1/2*Sign[a]*(a*a)/Jm;
Sb[v_, a_] := vb[v, a] RealAbs[a]/Jm + 1/6 (a*a*a)/(Jm*Jm);
Sf[v_, a_] := vf[v, a] RealAbs[a]/Jm - 1/6 (a*a*a)/(Jm*Jm);

test1 = vAb < vAf; expr1 = {Vm + Am, "Vm + Am"};
test2 = vEb < vEf; expr2 = {Vm - Am,   "Vm - Am"};

(*verify the above  formulas, Vm  Am Jm as coefficients, (v,a) as \
independent variables*)
(*Block: the names to be global,but values to be local*)
(*With: define constant local variable think of With as a \
generalization of the/.operator *)
ClearAll[TSa]
TSa[A_, E_, v_, cutoff_, S_] :=
Block[{Vm = 20.0, Am = 10.0, Jm = 30.0, vA = A[[1]], aA = A[[2]],
vE = E[[1]], aE = E[[2]], TA = Abs[aA]/Jm, TE = Abs[aE]/Jm,
vM = v},
With[{vAb = vb[vA, aA], vAf = vf[vA, aA], vEb = vb[vE, aE],
vEf = vf[vE, aE], SAb = Sb[vA, aA], SAf = Sf[vA, aA],
SEb = Sb[vE, aE], SEf = Sf[vE, aE]},

Evaluate@{{{"vEb", vEb}, {"vEf", vEf}, {"vAb", vAb}, {"vAf",
vAf}, {"SAb", SAb}, {"SEf", SEf}},
SortBy[{{"vEb", vEb}, {"vEf", vEf}, {"vAb", vAb}, {"vAf",
vAf}, {"SAb", SAb}, {"SEf", SEf}}, Last],
Which[(*just one simple example*)
Evaluate@test1, Evaluate@expr1,
Evaluate@test2, Evaluate@expr2
]
}

]

]

S = 14.880227636588513;
TSa[{10.0, -5.0}, {18.0, 9.5}, 9.583, False, S]


output

    {{{"vEb", 16.4958}, {"vEf", 19.5042}, {"vAb", 10.4167}, {"vAf",
9.58333}, {"SAb", 1.71296}, {"SEf", 6.01755}}, {{"SAb",
1.71296}, {"SEf", 6.01755}, {"vAb", 10.4167}, {"vAf",
9.58333}, {"vEb", 16.4958}, {"vEf", 19.5042}}, {10., "Vm - Am"}}


The first line is before sorting, after sorting, the second line seems incorrect.

If remove Evaluate@ , it is connect. However, I must add Evaluate@ since there is one Which function following SortBy. With function replace rule does not work in

• SortBy is evaluated before the values are plugged in by With. I'm confused why you have evaluate SortBy before there are values. If true, it seems a catch-22. Commented Nov 22, 2023 at 15:04
• I have no idea about the order. Evaluate@{expr1,expr2} I think expr2 is valuated after expr1, expr1 is correct according the output, so I thought expr2 already has these value. Commented Nov 22, 2023 at 15:13
• You are correct about the order of expr1 and expr2, but Evaluate@{expr1,expr2} is evaluated first in With[settings, Evaluate@{expr1,expr2}] and then With[settings, result] is evaluated, where result represents the expression returned by Evaluate@{expr1,expr2}. The reason expr1 has the right final output is because expr1 evaluates to itself. Try evaluating the SortBy[{...}], Last] expression by itself in its own cell -- the result should correspond to the output you see. Commented Nov 22, 2023 at 15:20
• We cannot execute your code because you have not provided all of the required definitions. Commented Nov 22, 2023 at 16:19
• @BobHanlon I post full code, thanks. Commented Nov 22, 2023 at 17:29

Since I don't know the semantics, I'm not sure if what follows is ideal, but hopefully it illustrates some better design choices.

vb[Jm_, {v_, a_}] := v - 1/2*Sign[a]*(a*a)/Jm;
vf[Jm_, {v_, a_}] := v + 1/2*Sign[a]*(a*a)/Jm;
Sb[Jm_, {v_, a_}] :=  vb[Jm, {v, a}] RealAbs[a]/Jm + 1/6 (a*a*a)/(Jm*Jm);
Sf[Jm_, {v_, a_}] := vf[Jm, {v, a}] RealAbs[a]/Jm - 1/6 (a*a*a)/(Jm*Jm);

CheckPair[Jm_, pair : {_, _}] := vb[Jm, pair] < vf[Jm, pair];

TSa[Vm_, Am_, Jm_, A_, E_, v_, cutoff_, S_] :=
With[
{results = {{"vEb", vb[Jm, E]}, {"vEf", vf[Jm, E]}, {"vAb", vb[Jm, A]}, {"vAf", vf[Jm, A]}, {"SAb", Sb[Jm, A]}, {"SEf", Sf[Jm, E]}}},
{results,
SortBy[results, Last],
Which[
CheckPair[Jm, A], {Vm + Am, "Vm + Am"},
CheckPair[Jm, E], {Vm - Am, "Vm - Am"},
True, "something went wrong"]}]


some explanation

You had lots of repeats of "formal" symbols (e.g. Jm) in your various functions and expressions, and your intention was to give those symbols a value later to cause your functions to magically evaluate. This style of coding relies on side effects, and it's very difficult to get right and maintain. That's why you had to play games with Module, With, and Evaluate. If those things were true constants, then you can define them "globally" and Protect them so they don't accidentally get overwritten. I'm assuming that they're parameters, not constants, and so we can just make them arguments to the functions. Now often, with parameters, we do want to "push them out of the way" a bit, and in that case we might curry these functions or set default values, but I'll leave that to another discussion.

I also changed the function signatures to work more naturally with pairs, since that's what you pass in to TSa (e.g. A and E).

I notice that v, cutoff, and S aren't actually used in the result, and I would have removed them entirely but I'm assuming that they are used in your actual code and that what you presented here was a simplification.

Further improvements might include generalizing the v* and S* functions and improving how we produce the string labels in the results, but I'd be speculating too much to come up with such suggestions. Also, the names seem completely opaque, so renaming things is another potential improvement.

Just making minimal changes to your TSa function (there are larger design issues here that I'm ignoring):

TSa[A_, E_, v_, cutoff_, S_] :=
Block[{Vm = 20.0, Am = 10.0, Jm = 30.0},
With[
{rules = {vAb -> vb @@ A, vAf -> vf @@ A, vEb -> vb @@ E, vEf -> vf @@ E, SAb -> Sb @@ A, SAf -> Sf @@ A, SEb -> Sb @@ E, SEf -> Sf @@ E}},
{{{"vEb", vEb}, {"vEf", vEf}, {"vAb", vAb}, {"vAf", vAf}, {"SAb", SAb}, {"SEf", SEf}} /. rules,
SortBy[{{"vEb", vEb}, {"vEf", vEf}, {"vAb", vAb}, {"vAf", vAf}, {"SAb", SAb}, {"SEf", SEf}} /. rules, Last],
Which[test1, expr1, test2, expr2] //. rules}]]
`
• Could you give me some advice on design issues? I will be thankful Commented Nov 23, 2023 at 15:17