62

The plus sign + is used for addition and for string concatenation, but its companion: the minus sign, -, is generally not seen for trimming of strings or some other case other than subtraction. What could be the reason or limitations for that?

Consider the following example in JavaScript:

var a = "abcdefg";
var b = "efg";

a-b == NaN
// but
a+b == "abcdefgefg"
28
  • 35
    which "yy" should be removed?
    – gashach
    Commented Oct 28, 2015 at 18:54
  • 12
    If I go with the behavior of the '+' sign, then the right most makes sense to to. Commented Oct 28, 2015 at 18:56
  • 46
    It is bad enough that the binary + operator is overloaded with the two totally unrelated meanings “numeric addition” and “string concatenation”. Thankfully, some languages provide a separate concatenation operator such as . (Perl5, PHP), ~ (Perl6), & (VB), ++ (Haskell), …
    – amon
    Commented Oct 28, 2015 at 19:11
  • 6
    @MasonWheeler They use -> (think dereferencing member access in C, since virtual method calls necessarily involve pointer-like indirection). There is no law of language design that requires method calls/member access to use a . operator, though it is an increasingly common convention. Did you know that Smalltalk has no method call operator? Simple juxtaposition object method is sufficient.
    – amon
    Commented Oct 28, 2015 at 19:28
  • 20
    Python does overload minus, for set subtraction (and it can be overloaded in user-defined types as well). Python sets also overload most of the bitwise operators for intersection/union/etc.
    – Kevin
    Commented Oct 28, 2015 at 22:40

6 Answers 6

116

In short, there aren’t any particularly useful subtraction-like operations on strings that people have wanted to write algorithms with.

The + operator generally denotes the operation of an additive monoid, that is, an associative operation with an identity element:

  • A + (B + C) = (A + B) + C
  • A + 0 = 0 + A = A

It makes sense to use this operator for things like integer addition, string concatenation, and set union because they all have the same algebraic structure:

1 + (2 + 3) == (1 + 2) + 3
1 + 0 == 0 + 1 == 1

"a" + ("b" + "c") == ("a" + "b") + "c"
"a" + "" == "" + "a" == "a"

And we can use it to write handy algorithms like a concat function that works on a sequence of any “concatenable” things, e.g.:

def concat(sequence):
    return sequence.reduce(+, 0)

When subtraction - gets involved, you usually talk about the structure of a group, which adds an inverse −A for every element A, so that:

  • A + −A = −A + A = 0

And while this makes sense for things like integer and floating-point subtraction, or even set difference, it doesn’t make so much sense for strings and lists. What is the inverse of "foo"?

There is a structure called a cancellative monoid, which doesn’t have inverses, but does have the cancellation property, so that:

  • A − A = 0
  • A − 0 = A
  • (A + B) − B = A

This is the structure you describe, where "ab" - "b" == "a", but "ab" - "c" is not defined. It’s just that we don’t have many useful algorithms that use this structure. I guess if you think of concatenation as serialisation, then subtraction could be used for some kind of parsing.

12
  • 2
    For sets (and multi-sets) subtraction makes sense, because unlike sequences, the order of the element doesn't matter. Commented Oct 28, 2015 at 23:16
  • 12
    Actually, the + operation is also commutative for numbers, i.e. A+B == B+A, which makes it a bad candidate for string concatenation. This, plus the confusing operator precedence renders using + for string concatenation a historical mistake. However, it’s true that using - for whatever string operation made things much worse…
    – Holger
    Commented Oct 29, 2015 at 9:12
  • 2
    @Holger: Hmm, I agree in part. Perhaps * or something would be better, as people familiar with linear algebra will already know that multiplication is not necessarily commutative. But there is also a strong appeal to natural language—“plus” has a strong association with “and”, even though for many monoids (and as far as operator precedence goes) it’s more like an “or” operation: addition is like bitwise union with carry, for example.
    – Jon Purdy
    Commented Oct 29, 2015 at 10:08
  • 2
    @Darkhogg: Right! PHP borrowed . from Perl; it’s ~ in Perl6, possibly others.
    – Jon Purdy
    Commented Oct 29, 2015 at 13:03
  • 1
    @MartinBeckett but you can see that the behaviour might be confusing with .text.gz.text... Commented Oct 29, 2015 at 15:46
