String concatenation feature or bug?

Found this the usual way (meaning the hard way):

Background: 2024-10-13,06:25:21 beginLP _nextPos 0

		l_pos = '_' + _nextPos;
		_util.logTime("requestLogPos " + _nextPos + ", l_pos " + l_pos);
		
Background: 2024-10-13,06:25:21 requestLogPos 0, l_pos _

		l_pos = "_" + _nextPos;
		_util.logTime("requestLogPos " + _nextPos + ", l_pos " + l_pos);

Background: 2024-10-13,06:25:21 requestLogPos 0, l_pos _0

Characters can work, as in this:

	public function httpURL(php, rest) {
		return _host + php + '/' + G_uID + '_' + System.getTimer() + '_' + _httpRetrys + '/' + rest;
	}

		_util.logTime("requestLogPos l_pos " + l_pos + ", l_url " + l_url);
	
Background: 2024-10-13,06:25:21 requestLogPos l_pos logPos_0, l_url https://my.example.com/g1/lp/ffa0f28f1a9e91d2133a05fc4ff987c817fb2ab4_361254406_0/logPos_0

Multiple characters OK, single characters bad and have to use string as workaround. Normally it wouldn't matter except that strings are objects which add up after a while.

  • When I was a beginner programmer strings were still called Hollerith constants.

    There's actually several things happening at once, each of which is ambiguous in its own right.

    1. Character + number = character, because characters are not strings: 'A' + 1 = B

    Except when they are: Character + character = string: 'A' + '1' = A1

    2. The math addition operator is the same as the character / string concatenation operator.

    3. Duck typing without an explicit conversion method the compiler / VM decides what the final type will be based on how the operands are cast with respect to the operators.

    With a data type that could be either a string or a number but you can't be sure until you open the box, an operator that works with both strings and numbers, and data types that could be used more than one way depending on how they are combined it's maybe not a bug per se, but I wouldn't call it a great feature.

  • I'm not sure I understand the last paragraph. I think Monkey C behaves pretty reasonably similar to some other comparable languages

  • 1. Character + number = character, because characters are not strings: 'A' + 1 = B

    Except when they are: Character + character = string: 'A' + '1' = A1

    I wouldn't characterize the 2nd example as being proof that "characters are strings". I would say that the 2nd example shows that the result of adding a char and a char is the concatenation of the two values after converting them to strings.

    Consider "A" + 1:

    In Monkey C and JavaScript, the result is "A1", but that doesn't mean 1 is a string and I'm 100% that nobody would say it means 1 is a string, not even you. It means that s + x, where s is a string, causes x to be implicitly converted to string.

    It's worth noting that "A" + 1 is an error in python:

    TypeError: can only concatenate str (not "int") to str

    Perhaps this is a better strategy, but again it's too late for that.

    2. The math addition operator is the same as the character / string concatenation operator.

    Well, lots of languages are like this, like it or not. There's always certain tradeoffs to be made, such as conciseness/simplicity/intuitiveness vs. clarity/unambiguity.

    In C, two adjacent string literals (not character arrays / strings) are concatenated, but I bet that's not intuitive to most users of modern programming languages. I'm pretty sure I worked with C programmers with decades of experience who didn't know this. They def didn't know a bunch of other C esoterica, such as why macros that act as "inline functions" are written like #define FAKE_INLINE_FN(x) do { ... } while (0); Goes without saying the (very old) codebase was full of stuff like:

    #define BAD_MACRO(x) { func1(x); func2(x); }

    // ...

    if (x == 42)
      BAD_MACRO(x) // can't put a semicolon here!!!!1! (comment mine)
    else
      something_else(x);
     

    3. Duck typing without an explicit conversion method the compiler / VM decides what the final type will be based on how the operands are cast with respect to the operators.

    Like JavaScript, Monkey C seems to have been designed with a "worse is better" mindset (*), where the bar for entry is supposed to be low for new devs. Therefore, code like x + y is allowed to be valid for as many types as possible, even if it's not always clear what the result will be. (* At least an attempt was made. There's a bunch of cases where I could say they failed at this. For example: when x is null, if (x) is valid code, but if (x && y) is not valid.)

    It's worth noting that both js and monkey c have moved away from that ideal since their inception. (js has become stricter and stricter, and typescript was implemented on top of it. Monkey Types was implemented on top of Monkey C, but none of the weird quirks like the one I mentioned above are probably ever going to be fixed/changed, bc the designers don't want to break old code. Then again there's an open bug report which suggested that Floats and Numbers can't be properly compared with == in older devices, but they can in newer devices, so maybe some things are changing after all.

    With a data type that could be either a string or a number but you can't be sure until you open the box, an operator that works with both strings and numbers, and data types that could be used more than one way depending on how they are combined it's maybe not a bug per se, but I wouldn't call it a great feature.

    Well, if you're not sure what the result of x + y is when x is a character (or it doesn't make sense to you, or it doesn't do what you want in all cases), then the simplest solution is to refrain from using it. (Yeah hindsight is 20/20).

    Clearly the only reason you're using "+" with characters (instead of strings) all over the place is to save memory, which I would argue is misguided in the first place. I don't think it fits the purpose of the character data type in Monkey C.

  • 2. The math addition operator is the same as the character / string concatenation operator.

    "character / string concatenation operator".

    I wanted to bring this up before but I think the root cause of the original misunderstanding / false assumption is that you think "+" is the "character / string concatenation operator" as if characters can be (directly) concatenated.

    No, what's actually happening with an operation such as 'A' + '1' (which produces "A1") is that A and B are implicitly converted to strings, and those strings are concatenated.

    Similarly, just because "A" + 1 produces "A1", it doesn't mean that "+ is the number / concatenation operator either.

    The mistake here is really in assuming that characters should behave just like strings when you use "+" operator.

    Indeed, the documentation says:

    The + operator is also used to concatenate String values.
    Yes, it fails to explain all the cases where values are implicitly converted to strings when the + operator is used, which is a shame.
  • Point being that if

    Character +

    is sometimes string concatenation, then for consistency and predictability it seems like it should always be string concatenation. Unless that's not important.

    A lot of languages have different operators for addition and concatenation which avoids this problem. Using the same operator was a choice, not a necessity.

    Whether I use one character characters or one character strings doesn't affect the result. What matters in this constrained system is which is more efficient based on required code and storage. No, one character won't make much difference. Lots of string operations and then it makes sense to figure out which is better.

  • Whether I use one character characters or one character strings doesn't affect the result.

    Uh actually it does otherwise this topic would not exist.

    Point being that if

    Character +

    is sometimes string concatenation, then for consistency and predictability it seems like it should always be string concatenation

    Well "+" apparently has exactly 3 meanings in Monkey C:

    1) Addition, when used with 2 numeric types: Long, Number, Double, or Float

    2) Unicode value addition, resulting in a Char, in the case of c + n, where c is a Char and n is a Number or Long. (adding a char and float/double causes a runtime crash.)

    3) String concatenation

    Again, there is no such thing as (direct) "character concatenation" ('A' + '1') or (direct) "string and number concatenation" ("A" + 1). There is such a thing as one or both of the operands of x + y being implicitly converted to string when either x or y is a string, or when both are characters, which is sadly not mentioned in the documentation afaik. However, this kind of thing does happen in JavaScript (yeah, not a great example of a well-designed language), which is probably why the Monkey C designers are ok with it. Nobody refers to "+" as the "string / number / boolean / null concatenation operator" in js, either, even though, in js, adding a string and any of those other types results in a string consisting of the concatenation of the two values (after converting the 2nd value to a string).

    It's consistent and predictable if you understand the rules (yeah, I know it's kind of a cop-out / hindsight is 20-20 thing). Too bad it's not even informally documented. A formal language spec would be even better. Again, perhaps it would've been better if there was no implicit conversion to string, so the string concatenation operator could only be used with two strings, just like python. That would avoid problems like this at the cost of a certain kind of simplicity. (Arguably, this kind of thing is a plague in javascript, so I see where you're coming from.)

    Point being that if

    Character +

    is sometimes string concatenation, then for consistency and predictability it seems like it should always be string concatenation. Unless that's not important.

    Kinda contradicting yourself as you previously referred to "string / character concatenation" (implying "string concatenation" and "character concatenation" are two different, yet related things), then you say that "+" should always be string concatenation.

    Again the problem is your mischaracterization of what actually happens in the case of something like 'A' + '1'. It's not "character concatenation", it's string concatenation after the two operands have been implicitly converted to strings.

    Interestingly, you don't refer to "A" + 1 as "string/number concatenation", which again points to some inconsistency in your thinking.

  • I think something that everyone posting here needs to consider, is that Garmin won't change anything that impacts backward compatibility.  Sure, this could be fine for new devs, but consider all the apps developed over the last decade.  Many that don't use typechecking at all.  If a change impacts them, the only way they may know is by bad reviews ERA, etc.

  • I think something that everyone posting here needs to consider, is that Garmin won't change anything that impacts backward compatibility.

    I agree, and I alluded to that (see quote below).

    (I disagree that the type checker could help in general if Garmin decided to make the change the OP wants, same as it doesn't help in the OP's current situation in the first place. Again, the type checker isn't going to magically determine what the dev thinks should happen when you type 'A' + 0, especially if you add that to another string or pass it directly to System.println(), because in the first case, the resulting type is a string in both reality and with OP's desired behavior, and in the 2nd case, System.println() accepts an object so you can pass pretty much anything into it and the type checker can't tell you you're making a mistake.)

    the designers don't want to break old code.

    Monkey C is unlike widely used languages like Python and js, in the sense that it isn't possible to target a specific version (via transpilation or other means) while still supporting the latest devices, so Garmin won't make any backwards-incompatible changes unless they're bug fixes (like the number and float comparison thing)

    Then again, python 2 is deprecated so the big languages do make backwards incompatible changes and expect devs to adapt. (I can see why Garmin won't do the same with Monkey C.)

    Personally I don't see a need to change anything here.

    If Garmin decides this behavior is too confusing, they could always emit a warning when you add a Char to *anything*. (Or maybe just when you add a Char and a Numeric type.)

    It could be similar to some of the standard warnings in the C compiler which are displayed when someone uses a construct that's widely misused or misunderstood.

    This could be a good feature request.

  • Further proof that there's no such thing as "character concatenation" (in the sense of something analogous to "string concatenation") in Monkey C:

    System.println("Z" + false); // outputs "Zfalse" (like javascript)
    System.println('Z' + false); // run-time error

    It seems that adding 2 Chars and getting a concatenated String is the exception to the rule, not an example of a general rule.

    If you were still of the mindset that "+" is the "character/string concatenation operator" (despite the fact nobody calls it that), you might expect those two lines to do the same thing. Can't speak for anyone else but I never thought of it that way so I never would've expected those two lines to do the same thing. If you had asked me what the result of adding 2 Chars would be in monkey c before I read this topic, I would've said I'd have to try it and find out for myself.

    TL;DR Char in Monkey C is not a convenient data type that's interchangeable with String except that it also saves you some memory. It's a distinct data type that holds the unicode value of a single character and whose string representation (via toString()) is the character itself.

    I don't really know offhand of any popular language that has a char type which is also interchangeable with its string type as a general rule (what would be the point of having a separate char type, in that case?) (I could be wrong tho.)

  • If you were still of the mindset that "+" is the "character/string concatenation operator" (despite the fact nobody calls it that),

    Nobody except Garmin.

    In Monkey C objects add overhead that native types don't have. Just like you have to need to do double math to be willing to live with the 16 bytes a double requires, all strings are objects and a two character string takes 11 bytes. If all you need is a one character string then yes it will save space if you have to do it enough times.

    CPU's are deterministic, otherwise they would be useless. Computer programming languages should be as well. Inconsistent rules are not ok. We will just have to agree to disagree.