Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

StringJoin with ToString

21 views
Skip to first unread message

replic...@gmail.com

unread,
Apr 3, 2009, 9:15:40 PM4/3/09
to
It is quite interesting why StringJoin does not apply ToString to
those arguments which are non-string. I've overridden the built-in
StringJoin, to save a few unnecessary ToStrings by the standard method
(the context specifications are necessary for packaging):

Unprotect[System`StringJoin];
$UseNewStringJoin = True;
System`StringJoin[expr___] :=
Block[{$UseNewStringJoin = False},
StringJoin@(ToString /@ {expr})] /; TrueQ[$UseNewStringJoin];
Protect[$UseNewStringJoin];
Protect[System`StringJoin];

This seems to work (after loading the package) for any cases, e.g.:

In[2]:= StringJoin[1, 2, 3]

Out[2]= "123"

In[3]:= 4 <> 5 <> 6

Out[3]= "456"

but has a strange effect on messages:

In[5]:= Append[dummy, 2]

During evaluation of In[5]:= Append::normal: {Nonatomic expression
expected at position , {1}, in , {Append[dummy,2]}, .} >>

Out[5]= Append[dummy, 2]

that is: the message strings are displayed in a non-joined list
format.
The code below shows an other modified StringJoin, that gives the
exact same results (after initiating a fresh kernel):

Unprotect[System`StringJoin];
System`StringJoin[expr___]:=Module[{str=""},
Scan[(Print[#]; str=StringInsert[str,ToString[#],-1])&,{expr}];
str
];
Protect[System`StringJoin];

Now I've inserted a Print[#]; inside the function to see what's
happening. This reveals some internal calculation that is done during
the evaluation of the erroneous call of Append[dummy, 2], which I
don't really understand (did not copy here for size constraints).
Apart from this strange sideeffect on messages (which seems to be
harmless), both functions work as expected. Does anyone have any idea
what is the exact cause of this behaviour and how to overcome it?

Thanks

Istvan Zachar

replic...@gmail.com

unread,
Apr 5, 2009, 6:36:06 AM4/5/09
to

Albert Retey

unread,
Apr 5, 2009, 6:39:01 AM4/5/09
to
H,
.

I think you would be much better off to leave the System functions as
they are and define a tostring or mytostring that behaves as you want. I
have no idea what causes the problems with messages that you see, but I
suspect that there are many more problems that eventually could arise
but you haven't yet discovered because you presumably only tried a very
limited part of Mathematica's full functionality. On the other hand it
is very easy to construct cases where your "improved" ToString behaves
different from the original and so will potentially cause troubles, here
is just one:

If[Head[Quiet[StringJoin[1,2,3]]] == StringJoin,
Print["this was no string!"]]


hth,

albert

ADL

unread,
Apr 5, 2009, 6:40:18 AM4/5/09
to
With the following definitions, your idea appears to work (at least in
a few examples I tested):

ClearAll[toString];
toString[x_?NumericQ, format___] := ToString[x, format];
toString[x_[y__], format___] := x @@ (toString /@ {y});
toString[x_Symbol, format___] := ToString[x, format];
toString[x_, format___] := x;

