6
$\begingroup$
*In[1]=* Transpose[{a, b}]

returned in earlier versions of Mathematica (e.g Mathemtica 12.1) simply

*Out[1]=* Transpose[{a, b}]

together with the warning: "Transpose::nmtx: The first two levels of {a,b} cannot be transposed."

However in Mathematica 12.2 (on Windows10) the output is:

*Out[1]=* {a,b}

The Transpose has disappeared. Is this a bug or a feature?

For me, it is problem, because this new behavior is breaking my existing code, where I have functions similar to:

getfromdatabase[key_String]:=CallToCppCodeAndReturnListsOfValues[key]
getfromdatabase[expr_]:=expr/. key_String :>  getfromdatabase[key]

In previous versions (<12.2) users of my code could execute

getfromdatabase[ Transpose[{"firstname", "lastname", "salary"-"rent"}] ]

and the output was for example:

{ {David, Smith, somenumber}, {Lisa, Garbo, someothernumber}, ... }

Now, in 12.2, one gets:

{ {David, Lisa,...}, {Smith, Garbo, ...}, {somenumber, someothernumber, ...}}

How can I change getfromdatabase to get back the old output using the same input as before? (Sure, in the example above, one could apply Transpose after calling the function. But in other calls there might be calls of Transpose more nested inside the expression, like getfromdatabase[ Transpose[{"firstname", "lastname", Transpose[{"salary","rent"}]] ]) Thanks!

$\endgroup$
10
  • 2
    $\begingroup$ I did not notice this before, but I noticed that Transpose[{}] is {}, which for me was a very welcome change. Note that Transpose[{{}}] is {}. $\endgroup$
    – Szabolcs
    Apr 15, 2021 at 17:11
  • 6
    $\begingroup$ Regarding the code you say was "broken" by this: I would not have been comfortable doing something like this in previous versions either. Relying on such non-evaluation seems extremely fragile. Also, Transpose[{1, 2, 3}] showed an error message in previous versions. Relying on specific details of how an error was handled before just isn't a good idea. $\endgroup$
    – Szabolcs
    Apr 15, 2021 at 17:14
  • 5
    $\begingroup$ I can confirm that this was an intended change in WL 12.2. Note that Transpose[{a, b}, {1}] always worked, returning {a, b}. But it was not automatic behavior, and now it is. This has several advantages, for example the fact that now the common expression Transpose[w].m.v works, where w and v are vectors and m is a matrix. $\endgroup$
    – jose
    Apr 15, 2021 at 19:39
  • 1
    $\begingroup$ @jose: The possibilty of Transpose[w].m.v is indeed a nice thing. I just wonder, why Wolfram is not mentioning this change in the documentation. Or did it just overlook it? $\endgroup$
    – dnet
    Apr 16, 2021 at 8:01
  • 2
    $\begingroup$ Because the solution in thorimur's answer i.e. making use of HoldAll won't work in this case. $\endgroup$
    – xzczd
    Apr 16, 2021 at 10:57

2 Answers 2

5
$\begingroup$

Presumably, in some part of your code, you check for the head Transpose being present. So, you can make your own head t that behaves like the old transpose, then check for the head t instead of Transpose in those parts of the code. You could possibly define it like this:

t[list : {__List}] := Transpose[list]
t[list_, spec_] := Transpose[list, spec]

so that only lists of lists will be actually transposed, and the rest will remain enclosed with t, as no definition is matched.

Then, we need to give getfromdatabase the attribute HoldAll or HoldFirst, rename the previous definition of getfromdatabase internally as, say, getfromdatabase0, and replace Transpose heads with t before passing to getfromdatabase0. E.g.:

SetAttributes[getfromdatabase, HoldAll]
getfromdatabase[expr_] := getfromdatabase0[ReleaseHold[Hold[expr] /. Transpose -> t]]

This is, of course, just one way to fix your code; disclaimer that there might be other, better ways! :)

$\endgroup$
1
  • $\begingroup$ Thank you for this answer, that should work for the specific problem with Transpose. I guess for me the better solution would be to hold the expression unevaluated before the strings are replaced by the lists of values. $\endgroup$
    – dnet
    Apr 16, 2021 at 8:10
1
$\begingroup$

Inspired by thorimur's answer, I came up with the following, which meets maybe better my original intention of how getfromdatabase should work in general.

SetAttributes[getfromdatabase, HoldFirst]
getfromdatabase[key_String]:=CallToCppCodeAndReturnListsOfValues[key]
getfromdatabase[expr_]:=ReleaseHold[ Hold[expr]/. key_String :>  getfromdatabase[key] ]

However, with this, I do not exactly get back the behavior of my function in ML <12.2. I need to check if this has unwanted side effects for some users. If yes, thorimur's procedure will be better for me, because it specifically adresses the problem with Transpose.

$\endgroup$

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.