Logical inconsistencies in comparing an empty variable with 0

Consider the following script:

In general, if untitled script pic 358 is true for any values a and b,
either untitled script pic 359 must be true, or untitled script pic 360, or both.

And if both untitled script pic 358 and untitled script pic 362 are true, so must be: untitled script pic 360.

Currently within Snap! neither rule applies if a is an empty variable, and b is 0 - as demonstrated by the introductory script. It may also not apply with some other inputs, but I didn’t test any other cases (yet).

I guess the explanation would be like: untitled script pic 365 not being a meaningful comparison, it can not be true, with untitled script pic 367 being the negation of the latter, so that one must be true, etc.

While the latter may be nice and simple to implement, from a logical stance it stinks logicwise it’s doubleplus unfragrant.

I may have posted a similar report earlier, but couldn’t pinpoint it.

Besides, I failed to resize some of the blocks here. It used to be done by pressing the 100% / 75% / 50% “buttons” underneath any block as displayed in the right half of the screen, but the buttons now appear to operate on the next block, if at all - I’m using an iPad Pro gen. 2 , iOS 17.7.2, and Safari.

Be nice.

Also, one complicating factor is that the relational operators do lexicographic comparison of non-numeric strings, so it's not obvious to me what the right answers are for empty strings. Number or text?

We try to "do what I mean" about things like this, and generally, as in this instance, it makes Snap! easy to use for realistic problems at the occasional cost of peculiar edge cases.

I don’t think what I wrote, considered within its context, was particularly un-nice. However, being a friendly person, I rephrased it.

Neither. See below.

I have a different view on this:

  1. Any operation when faced with a nonsensical argument should raise an exception. In any realistic use case the nonsensical argument is probably the result of upstream error. Snap! pretending nothing is wrong won't help the user find the error.
  2. Testing edge cases is a important approach for debugging code. Therefore edge cases should be treated correctly by any programming language.

I actually totally agree with this, in general. Jens resists doing a lot of error checking because it would make Snap! slower. I think we should not only catch domain errors but provide a stack trace etc. (We do provide metaprogramming tools with which users could program their own stack trace, but that's not much help to the sort of users who routinely make such domain errors.)

The particular case of empty input slots, though, is fraught with policy issues, because we cram a lot of different meanings into an empty slot. This is mostly what I mean about providing a "do what I mean" experience to users' common programming patterns at the cost of hairing up edge cases.

I think you'd be happier if we'd chosen a different notation for "please fill this input slot for me," or not offered that feature at all but instead required explicit formal parameters in all lambda expressions. No question, that'd be a better choice in a professional language for adults. But, for our target users, I proudly stand by the choice we made (my design, so throw any tomatoes at me rather than at Jens).

In Logo, a text-based language, I used "?" to mean "please fill this slot," as in

If ASCII had had a :white_square_button: character, I would have used that instead, because it looks like a slot waiting to be filled.

Stop me if you've heard me say this before... If you show an eight-year-old "x+4=7, solve for x" you're likely to get a blank stare, but if you show the same kid ":white_square_button:+4=7, what number goes in the box?" you're likely to get the correct answer. This nugget of developmental psychology, which is used in pretty much every early math curriculum nowadays, is the reason we do what we do.

But the trouble is that sometimes a cigar empty input is just an empty value. That's the biggest problem about empty slots; they mean something different in a CALL (directly or indirectly via a HOF) from what they mean in other contexts. An additional issue is that we inherit from Scratch the goal of burying the distinction between numbers and strings of digits, and so in particular an empty slot sometimes means 0. Personally I think that's an overly simple choice; if anything, the empty slot in untitled script pic (7) should mean 1, not 0. I agree with the idea of treating a string of digits as a number, but that doesn't actually imply treating an empty string as a string of digits.

I would make a distinction between our edge cases and users' edge cases. So, for example, we should give correct behavior when a numeric input slot has an explicit 0 in it, e.g., untitled script pic (8), which should, in my view, give an error message -- although Jens keeps finding reasons why a project might generate that item number on purpose, e.g., looking for the neighbors of an array item.

But I don't want users to have to write explicit code to check for an empty input. In commercial software, which should check for every screwy edge case, the error-checking code comes to swamp the code that actually does the work of a procedure. That's not okay in a teaching language.

definitely agree.
question for qw23: why are you finding yourself comparing strings in the [] \> [] @<:> block? why is this important?

I don’t remember the exact case triggering this. In general I post a bug report if, while writing a piece of code, I happen to stumble upon some Snap! feature that appears flawed. Consider it a service to the developers - it’s up to them if they agree and, if so, deem it important enough to try and fix it.
Sometimes the root of the “bug” turns out to be a fundamental design decision. Exploring these, and the underlying considerations, assumptions, and paradigms, I personally find particularly interesting.

And how about you, @sathvikrias - what’s driving you to post in this forum?

That may all be true, but it doesn't explain why Snap! apparently doesn't substitute an empty value with the same value in every comparison.

I agree. However, that doesn't mean that native Snap! blocks should skip such checks.

You're right; I don't understand why you got those answers in your experiment. The three simple comparisons all reporting False is already problematic. I bet if they were made consistent, the three compound comparisons would follow.

If we take the empty string as representing 0, then = should report True. If we take it as representing itself (an empty string), then I think < should report True, because the empty string comes lexicographically before any other string, including "0". That's the answer I would give. So yeah, I agree that this is a bug. But I stand by my guess that it comes about because of an explicit effort to make some other case come out right. :~)

the reason why he's seeing this is because of our conscientious decision against having the concept of nil (or null) in the Snap! programming language. The more Snap! has matured and the more its domain has grown I actually believe that something like nothing as a value would probably be very beneficial, because it would handle all these cases consistently, and not in some broken JS way.

OTOH, I have to say that in practice - even in school / college - all these edge cases around null-ish / zero-ish / false-ish / empty-ish values hardly ever surface as a problem. It's only the Besserwisser (who often have actual handles literally named "hairsplitter" who obsess over this).

Yeah, that's roughly what I said too.

I'd be open to discussing having a null value that's distinct from the empty string. Still, even then there will be such a thing as an empty string, and I think it should sort before any other text value.