Unprotect[System`StringJoin];
Unprotect[$UseNewStringJoin];


$UseNewStringJoin = True;
System`StringJoin[expr___] :=
Block[{$UseNewStringJoin = False},

StringJoin @@ (toString /@ {expr})] /; TrueQ[$UseNewStringJoin];


Protect[$UseNewStringJoin];
Protect[System`StringJoin];

The problem was the way ToString works, which is incompatible with
intermediate formatting functions. The definition of "toString" does
the job.


In[12]:= StringJoin[{1, 2, 3}] // InputForm

Out[12]//InputForm=
"123"

In[13]:= StringJoin[1, a, 3] // InputForm

Out[13]//InputForm=
"1a3"

In[17]:= StringJoin[1, "2", \[Pi]] // InputForm

Out[17]//InputForm=
"12Pi"

In[15]:= Append[dummy, 2]

During evaluation of In[15]:= Append::normal: Nonatomic expression
expected at position 1 in Append[dummy,2]. >>

Out[15]= Append[dummy,2]


ADL


On 4 Apr, 03:15, replicator...@gmail.com wrote:
> It is quite interesting why StringJoin does not apply ToString to
> those arguments which are non-string. I've overridden the built-in
> StringJoin, to save a few unnecessary ToStrings by the standard method
> (the context specifications are necessary for packaging):
>

...
>
> Thanks
>
> Istvan Zachar


David Bailey

unread,
Apr 5, 2009, 6:40:29 AM4/5/09
to
I would never routinely override built-in commands - even to extend
their functionality. There are lots of reasons for this, but not least
is the fact that code that uses this trick is very hard to read. Why not
simply write a function ExtendedStringJoin.

Part of Mathematica is written using other Mathematica functions - so
problems such as you report are not unexpected.

On the face of it, adding extra functionality to a built-in might not be
expected to cause problems, but it is easy to write code that would be
disrupted by your change:

ss = Quiet[2 <> 3];
Map[ToString[#^2] &, ss]

Who knows what sort of code lies inside Mathematica :)

David Bailey
http://www.dbaileyconsultancy.co.uk

replic...@gmail.com

unread,
Apr 8, 2009, 2:48:56 AM4/8/09
to
Thanks AES,

with your extension the code works fine so far.
For those who warned me not to play with system symbols: I use this
modified StringJoin in a program where it can produce onyl two types
of string outputs: either one that is printed for verbosing (i.e. just
for me to check behaviour of the code) or a string that is used as a
path of some file to be saved.
In both situations any erroneous behaviour would be immediatley
recognized, since either the text printed would be garbled, or the
file/directory would not be created, thus I do not afraid using the
modified StringJoin in this "controlled" environment.

Istvan

replic...@gmail.com

unread,
Apr 8, 2009, 2:49:07 AM4/8/09
to
I was too hasty posting my reply:

First, I'm extremely sorry to mix up your name, ADL.
Second: this code of you does only half of the job: It can not convert
lists to strings verbatim,
that is toString[{1,2,3}] gives {"1","2","3"} instead of "{1,2,3}".
Which (as it seems to me) is exactly the problem causing the wrong
pinting of Messages. Still I wonder whether there is a reliable
solution to apply ToString to inputs of StringJoin without explicitely
writing it each time I call StringJoin.
If I use a different name (e.g. ExtendedStringJoin, as it was
suggested), I loose the effective shorthand version (<>), as it would
still use the original function. Is there a way to overwrite at least
the definitions of the infix operator?

Istvan


On Apr 5, 12:40 pm, ADL <alberto.dilu...@tiscali.it> wrote:
> With the following definitions, your idea appears to work (at least in
> a few examples I tested):
>
> ClearAll[toString];
> toString[x_?NumericQ, format___] := ToString[x, format];
> toString[x_[y__], format___] := x @@ (toString /@ {y});
> toString[x_Symbol, format___] := ToString[x, format];
> toString[x_, format___] := x;
>
> Unprotect[System`StringJoin];
> Unprotect[$UseNewStringJoin];
> $UseNewStringJoin = True;
> System`StringJoin[expr___] :=
> Block[{$UseNewStringJoin = False},

> StringJoin@@ (toString /@ {expr})] /; TrueQ[$UseNewStringJoin];


> Protect[$UseNewStringJoin];
> Protect[System`StringJoin];
>
> The problem was the way ToString works, which is incompatible with
> intermediate formatting functions. The definition of "toString" does
> the job.
>

> In[12]:=StringJoin[{1, 2, 3}] // InputForm
>
> Out[12]//InputForm=
> "123"
>
> In[13]:=StringJoin[1, a, 3] // InputForm
>
> Out[13]//InputForm=
> "1a3"
>
> In[17]:=StringJoin[1, "2", \[Pi]] // InputForm


>
> Out[17]//InputForm=
> "12Pi"
>
> In[15]:= Append[dummy, 2]
>
> During evaluation of In[15]:= Append::normal: Nonatomic expression
> expected at position 1 in Append[dummy,2]. >>
>
> Out[15]= Append[dummy,2]
>
> ADL
>
> On 4 Apr, 03:15, replicator...@gmail.com wrote:
>

> > It is quite interesting whyStringJoindoes not apply ToString to

David Bailey

unread,
Apr 10, 2009, 4:59:33 AM4/10/09
to
replic...@gmail.com wrote:

> If I use a different name (e.g. ExtendedStringJoin, as it was
> suggested), I loose the effective shorthand version (<>), as it would
> still use the original function. Is there a way to overwrite at least
> the definitions of the infix operator?

