Is it possible to define '+' operator for string as '*' do for them?

I’d like to use ‘+’ operator for string concatenation.
but it is ‘*’ in Julia.

If possible I’d like to define ‘+’ as alias of ‘*’ if opland are strings.
because some insufficient definition cause some side effect.

Thanks in advance!!

No, that would be type piracy. Do not do this!

4 Likes

Of course, you can:

Base.:+(a::String, b::String)=a*b

But there is a clear mathematical reason for the choice in Julia: generally, “+” is considered to be always commutative, while “*” (e.g. for matrices) may be not commutative, and string concatenation is clearly not commutative. Also, yes, this would be type piracy which may render your code harmful if it is used by others.

14 Likes

See also the Julia FAQ: Why does Julia use * for string concatenation? Why not + or something else?

In general, when you learn a new language, you should try to learn the idiomatic “spelling” of things in that language. Trying to write code in Julia that superficially resembles some other language (e.g. Python) is just going to get you into trouble — even if there are no technical issues (ala type piracy), your code won’t fit nicely in to the surrounding ecosystem.

18 Likes

It is however possible to create your own string type with whatever properties you like, without breaking any norms like “type piracy”:

struct MyString <: AbstractString
    s::String
end

macro s_str(e)
    :(MyString($e))
end

Base.getindex(s::MyString, arg::Int) = getindex(s.s, arg)
Base.iterate(s::MyString, state::Int=1) = iterate(s.s, state)
Base.:+(s1::MyString, s2::MyString) = MyString(s1*s2)
Base.:*(n::Integer, s::MyString) = MyString(s.s^n)

julia> s1 = s"string1"
"string1"  # a MyString

julia> s2 = s"string2"
"string2"  # a MyString

julia> s1 + s2
"string1string2"  # also a MyString

julia> 4s1
"string1string1string1string1"

# but, while many operations on Strings work for MyString, they may still return String
# not MyString, unless you handle it specially.

julia> s3 = join((s1,s2), ", ")
"string1, string2"

julia> typeof(s3)
String
5 Likes

It’s even easier to create your own +! No piracy, and you don’t need to forward all the other string methods.

julia> a::String + b::String = a * b  # may need to restart the session for this line to work
+ (generic function with 1 method)

julia> "a" + "b"
"ab"

julia> 2 + 2
ERROR: MethodError: no method matching +(::Int64, ::Int64)
You may have intended to import Base.:+
The function `+` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  +(::String, ::String)
   @ Main REPL[1]:1

Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

julia> a + b = Base.:+(a, b)   # fall back to the Base version
+ (generic function with 2 methods)

julia> 2+2
4

It’s a nice trick for designing DSLs.

In any case, “just use *” is the correct answer of course.

11 Likes

Thank you j-fu for your code and your advice.
I found that your code works well for me.

but “+” looks more natural operator for string concatenation I suppose…

1 Like

Thank you for your suggestion cstjean.
I learned a lot from your code.

I appreciate it!!

Well, but ‘*’ looks strange for me…
thanks anyway

I understand your point.
thank you for your information.
but I am going to use my code for internal use only.

thanks sgaure,
maybe you are genius!
Although I will use j-fu method for now, I might use your method in some other issues.

Take also a look at this thread and the tiny Julia package PlusPlus.jl with just three lines of code.

1 Like

Another thing is that string concatenation may not be too useful in any language.

Note you can do e.g.:

println(a, b) # and/or use string interpolation

it will be faster than doing concatenation first explicitly: c = a * b first, or c = String(a, b) another possibility if you do not like *, and it’s similar syntax to println, but result can be used generally, and a and b can be strings, or e.g. integers, unlike for *.

Welcome to the Julia community!

1 Like

It looks strange to me, too, and I think it was a particularly poor choice in the design of the Julia language. And, quite frankly, I consider the “explanation” borderline offensive… but the important point is

That is, don’t try to bend the language to existing idioms in languages you may already be familiar with. Just learn the syntax and the idioms native to Julia, and take them as a given.

And, not really directly related but even more important (hence my original curt response): everyone should be maximally allergic to defining methods for functions they don’t own on a set of arguments they don’t fully own (aka “type piracy”). Never, ever do it unless you really understand the implications of what you’re doing. Without that, it can easy lead to catastrophic correctness bugs, and it continues to be the source of major latency issues within the language by causing invalidations.

9 Likes

Honestly I didn’t get how troublesome + would be for strings in Julia until recently — when I started really hammering on reductions. It’s not all that unusual to consider concatenating a bunch of strings with a reduction. But if we used + then folks could (and I’d wager would) end up writing sum(strs) (or more straightforwardly reduce(+, strs)) to do this. And that might jumble up the orderings!

This is in contrast to using prod or reduce(*, strs), which is guaranteed to preserve the orderings. I still wouldn’t encourage doing this, but at least it won’t do the wrong thing!

14 Likes

Funny thing, considering the number of Excel users, that no-one ever asks for ‘&’ for string concatenation in Julia…

5 Likes

Thats a replicode thing

Why? reduce assumes that the operator is associative, but not commutative — the implementation can change the associativity, but not the ordering. And * is associative.

Why, what am I missing here? The only thing I could object to is that in my opinion * carries no connotation of (non)commutativity at all, i.e. it could go either way (e.g. matrix multiplication, convolution).

I don’t follow…

It better not! There is nothing in the Julia language that would enforce some kind of commutativity of +. I can easily imagine a type for which a direct sum is implemented via + in lieu of the more correct – and direct sums are not commutative in the sense most people would expect (only up to isomorphisms: For two matrices A and B, A ⨁ B is a different matrix than B ⨁ A, even if they’re isomorphic, with a change of basis). And there are certainly many colloquial uses of “plus” that are inherently non-commutative (including string concatenation: “put your first name plus last name in this field”)

The docstrings of neither sum nor prod say anything about this explicitly, but reduce guarantees preserving the order (assuming the underlying collection is ordered). It only makes assumptions about associativity. I would expect this behavior to translate back to sum and prod, as I’m sure it does.

I don’t think there would be anything wrong if we had chosen + for string concatenation, or if people were using sum(strings) to concatenate strings. At the end of the day it should boil down to “Julia uses * for string concatenation”, and leave it at that. I wish Julia wouldn’t be the only language using *, but had chosen any of the “prior art” of +, ., .., ~, &, \\. But okay. Just that ridiculous “abstract algebra” justification should be stricken from the record. Strings are not a field of vectors with a bilinear product! None of that mathematical structure applies even remotely in this situation. And of course, most fields are commutative, so 99.9% of all * symbols are just as commutative as + symbols (despite, as a quantum physicist, non-commutative algebras being my bread and butter). I can guarantee that nobody ever would have been confused by a non-commutive + for string concatenation. Alas. /end rant

10 Likes