37

Because concatenation of any two valid strings is always a valid operation, but the opposite is not true.

var a = "Hello";
var b = "World";

What should a - b be here? There's really no good way to answer that question, because the question itself isn't valid.

11
  • 31
    @DigvijayYadav, if you remove 5 mangoes from 5 apples does there have to then be a counter of -5 mangoes? Does it do nothing? Can you define this well enough that it can be broadly accepted and put into all compilers and interpreters of languages to use this operator in this form? That is the big challenge here.
    – JB King
    Commented Oct 28, 2015 at 19:17
  • 29
    @DigvijayYadav: So you just described two possible ways to implement this, and there's a good argument to consider each one as valid, so we're already making a mess of the idea of specifying this operation. :P Commented Oct 28, 2015 at 19:34
  • 14
    @smci Seems to me 5 + False should obviously be an error, since a number is not a boolean and a boolean is not a number. Commented Oct 29, 2015 at 10:32
  • 7
    @JanDvorak: There's nothing particularly "Haskelly" about that; that's basic strong typing. Commented Oct 29, 2015 at 13:42
  • 5
    @DigvijayYadav So (a+b)-b = a (hopefully!), but (a-b)+b is sometimes a, sometimes a+b depending on if b is a substring of a or not? What madness is this?
    – user198399
    Commented Oct 30, 2015 at 14:45
28

Because the - operator for string manipulation does not have enough "semantic cohesion." Operators should only be overloaded when it is absolutely clear what the overload does with its operands, and string subtraction doesn't meet that bar.

Consequently, method calls are preferred:

public string Remove(string source, string toRemove)
public string Replace(string source, string oldValue, string newValue)

In the C# language, we use + for string concatenation because the form

var result = string1 + string2 + string3;

instead of

var result = string.Concat(string1, string2, string3);

is convenient and arguably easier to read, even though a function call is probably more "correct," from a semantic standpoint.

The + operator can really only mean one thing in this context. This isn't as true for -, since the notion of subtracting strings is ambiguous (the function call Replace(source, oldValue, newValue) with "" as the newValue parameter removes all doubt, and the function can be used to alter substrings, not just remove them).

The problem, of course, is that the operator overload is dependent on the types being passed to the operator, and if you pass a string where a number should have been, you may get a result you didn't expect. In addition, for many concatenations (i.e. in a loop), a StringBuilder object is preferable, since each use of + creates a brand new string, and performance can suffer. So the + operator isn't even appropriate in all contexts.

There are operator overloads that have better semantic cohesiveness than the + operator does for string concatenation. Here's one that adds two complex numbers:

public static Complex operator +(Complex c1, Complex c2) 
{
    return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}
1
  • 8
    +1 Given two strings, A and B, I can think of A-B as "remove a trailing B from the end of A," "remove an instance of B from somewhere in A," "remove all instances of B from somewhere in A," or even "remove all characters found in B from A."
    – Cort Ammon
    Commented Oct 29, 2015 at 1:02
8

The Groovy language does allow -:

println('ABC'-'B')

returns:

AC

And:

println( 'Hello' - 'World' )

returns:

Hello

And:

println('ABABABABAB' - 'B')

returns:

AABABABAB
8
  • 12
    Interesting - so it chooses to remove the first occurrence? A good example for a completely counter-intuitive behavior.
    – Hulk
    Commented Oct 29, 2015 at 11:23
  • 9
    Hence, we have that ('ABABABABA' + 'B') - 'B' is nowhere near the same as the starting value 'ABABABABA'.
    – user
    Commented Oct 29, 2015 at 13:21
  • 3
    @MichaelKjörling OTOH, (A + B) - A == B for every A and B. Can I call that a left subtraction? Commented Oct 29, 2015 at 14:39
  • 2
    Haskell has ++ for concatenation. It works on any list and a string is just a list of characters. It also has \\, which removes the first occurence of every element in the right argument from the left argument. Commented Oct 29, 2015 at 14:42
  • 3
    I feel like these examples are exactly why there should be no minus operator for strings. It's inconsistent and not intuitive behavior. When I think of "-" I sure don't think, "remove the first instance of the matching string, if it occurs, otherwise just do nothing."
    – enderland
    Commented Oct 29, 2015 at 17:18
6

The plus sign probably contextually makes sense in more cases, but a counter-example (perhaps an exception that proves the rule) in Python is the set object, which provides for - but not +:

>>> set('abc') - set('bcd')
set(['a'])
>>> set('abc') + set('bcd')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'set' and 'set'

It doesn't make sense to use the + sign because the intention could be ambiguous - does it mean set intersection or union? Instead, it uses | for union and & for intersection:

>>> set('abc') | set('bcd')
set(['a', 'c', 'b', 'd'])
>>> set('abc') & set('bcd')
set(['c', 'b'])
3
  • 2
    This is more likely because set subtraction is defined in math, but set addition is not.
    – user541686
    Commented Oct 29, 2015 at 9:28
  • The use of "-" seems dodgy; what's really needed is a "but not" operator which would also be useful when performing bitwise arithmetic with integers. If 30 ~& 7 were 24, then using ~& with sets would fit nicely with & and | even though sets lack a ~ operator.
    – supercat
    Commented Oct 29, 2015 at 22:00
  • 1
    set('abc') ^ set('bcd') returns set(['a', 'd']), if you're asking about the symmetric difference.
    – Aaron Hall
    Commented Oct 29, 2015 at 22:35
3

"-" is used in some compound words (for example, "on-site") for joining the different parts into the same word. Why don't we use "-" for joining different strings together in programming languages? I think it would make perfect sense! To hell with this + nonsense!

However, let's try looking at this from a bit more abstract angle.

How would you define string algebra? What operations would you have, and what laws would hold for them? What would their relations be?

Remember, there may be absolutely no ambiguity! Every possible case must be well defined, even if it does mean saying it is not possible to do this! The smaller your algebra is, the easier this is done.

For example, what does it actually mean to add or subtract two strings?

If you add two strings (for example, let a = "aa" and b = "bb"), would you get aabb as the result of a + b?

How about b + a? Would that be bbaa? Why not aabb? What happens if you subtract aa from the result of your addition? Would your string have a concept of negative amount of aa in it?

Now go back to the beginning of this answer and substitute spaceshuttle instead of the string. To generalize, why is any operation defined or not defined for any type?

The point I'm trying to make is, that there is nothing stopping you from creating an algebra for anything. It might be hard to find meaningful operations, or even useful operations for it.

For strings, concatenating is pretty much the only sensible one I've ever come across. Doesn't matter what symbol is used to represent the operation.

4
  • 1
    "For strings, concatenating is pretty much the only sensible one I've ever come across". Then do you disagree with Python's 'xy' * 3 == 'xyxyxy'?
    – smci
    Commented Oct 29, 2015 at 9:09
  • 4
    @smci that's just multiplication-as-repeated-addition, surely?
    – jonrsharpe
    Commented Oct 29, 2015 at 10:08
  • what is the proper operator to concatenate spaceshuttles?
    – Mr.Mindor
    Commented Oct 29, 2015 at 20:29
  • 4
    @Mr.Mindor backspace ... to remove the space between the spaceshuttles.
    – YoungJohn
    Commented Oct 30, 2015 at 13:48

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.