You can't overwrite it (as far as I know), and in some ways that would
be as bad as redefining the actual function, but you can co-opt an
unused operator:

In[1]:= SetAttributes[CirclePlus, Flat];
SetAttributes[CirclePlus, OneIdentity];

In[3]:= CirclePlus[x___] := f[x]

In[4]:= a\[CirclePlus]b\[CirclePlus]c\[CirclePlus]d

Out[4]= f[a, b, c, d]

David Bailey
http://www.dbaileyconsultancy.co.uk

ADL

unread,
Apr 11, 2009, 3:56:13 AM4/11/09
to
Istvan, actually toString was not meant to replace ToString, but only
to support your StringJoin functionality in a "hidden" way.
Also, StringJoin[{1, 2, 3}] gives "123", so that only ToString[{1, 2,
3}] can return "{1,2,3}".

In any case, I would also support David Bailey's suggestion. When I
had to do similar things in the past, I used exactly CirclePlus, which
is also rather handy to type in the GUI: <Esc>c+<Esc>.

In the same category, you also find CircleMinus <Esc>c-<Esc>,
CircleDot <Esc>c.<Esc>, CircleTimes <Esc>c*<Esc> and perhaps some
more.

So you can avoid tampering with built-in functions and keep effective
shorthands. Unfortunately, only <> works also in the text interface,
but you might use NonCommutativeMultiply (**), if you do not already
use it for other reasons:

Unprotect[NonCommutativeMultiply];
NonCommutativeMultiply[expr___]:=StringJoin@@(toString/@{expr});
Protect[NonCommutativeMultiply];

1 ** "b" ** c // InputForm
"1bc"


ADL


On 8 Apr, 08:49, replicator...@gmail.com wrote:
> I was too hasty posting my reply:
>
> First, I'm extremely sorry to mix up your name, ADL.
> Second: this code of you does only half of the job: It can not convert
> lists to strings verbatim,
> that is toString[{1,2,3}] gives {"1","2","3"} instead of "{1,2,3}".

> ...
> Istvan Zachar


replic...@gmail.com

unread,
Apr 14, 2009, 6:12:38 AM4/14/09
to
Dear All,

I've considered your warnings, but - mainly because of my curiosity -
I continued to find a solution to the problem. Combining information
from here and my earlier tries, I've managed to come up with a stable
code that is quite short:

toString[expr_String] := expr;
toString[expr_] := ToString[expr];

Unprotect[System`StringJoin];
Attributes[System`StringJoin] = {};
System`StringJoin[expr___] := StringJoin[{expr}];
System`StringJoin[expr_List] := Module[{s = ""}, Scan[(s = StringInsert
[s, toString[#], -1]) &, expr]; s]
Protect[System`StringJoin];

Extensive testing shows that this works as expected (error messages
are returned unchanged, Import[file, "Lines"] works correctly, etc.).
Of course, there are effects, which must be considered, e.g. David's
example:

ss = Quiet[2 <> 3];
Map[ToString[#^2] &, ss]

this indeed would give 23 instead of 49, but that is definitely not
disrupting, since one who uses the new StringJoin *should be aware*
that it actually joins non-strings as well. I wouldn't consider it a
problem, just a point, where it must be kept in mind what the extended
StringJoin can do. To make my point clearer: no one would expect the
following

ss = Quiet[2\[CirclePlus]3];


Map[ToString[#^2] &, ss]

to result other than "4"\[CirclePlus]"9" IFF there is no assigned
meaning of CirclePlus. Although if one uses CirclePlus to actually do
something, then the result of the first line won't be the same as the
input: 2\[CirclePlus]3. As one would expect.

For the other problematic example of Albert (modified here a bit):

If[Head[Quiet[StringJoin[1, 2, 3]]] === StringJoin, Print["this was no
string!"], Print["string ok"]]

This should (in case of the new StringJoin) result "string ok", while
in case of the original StringJoin: "this was no string!". But the
former is exactly the functionality I would like to gain, namely to
get a string from StringJoin even in case of non-string input.

So, of course one must be cautios, but I wouldn't consider these cases
problematic.
Could anyone find any abnormal/unexpected behaviour of this new
function?

Thanks for your help,

Istvan

0 new messages