64

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"
  • 35
    which "yy" should be removed? – gashach Oct 28 '15 at 18:54
  • 12
    If I go with the behavior of the '+' sign, then the right most makes sense to to. – Digvijay Yadav Oct 28 '15 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 Oct 28 '15 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 Oct 28 '15 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 Oct 28 '15 at 22:40
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.

  • 2
    For sets (and multi-sets) subtraction makes sense, because unlike sequences, the order of the element doesn't matter. – CodesInChaos Oct 28 '15 at 23:16
  • @CodesInChaos: I added a mention of them, but I wasn’t really comfortable putting sets as an example of a group—I don’t believe they form one, as you can’t generally construct the inverse of a set. – Jon Purdy Oct 29 '15 at 1:38
  • 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 Oct 29 '15 at 9:12
  • 2
    @Darkhogg: Right! PHP borrowed . from Perl; it’s ~ in Perl6, possibly others. – Jon Purdy Oct 29 '15 at 13:03
  • 1
    @MartinBeckett but you can see that the behaviour might be confusing with .text.gz.text... – Boris the Spider Oct 29 '15 at 15:46
38

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.

  • 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 Oct 28 '15 at 19:17
  • 28
    @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 – Mason Wheeler Oct 28 '15 at 19:34
  • 13
    @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. – Mason Wheeler Oct 29 '15 at 10:32
  • 6
    @JanDvorak: There's nothing particularly "Haskelly" about that; that's basic strong typing. – Mason Wheeler Oct 29 '15 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 Oct 30 '15 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);
}
  • 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 Oct 29 '15 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
  • 11
    Interesting - so it chooses to remove the first occurrence? A good example for a completely counter-intuitive behavior. – Hulk Oct 29 '15 at 11:23
  • 9
    Hence, we have that ('ABABABABA' + 'B') - 'B' is nowhere near the same as the starting value 'ABABABABA'. – a CVn Oct 29 '15 at 13:21
  • 3
    @MichaelKjörling OTOH, (A + B) - A == B for every A and B. Can I call that a left subtraction? – John Dvorak Oct 29 '15 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. – John Dvorak Oct 29 '15 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 Oct 29 '15 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'])
  • 2
    This is more likely because set subtraction is defined in math, but set addition is not. – Mehrdad Oct 29 '15 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 Oct 29 '15 at 22:00
  • 1
    set('abc') ^ set('bcd') returns set(['a', 'd']), if you're asking about the symmetric difference. – Aaron Hall Oct 29 '15 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.

  • 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 Oct 29 '15 at 9:09
  • 3
    @smci that's just multiplication-as-repeated-addition, surely? – jonrsharpe Oct 29 '15 at 10:08
  • what is the proper operator to concatenate spaceshuttles? – Mr.Mindor Oct 29 '15 at 20:29
  • 4
    @Mr.Mindor backspace ... to remove the space between the spaceshuttles. – YoungJohn Oct 30 '15 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.