<p>I'm designing a language intended to teach kids and other beginners how to program. It's just a fork of Python with a few extra features to make it easier to learn.</p>
<p>I've noticed students make one specific mistake very often: They'll write code like this:</p>
<pre class="lang-py prettyprint-override"><code>number = input()
if number == 5 or 12:
    print &quot;You guessed the correct number!&quot;
</code></pre>
<p>This of course won't work of course, since it parses as <code>(number == 5)</code> or <code>12</code> and <code>12</code> is always truthy, thus you will always enter the if regardless of the value of number.</p>
<p>Students make this mistake very often, and it's especially deadly because it does not produce any kind of error, and since they are not very experienced in debugging they can spend days unable to track down the issue before they ask for help.</p>
<p>I could of course special case this syntax and transform it into <code>number in (5, 12)</code>, but the goal here is not to just make it work but help the students build an intuition for how expressions and statements work, and ease the transition to real python later in the course.</p>
<p>Are there any changes to the language I could make to make this syntax more intuitive or make the error easier to spot while ideally staying somewhat close to python?</p>

<syntax><control-flow>
Avoiding student confusion about the meaning of `x == a or b`
CC BY-SA 4.0
2023-09-08T15:07:10.937Z
@JosephDoggie I can never remember whether `&&` or `||` has tighter precedence, so I always use parentheses when mixing them.
Seggan
CC BY-SA 4.0
2023-07-18T21:06:08.863Z
COBOL of all languages lets you do this, `a = b OR c`. According to Wikipedia, `a > b AND a > c OR a = d` can be shortened to `a > b AND c OR = d`.
Bbrk24
CC BY-SA 4.0
2023-07-19T06:05:10.560Z
You could warn when an "advanced" expressions is always evaluated as true, or false
tallberg
CC BY-SA 4.0
2023-07-19T06:08:48.643Z
@tallberg If worked out more that could be a good answer
mousetail 'he-him'
CC BY-SA 4.0
2023-07-19T09:51:58.147Z
You basically would want to make a linter here, or make it a compile time error in your language. Changing the semantic of the expression is probably undesirable, because if your goal is to transition people to standard Python, they might develop the wrong intuition if they're used to a variant of Python that behaves differently than standard Python, so IMO you should be very cautious about doing that.
Lie Ryan
CC BY-SA 4.0
2023-07-19T21:02:39.550Z
@Bbrk24, Gotta be careful with that because `a > b AND c` already means something in many languages.
ikegami
CC BY-SA 4.0
2023-07-19T22:30:47.517Z
My first boss after leaving the University said "Always use extra parentheses."  I agree.  Whether needed or not, they clarify things, of course this bothers some people.  The SQL usually has way more parentheses than some people would use.
JosephDoggie
CC BY-SA 4.0
2023-07-20T13:20:02.950Z
Well, the obvious thing is to simply disallow implicit conversions to `bool`. This is actually what Go does by default, so it can't be that bad of an idea.
Passer By
CC BY-SA 4.0
2023-07-20T19:21:45.723Z
As a kid, I once took a programming course, and one of my peers had been bitten by this bug in C++, like `x == 1 || 2 || 3`. When I pointed out that it wouldn’t work, he said that it did work, because he’d tested 1, 2, and 3. It really stuck with me, partly because it was the first time I saw in the wild how [falsification isn’t obvious](https://en.wikipedia.org/wiki/Wason_selection_task), partly because I was flabbergasted, and that’s a memorable feeling.
Jon Purdy
CC BY-SA 4.0
2023-07-20T20:59:19.247Z
What's the order of precedence?  I mean... if you can teach (the beginnings of) PEMDAS to second graders, you can teach your students that the `or` test  comes before the `equality` test.  Or vice versa.  Or that they're equal precedent, evaluated left to right.
RonJohn
CC BY-SA 4.0
2023-07-21T05:34:25.427Z
You might also be interested in [cseducators.se], for approaching the problem from the classroom angle rather than the language design angle.
Karl Knechtel
<h2>Use <code>any</code>/<code>all</code> operators and compound operators instead</h2>
<blockquote>
<p>I could of course special case this syntax and transform it into <code>number in (5, 12)</code>, but the goal here is not to just make it work but help the students build an intuition for how expressions and statements work, and ease the transition to real python later in the course.</p>
</blockquote>
<p>Even so, this is langdev.SE, so I want to explore this possibility. Since &quot;special cases aren't special enough to break the rules&quot;, I'd like to propose something more self-consistent.</p>
<p>This approach is inspired by the built-in functions <code>any</code> and <code>all</code> in Python - what if they were operators instead, and replaced <code>and</code> and <code>or</code> (so as to avoid confusion by trying to use both at once)?</p>
<p><code>x OP any a, b, c</code> would have semantics equivalent to <code>(x OP a) or (x OP b) or (x OP c)</code>. Similarly for <code>all</code> with <code>and</code>. You could perhaps require <code>OP any</code> to be joined up into a single token. Either way, the fact that the single keyword <code>any</code> is not infix, and is in the same place as the operator, avoids the weirdness of seeming special-case semantics. (Of course, it would be possible to do this with conventional <code>or</code> and <code>and</code>, but such <a href="https://langdev.stackexchange.com/questions/2451">multifix operators</a> are harder to implement.)</p>
<p>Similarly, just <code>any a, b, c</code> could mean <code>a or b or c</code> - there's no OP, so nothing to apply separately to each value.</p>
<p>To recover the functionality of Python's builtin <code>any</code>/<code>all</code> functions, then, just allow Python-like unpacking generalizations to work with the expression list:</p>
<pre><code># python
any(f(x) == 3 for x in ys)
# new language
3 == any *(f(x) for x in ys)
# or even:
3 == any *map(f, ys)
</code></pre>
<p>The hard part is making it short-circuit ;)</p>

CC BY-SA 4.0
2023-07-21T06:10:44.473Z
2023-07-21T06:10:44.473Z
Karl Knechtel
CC BY-SA 4.0
2023-07-23T19:36:48.007Z
This reminds me of [Raku's Junction type](https://docs.raku.org/type/Junction): in Raku, you can write `if number == any(5, 12)`, or even `if number == 5 | 12`, and get the intuitively expected answer. The `any` creates a "superposition" of values, the `==` is applied to each member via "auto-threading", and the boolean context "collapses" to a single value.
IMSoP
CC BY-SA 4.0
2023-07-24T13:48:25.197Z
*"Similarly, just any a, b, c could mean a or b or c - there's no OP, so nothing to apply separately to each value."* ─ But this would make the grammar ambiguous: `x OP any a, b, c` could mean `x OP (any a, b, c)`. It would also be confusing, because in the main intended use-case the purpose of `OP` is to *prevent* the code from accidentally meaning `a or b or c`, whereas in your other use-case it does mean exactly that.
kaya3
CC BY-SA 4.0
2023-07-24T16:48:35.663Z
"But this would make the grammar ambiguous" - that would be one reason to introduce single-token compound operators. ;) But really I just didn't consider operator precedence in laying out the core idea, because I considered that those issues are orthogonal. "It would also be confusing..." - the use case is to make the behaviour match intuition. What `or` does in other languages simply doesn't factor in to the design rationale. But if we like, we could make the intent very explicit with, perhaps, a spelling like `istrue any`.
Karl Knechtel
<p>I think there's a philosophical question to be considered here: is it better for a teaching language to be &quot;Python-like&quot;, or for it to parse statements properly so that <strong>==</strong> and <strong>or</strong> are treated with the correct precedence?</p>
<p>One way round it would be to avoid the issue by using extremely strict type checking, and to never apply automatic conversions which are an unending source of problems.</p>
<p>I'd add that experience has led me to believe that <strong>=</strong> should be &quot;considered harmful&quot;, use <strong>==</strong> for comparison and <strong>:=</strong> for assignment with a simple <strong>=</strong> being an error in all cases.</p>
<p>But again, the question remains of how closely a teaching language should stick to Python syntax and conventions: the simplest solution, which could be applied without syntax changes, would be rigorous type checking.</p>
<p>Edited to add: as an historical note, <a href="https://en.wikipedia.org/wiki/Plankalk%C3%BCl#Assignment_operation" rel="nofollow noreferrer">https://en.wikipedia.org/wiki/Plankalk%C3%BCl#Assignment_operation</a> describes controversy relating to whether Zuse favoured <a href="https://i.sstatic.net/MfM7U.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/MfM7U.png" alt="Zuse's claimed assignment operator" /></a> as an assignment operator, which became ← (supported by ASCII-63 and several 6-bit character sets) or the digraph :=</p>

CC BY-SA 4.0
2023-07-19T08:39:27.143Z
2023-07-20T07:31:55.253Z
2023-07-20T07:31:55.253Z
Mark Morgan Lloyd
CC BY-SA 4.0
2023-07-19T12:59:26.767Z
I remember how my secondary school Physics teacher used to curse early programming language design for using `=` for assignment, when that symbol a long history of the equality sign in mathematics. Whenever he would see `x = x + 1`, he would cringe.
gerrit
CC BY-SA 4.0
2023-07-20T02:09:20.753Z
At around age 11, I was learning BASIC and hit an example in a book which had a pair of lines like  `x = x + 1` and `y = x + 2` or something like that. I thought, what, is the machine supposed to solve those equations? But there is no solution: `x` cannot be equal to `x + 1`; why not use an example that has solutions?
Kaz
CC BY-SA 4.0
2023-07-20T15:57:27.200Z
@Kaz Through the magic of floating-point, there are actually loads of values for _x_ that would satisfy (to a computer) the equation x = x + 1. The smallest single-precision float _x_ for which that is true is `16777216.`. For a double-precision float, it's `9007199254740992.d`.
OpenAI was the last straw
CC BY-SA 4.0
2023-07-20T19:17:29.640Z
@AR-solidarityforModstrike Yes; that and saturation math.
Kaz
<p>The underlying problem is that, unlike COBOL, python doesn't try to make sense when read as English.</p>
<p>COBOL was designed so that non-technical managers could mostly understand the code, and it worked well: One doesn't need a degree in mathematics to understand the meaning of <code>a IS GREATER THAN b AND c, OR EQUALS d</code>.</p>
<p>On the other hand, python is happy to combine different things in strange ways that make sense mathematically, but not to the &quot;common reasonable man&quot;, who is not going to be able to understand that <code>if 12</code> has any sensible meaning.</p>
<p>Not surprisingly, kids and beginner programmers don't think like mathematicians (who can blindly accept rules like &quot;<code>==</code> binds more tightly than <code>OR</code>&quot;).</p>
<p>You might consider requiring that for <code>a or b</code>, <code>a</code> and <code>b</code> must have the same type of semantics.<br />
Or better yet (as suggested by @coredump), don't allow implicit conversion to Boolean.<br />
E.g. reject Python's <code>if x &gt; y or z</code> and require that it be entered as <code>if x &gt; y or z != 0</code>.</p>
<p>Similarly reject expressions that in English either are ambiguous or have different interpretations.<br />
E.g. <code>x+y*a+b</code> should be an error, requiring <code>(x+y)*(a+b)</code> or <code>x+(y*a)+b</code>.</p>
<p>(To be honest, I think those would be good changes to make to the <em>real</em> Python, but I won't hold my breath.)</p>

CC BY-SA 4.0
2023-07-19T00:51:52.060Z
2023-07-21T12:49:21.500Z
2023-07-21T12:49:21.500Z
Ray Butterworth
CC BY-SA 4.0
2023-07-19T15:16:53.680Z
The underlying problem is that, unlike most programming languages, English doesn't try to make sense mathematically.
leftaroundabout
<p>If the problem were purely one of being confused about syntax, we could avoid the confusion by eliminating all implicit associativity and precedence in the infix operators (except perhaps for addition, subtraction, multiplication, division and perhaps exponents, if we have them).</p>
<p>Thus A == B or C would be a syntax error, requiring parentheses: either A == (B or C) or (A == B) or C.</p>
<p>However, even with the mandatory parentheses, students will still get tripped up and believe that <code>A == (B or C)</code> means <code>(A == B) or (A == C)</code>.</p>
<p>Parentheses do not preclude distributive laws, whether real or imaginary. After all <code>A * (B + C)</code> is <code>A * B + A * C</code>!</p>
<p>Experienced programmers in C have a momentary lapse of reason and write something like <code>if (state == (STATE_INITIAL || STATE_REQUEST_RECEIVED)) ...</code>!!! This is a thing that happens; I've seen it in a code review more than once.</p>
<p>That particular expression pattern could be targeted by a code walker in your language, and subject to a diagnostic:</p>
<pre><code>foo.lang:42:warning: X == (Y or Z) doesn't mean (X == Y) or (X == Z).
</code></pre>
<p>This will irks someone who knows what they are doing and wants that expression. That programmer wants to compare <code>A</code> with <code>B</code>, except when <code>B</code> is missing, in which case <code>C</code> is taken instead and compared with <code>A</code>.</p>
<p>One possibility would be to have a class of warnings that are targeted at neophytes. All learning materials would steer students toward operating the language in a way that the newbie warnings are turned on.</p>
<p>Ultimately, the way to reduce the semantic confusion would be to make a &quot;Blub&quot; languages. Don't have a clever <code>or</code> operator that returns one operand or the other. Have a pure Boolean <code>or</code> that yields true or false.</p>
<p>Then make it a type error to compare some <code>X</code> which is not Boolean to a Boolean value!</p>
<pre><code>X == (Y or Z)     # error: comparing integer X with Boolean expression (Y or Z)
</code></pre>
<p>The idea of <code>A or B or C</code> yielding the leftmost one of A, B and C that is not falsy, and not evaluate the rest, comes from Lisp:</p>
<pre><code>  (or A B C ...) ; yield A if that is true, else B if that is true, else ..
</code></pre>
<p>However, while it is that way in Common Lisp and related dialects, it was not so in Lisp 1, according to the 1960 manual.<sup>*</sup>  While Lisp 1 had N-ary <code>AND</code> and <code>OR</code> operators, which were short-circuiting, they yielded a strictly Boolean value. Doing anything like <code>(EQL EXPR (OR A B))</code> in Lisp-1 would have meant that <code>EXPR</code> has a Boolean value, <code>T</code> or <code>F</code>, and that is being compared to the truth value of the <code>OR</code>.  If <code>EXPR</code> contained, say, a number, the comparison would be false even if both <code>A</code> and <code>B</code> contained that number.</p>
<p>So before Python could borrow the Lisp <code>or</code>; first the original Lisp one had to be upgraded to the more clever semantics.</p>
<p>Under the new, clever <code>or</code>, you will sometimes get the expected behavior. If <code>expr</code> evaluates to 42 and so does <code>a</code>, then <code>(eql expr (or a b))</code> will in that case behave consistently with the belief that <code>expr</code> is being separately compared with <code>a</code> and with <code>b</code>. The student will run the code and not notice the problem. The failing test case is something like <code>(eql 42 (or 73 42))</code>. Cases like <code>(eql 42 (or nil 42))</code> and <code>(eql 42 (or 42 nil))</code> do not reproduce the issue, and can confirm the wrong belief.</p>
<hr />
<p>See page 48.</p>

CC BY-SA 4.0
2023-07-20T02:07:26.320Z
2023-07-20T02:13:04.277Z
2023-07-20T02:13:04.277Z
Kaz
CC BY-SA 4.0
2023-07-20T16:03:26.637Z
I wonder if people who write `state == (STATE_INITIAL || STATE_REQUEST_RECEIVED)` are confused by the similar looking bitmap pattern `state & (STATE_INITIAL | STATE_REQUEST_RECEIVED)`.
OpenAI was the last straw
<p>It may have something to do with my natural language isn't similar to English, but when I learned, I was fully aware that the name of this operation is &quot;logical or&quot;. There is also &quot;bitwise or&quot; which is a different thing. I started in languages that have them sharing the same operator, but they are introduced differently nevertheless. Neither is the same thing as &quot;or as in the common sense&quot;.</p>
<p>Depending on how old the kids are, it may remove some ambiguity by using the longer name in teaching.</p>
<p>Doing that in language design may not sound clever. It would be something like extending the keyword to <code>logical_or</code>. Or you could use the C-style operator <code>||</code>, and say its name is &quot;logical or&quot; and there isn't a shortened &quot;or&quot; mentioned anywhere.</p>

CC BY-SA 4.0
2023-07-19T16:06:22.547Z
2023-07-19T16:06:22.547Z
user23013
CC BY-SA 4.0
2023-07-20T07:36:33.020Z
I know that this isn't strictly accurate, but I tend to think of || && == as always yielding a boolean, hence possibly allowing !! to normalise an integer expression to a boolean without an explicit cast.
Mark Morgan Lloyd
<p>You could issue a warning when an integer literal (this is important -- using an integer variable or a function result in this way can absolutely be legitimate) is used in a boolean context, i.e. in an <code>if</code> condition, or as an argument to <code>and</code>, <code>or</code> or <code>not</code>.</p>
<p>But on a larger scale, you might want to look into partial operator precedence ordering. This is a slightly weird use case for it, but it will still be immensly helpful. Essentially, instead of associating a priority with every operator (which in modern Python is done implicitly by just nesting the grammar rules in a certain way), you say that some operators' precedences are not comparable. For example, this could be done for <code>and</code> and <code>or</code>. Then the construct <code>a and b or c</code> would be invalid, and the programmer would have to explicitly specify <code>(a and b) or c</code> or <code>a and (b or c)</code>. For your case, you want comparisons to be 'parallel' to <code>or</code>, and perhaps <code>and</code>. Then the hypothetical student making that mistake would probably write <code>if number == (5 or 12):</code>, which would still result in a silent mistake, but of a different, more easily observable sort: <code>5</code> would work, but <code>12</code> wouldn't. Hopefully, the reasons behind this would be more apparent to a student.</p>
<p>In my language, I'm actually using the same (albeit modified heavily, but keeping the syntax) parser generator as Python 3.9+ does. I actually employ partial precedence ordering, and you can see examples of it in the <a href="https://github.com/abel1502/bondrewd/blob/master/grammar/bondrewd.gram#L212-L234" rel="noreferrer">grammar definition</a>. Hopefully that should help implement it for yourself</p>

CC BY-SA 4.0
2023-07-18T21:08:04.830Z
2023-07-18T21:08:04.830Z
abel1502
CC BY-SA 4.0
2023-07-19T11:01:34.370Z
Why a warning and not an error? I can't think of a valid use case for an integer literal as a boolean expression.
Jack Aidley
CC BY-SA 4.0
2023-07-19T11:48:07.573Z
@JackAidley Generated code could have it, for example. My general approach is to use warnings for unexpected or unusual things, if they are otherwise legitimate in terms of composability
abel1502
CC BY-SA 4.0
2023-07-19T18:59:59.150Z
@JackAidley I very frequently use integer/boolean equivalence as a practical feature: for example, in expressions like `(-1)**signShouldBeInverted`.  Another case would be the "accumulator int-bool" where you're trying to assess whether something is true for at least one of many conditions which are iterated over in a loop.  Set `anyAreTrue` to 0 before the loop, then within the loop increment it by 1 every time a true condition is encountered. When the loop finishes, use it as a boolean: `if (anyAreTrue) { //etc. }`.  Or, to do something when a number is not 0: `if (numberValue) { //etc. }`.
Quack E. Duck
CC BY-SA 4.0
2023-07-19T19:05:11.920Z
I really *dislike* how Swift (for example) disallows this -- you can't even *cast* Int to Bool there!  It definitely seems like a case where you should warn the new programmer that they *may* not be getting the result they want, instead of assuming they *must* be wrong and disabling such a potentially useful feature.  At least in my opinion - which may very well be in the minority. (My first programming language was JS, so I tend to take implicit type conversion for granted :D)
Quack E. Duck
CC BY-SA 4.0
2023-07-19T19:28:05.647Z
@QuackE.Duck Those aren't literals though. A naked literal as int/Boolean equivalence is a different matter.
Jack Aidley
CC BY-SA 4.0
2023-07-19T19:35:52.970Z
@JackAidley That's an excellent point!
Quack E. Duck
CC BY-SA 4.0
2023-07-22T06:32:03.280Z
Any literal is worth a warning there, IMO. In particular, boolean literals are only really even necessary for assigment/initialization, anyway.
Karl Knechtel
<p>Keep your types separated, only <code>True</code> or <code>False</code> are legitimate boolean values, and make sure that <code>or</code> only works with boolean. This is for example how Scratch does it, with different shapes for different types of values.
Since 12 cannot be used as an operand for <code>or</code>, your interpreter will detect it.</p>
<p>Alternatively, you may want to make parentheses mandatory around logical operators to avoid confusion with <code>and</code> vs <code>or</code> too, as in:</p>
<pre><code>if x == 3 and y == 2 or y == 5:
    pass
</code></pre>

CC BY-SA 4.0
2023-07-19T14:27:53.437Z
2023-07-19T14:27:53.437Z
coredump
CC BY-SA 4.0
2023-07-21T00:20:24.810Z
Right.  I've never understood why Python claims to have "explicit over implicit" as one of its core values and yet includes implicit conversion to boolean whenever one is required.  Require an explicit comparison is much better than just blindly saying an integer looks like it should mean True or False depending on what its value is...
occipita
CC BY-SA 4.0
2023-07-21T05:45:32.887Z
@occipita It turns out that language - and library - design is hard. I could probably give a whole talk about "the Zen of Python" vs. Python out of box. Starting, of course, with the `this` module itself.
Karl Knechtel
CC BY-SA 4.0
2023-07-21T08:48:31.217Z
@occipita I try to fix the issues with doing implicit conversions the C-way in Tyr and it turns out that you need a hell lot of things to get it to work including compile-time evaluation of implicit conversions. I'm not there after working on the language for five years. It's simply not that easy.
feldentm
CC BY-SA 4.0
2023-07-21T10:06:51.617Z
I do like the fact that in Lisp only NIL is false and everything else is true, it is convenient to have `(or this that)` evaluate to the first non-nil value for example.
coredump
<p><a href="https://pyret.org/" rel="noreferrer">Pyret</a> is a Python-like language explicitly designed for education. This is what it produces on your example program:</p>
<p><a href="https://i.sstatic.net/c9enF.png" rel="noreferrer"><img src="https://i.sstatic.net/c9enF.png" alt="Screenshot of Pyret’s web IDE." /></a></p>
<p>Note that the error message doesn’t explicitly call out the logical mistake, and perhaps it could. Pyret does have some special-purpose error reporting technology for other sorts of common mistakes. But the simple solution they’ve used here instead is to simply make <code>==</code> and <code>or</code> have incompatible precedence, so programmers have to be explicit about their grouping.</p>
<p>But suppose the user simply adds parentheses around <code>5 or 12</code>. Pyret then produces this error:</p>
<p><a href="https://i.sstatic.net/2jRIJ.png" rel="noreferrer"><img src="https://i.sstatic.net/2jRIJ.png" alt="Screenshot of Pyret’s web IDE." /></a></p>
<p>Note that logical operators are restricted to booleans. This catches mistakes earlier, and it helps the programmer understand that the program isn’t behaving the way they think.</p>
<p>These examples illustrate that some work making basic error messages as helpful as possible can go a really long way. The authors of Pyret have implemented a development environment with color-coded highlighting to help illustrate how the errors correspond to portions of the user’s program, and they’ve also presented them in a way that is likely to be less overwhelming to beginners. Many of these decisions were empirically supported by user studies, published in the paper <a href="https://doi.org/10.1145/2048237.2048241" rel="noreferrer">Mind your language: on novices' interactions with error messages</a> (<a href="http://static.cs.brown.edu/%7Esk/Publications/Papers/Published/mfk-mind-lang-novice-inter-error-msg/paper.pdf" rel="noreferrer">pdf</a>).</p>
<p>As your question states, leaving some of the details up to students to figure out for themselves could help to develop some programming problem-solving skills. The important thing is presenting the information in a way that makes it most likely that the students can put the pieces together.</p>

CC BY-SA 4.0
2023-07-18T21:06:47.063Z
2023-07-18T21:17:05.897Z
2023-07-18T21:17:05.897Z
Alexis King
CC BY-SA 4.0
2023-07-19T10:17:46.227Z
Actually, it's arguable whether **or** etc. is an operator within an expression, or a syntax-level keyword. If short-circuit ("early-out") evaluation is assumed then it might be better considered as a keyword, *but* I've come across some extremely bad explanations of that in older documentation and it might be that that scared people into trying to document it as an operator.
Mark Morgan Lloyd
CC BY-SA 4.0
2023-07-19T11:30:22.033Z
@MarkMorganLloyd In languages that let you write lazy evaluation, it can be a normal operator. E.g. in Swift where it's `func || (lhs: Bool, rhs: @autoclosure () -> Bool) -> Bool` (`@autoclosure` is how you denote lazy evaluation).
Bbrk24
CC BY-SA 4.0
2023-07-19T17:34:35.430Z
Bizarrely, Python itself has similar protection against misuse of these operators; e.g. `x == not y` is a `SyntaxError`. A bold community member could probably write a [PEP](https://peps.python.org/) to fix this issue with `or` (and `and`, presumably!)
Corbin
CC BY-SA 4.0
2023-07-20T01:12:20.117Z
@Corbin: That's not a protection measure. It's a consequence of how precedence is implemented, combined with the very low precedence of `not`. No one went to any special effort to prohibit `x == not y`; instead, they would have had to go to special effort to allow it (and that effort would have awkward consequences in more complex expressions, making it not at all worthwhile).
user2357112
CC BY-SA 4.0
2023-07-20T08:11:24.783Z
The demonstration of Pyret raises more questions than it answers :) Example 1: So in Pyret I cannot use `1 + 2 + 3` because the operations are at the same grouping level? Example 2: If logical operators are restricted to boolean then I would expect it to complain about non-boolean operands, not result :) ...or the error message is written an extremely confusing way.
pabouk - Ukraine stay strong
CC BY-SA 4.0
2023-07-20T10:27:29.303Z
@pabouk-Ukrainestaystrong: Pyret mostly just [doesn't use operator precedence](https://pyret.org/docs/latest/op-precedence.html). You can write `1 + 2 + 3`, because that's all the same operator, but you can't write `1 + 2 - 3`. (There are exceptions, like `^`, which isn't officially classified as a binary operator in the docs despite essentially acting as a low-precedence binary operator.) As for your second question, it is complaining about the operands - the expression it's complaining about is `5`, which was one of the operands of the `or` in `5 or 12`.
user2357112
CC BY-SA 4.0
2023-08-15T03:00:10.720Z
Its good if students learn to think in terms of «well typed ness = well formed ness» [See](https://stackoverflow.com/a/54734490/3700414). Languages like Python in which types are not part of the syntax work well in the wild because programmers have picked up an informal sense of well typing from elsewhere. But for beginners they fail to give this sense
Rusi
<p>I suspect the problem here is that the Englishy keyword <code>or</code> seems like it groups values as in <code>number in set(5, 12)</code> but is actually a connector for boolean expressions.</p>
<p>Being strict about both sides of <code>or</code> being a boolean expression can help catch these kinds of problems earlier.  I've written more about some <a href="https://dev.to/mikesamuel/boolean-coercion-pitfalls-with-examples-505k" rel="noreferrer">pitfalls of boolean coercion</a> elsewhere.  Even if your language is untyped, first making sure that you have some kind of type-like error at least at runtime is a place to start, and then you can try to customize the error messages as you learn more about the ways your user population goes astray.</p>
<p>It's nice that you're working on a teaching variant of Python which started off as a fork of <em>ABC</em>, a BASIC variant also meant for teaching.</p>
<p><a href="https://www.manning.com/books/the-programmers-brain" rel="noreferrer">&quot;<em>The Programmer's Brain</em>&quot;</a> tackles the topic of how we read code, touching on human cognition and natural language use, and has quite a bit on how beginning programmers are led astray.</p>
<p>I believe Felienne Hermans, the author of that book, has a teaching language, <a href="https://hedy.org/" rel="noreferrer">Hedy</a> and I think it's also trying to build intuitions that mesh with Python.  Maybe seeing how one expresses ideas that your users have trouble with in Hedy would give some ideas.</p>

CC BY-SA 4.0
2023-07-18T22:55:13.650Z
2023-07-19T15:23:30.110Z
2023-07-19T15:23:30.110Z
Mike Samuel
CC BY-SA 4.0
2023-07-19T07:18:52.167Z
This! Helpful conversions are **harmful**, for students. Beginners should be _explicit_ about their intentions, any "implicit" magic will just throw them off and confound them.
Matthieu M.
CC BY-SA 4.0
2023-07-19T11:46:02.777Z
This answer together with @MatthieuM. comment is most important advice for programming in general: **Be explicit!** Everything that is implicit must be inferred and is therefor depending on the context of the thing itself and the understanding of the reader or writer, and therefor their knowledge, and there's a very, very good chance it *will* be, at least partially, misunderstood or wrongly derivated or misconstrued. Just be explicit and that problem goes away completely without much "upfront" cost.
Bobby
CC BY-SA 4.0
2023-07-19T14:42:12.347Z
@Bobby being explicit everywhere about everything is hardly feasible though, unless you're happy to have expressions like `1.5 Double.+ (x Double.** (j Int.- 2))` instead of `1.5 + x**(j-2)`. What I would agree with is that everything implicit should follow some simple and universal principles and be statically traceable. Parametric polymorphism and typeclasses provide IMO the best framework for this, though it certainly can also cause confusion to beginners.
leftaroundabout
CC BY-SA 4.0
2023-07-19T17:40:22.370Z
@leftaroundabout, In the *pitfalls of boolean coercion* article I linked above, I propose a more lenient standard than "be explicit".  Instead I recommend "maybe ask yourself, could a proposed coercion semantics mask missing steps that the author should have performed?"
Mike Samuel
CC BY-SA 4.0
2023-07-21T05:40:49.227Z
In my experience, there are a **lot** of programmers who, when faced with a type error, will throw whatever coercion hammers they can find at the problem until it goes away, without attempting any reasoning for why the original problem occurred or what its existence could signify about the logic.
Karl Knechtel
CC BY-SA 4.0
2023-07-21T16:27:38.647Z
@KarlKnechtel my point was that making it an error gives you an opportunity to fine tune the error message based on context.  If it's not an error per the language semantics, you're more limited in what you can do.
Mike Samuel
CC BY-SA 4.0
2023-07-21T20:58:10.713Z
I think your article about the pitfalls of boolean conversion is really relevant to my question here: https://softwareengineering.stackexchange.com/questions/446665/when-is-3-valued-logic-useful but the way that page renders for me (Chrome & Edge) is pretty terrible.  Is it just me?
JimmyJames
CC BY-SA 4.0
2023-07-25T19:36:18.920Z
@JimmyJames, it renders ok for me on a recent Chrome, but idk.
Mike Samuel
CC BY-SA 4.0
2023-07-25T19:36:29.507Z
@JimmyJames Re your question, are you familiar with the [Liar's revenge](https://ndpr.nd.edu/reviews/revenge-of-the-liar-new-essays-on-the-paradox/) or [more accessibly on YT](https://www.youtube.com/watch?v=MsLQKGe_aiw)?  "This statement is third value or not true" recreates the liar's paradox "This statement is false."  3VLs do not save you from contradiction unless you sacrifice something: usually excluded middle.  So when using 3VLs, you need to be very careful about what the third value means and that depends on context, so there's not one 3VL but many.
Mike Samuel
CC BY-SA 4.0
2023-07-25T19:41:03.007Z
@JimmyJames I vaguely remember discussions of 3VLs from circuit design courses where you could have two lines that may either pull a gate output towards true or false or not pull at all.  That was useful in some cases, but if you had some logic or timing problem that caused the two inputs to pull in opposite directions then things tended to catch on fire.
Mike Samuel
CC BY-SA 4.0
2023-07-25T21:02:27.803Z
@MikeSamuel It's showing in a narrow div or whatever that's maybe 6 characters wide.  I was able to copy it into a text file and read it.  Must be something on my end I guess.
JimmyJames
CC BY-SA 4.0
2023-07-25T21:07:21.753Z
@MikeSamuel If I wasn't clear before, I've realized that a lot of the struggles I've had with SQL are centered around 'downmixing' from 3VL to 2VL.  For example, a CASE statement takes three values (true, false, and unknown) and maps them to THEN and ELSE.  I've also come to the conclusion that this is a big factor in why ORM is such a difficult problem.  Your point here seemed to align with those issues even though it's somewhat different than what this answer is about, I think.
JimmyJames
CC BY-SA 4.0
2023-07-27T16:36:49.313Z
@JimmyJames Oh, is this the filling a portion of a joined row when one side of an outer join does not correspond to a row in one of the joining tables?
Mike Samuel
CC BY-SA 4.0
2023-07-27T16:48:25.733Z
@MikeSamuel That's one scenario where nulls can come into play, yes. Nulls can also be the 'explicit' value of a cell/field in a table. The thing is that in many scenarios, null/unknown is treated like `false` (but not always). For example, in a select, you either get a row, or you don't. If I select people who like in country X, I will not get people whose country is recorded as 'null'. Where things get weird is if I say I want everyone who is not from that country, I will not get the null rows.  In both scenarios, the condition result was unknown but the select treats it the same as false.
JimmyJames
CC BY-SA 4.0
2023-07-27T16:50:47.997Z
This article explains this well: https://www.linkedin.com/pulse/null-bas-van-gils
JimmyJames
CC BY-SA 4.0
2023-07-27T21:00:25.203Z
Ah, I see "The fundamental point I want to make is that certain boolean expressions—and therefore certain queries—produce results that are correct according to three-valued logic but not correct in the real world."  It's been a long time since I've slung SQL but I remember that being a pain; I remember a problem in an account creation flow where in between assigning a uid and entering their credentials in another table, anyone could log in as them because there was NULL in the salted-password column.
Mike Samuel
<p>Expressions containing <code>AND</code> and <code>OR</code> should not contain any part that is always truthy, or falsey - this should produce a warning or even an error.
If it's for learning, maybe errors are scary, a tip might be better.</p>
<p>TIP: <code>5 OR 12</code> always equals 5</p>
<p>TIP: <code>5 AND 12</code> always equals 12</p>

CC BY-SA 4.0
2023-07-19T06:24:23.673Z
2023-07-19T06:24:23.673Z
tallberg
# Avoiding student confusion about the meaning of `x == a or b`
Posted by: username_0 on 2023-07-18
I'm designing a language intended to teach kids and other beginners how to program. It's just a fork of Python with a few extra features to make it easier to learn.
I've noticed students make one specific mistake very often: They'll write code like this:
number = input()
if number == 5 or 12:
    print "You guessed the correct number!"

This of course won't work of course, since it parses as (number == 5) or 12 and 12 is always truthy, thus you will always enter the if regardless of the value of number.
Students make this mistake very often, and it's especially deadly because it does not produce any kind of error, and since they are not very experienced in debugging they can spend days unable to track down the issue before they ask for help.
I could of course special case this syntax and transform it into number in (5, 12), but the goal here is not to just make it work but help the students build an intuition for how expressions and statements work, and ease the transition to real python later in the course.
Are there any changes to the language I could make to make this syntax more intuitive or make the error easier to spot while ideally staying somewhat close to python?
**Tags:** syntax control-flow
> **Comments:**
> **username_2**: COBOL of all languages lets you do this, `a = b OR c`. According to Wikipedia, `a > b AND a > c OR a = d` can be shortened to `a > b AND c OR = d`.  
> **username_3**: You could warn when an "advanced" expressions is always evaluated as true, or false  
> **username_0**: @tallberg If worked out more that could be a good answer  
> **username_4**: You basically would want to make a linter here, or make it a compile time error in your language. Changing the semantic of the expression is probably undesirable, because if your goal is to transition people to standard Python, they might develop the wrong intuition if they're used to a variant of Python that behaves differently than standard Python, so IMO you should be very cautious about doing that.  
> **username_5**: @Bbrk24, Gotta be careful with that because `a > b AND c` already means something in many languages.  
> **username_6**: My first boss after leaving the University said "Always use extra parentheses."  I agree.  Whether needed or not, they clarify things, of course this bothers some people.  The SQL usually has way more parentheses than some people would use.  
> **username_7**: Well, the obvious thing is to simply disallow implicit conversions to `bool`. This is actually what Go does by default, so it can't be that bad of an idea.  
> **username_8**: As a kid, I once took a programming course, and one of my peers had been bitten by this bug in C++, like `x == 1 || 2 || 3`. When I pointed out that it wouldn’t work, he said that it did work, because he’d tested 1, 2, and 3. It really stuck with me, partly because it was the first time I saw in the wild how [falsification isn’t obvious](https://en.wikipedia.org/wiki/Wason_selection_task), partly because I was flabbergasted, and that’s a memorable feeling.  
> **username_9**: What's the order of precedence?  I mean... if you can teach (the beginnings of) PEMDAS to second graders, you can teach your students that the `or` test  comes before the `equality` test.  Or vice versa.  Or that they're equal precedent, evaluated left to right.  
> **username_10**: You might also be interested in [cseducators.se], for approaching the problem from the classroom angle rather than the language design angle.  
> **username_1**: @JosephDoggie I can never remember whether `&&` or `||` has tighter precedence, so I always use parentheses when mixing them.  
---
## 10 answers
### Answer by username_26 on 2023-07-18. Score: 45
[Pyret](https://pyret.org/) is a Python-like language explicitly designed for education. This is what it produces on your example program:

[![Screenshot of Pyret’s web IDE.](https://i.sstatic.net/c9enF.png)](https://i.sstatic.net/c9enF.png)

Note that the error message doesn’t explicitly call out the logical mistake, and perhaps it could. Pyret does have some special-purpose error reporting technology for other sorts of common mistakes. But the simple solution they’ve used here instead is to simply make `==` and `or` have incompatible precedence, so programmers have to be explicit about their grouping.

But suppose the user simply adds parentheses around `5 or 12`. Pyret then produces this error:

[![Screenshot of Pyret’s web IDE.](https://i.sstatic.net/2jRIJ.png)](https://i.sstatic.net/2jRIJ.png)

Note that logical operators are restricted to booleans. This catches mistakes earlier, and it helps the programmer understand that the program isn’t behaving the way they think.

These examples illustrate that some work making basic error messages as helpful as possible can go a really long way. The authors of Pyret have implemented a development environment with color-coded highlighting to help illustrate how the errors correspond to portions of the user’s program, and they’ve also presented them in a way that is likely to be less overwhelming to beginners. Many of these decisions were empirically supported by user studies, published in the paper [Mind your language: on novices' interactions with error messages](https://doi.org/10.1145/2048237.2048241) ([pdf](http://static.cs.brown.edu/%7Esk/Publications/Papers/Published/mfk-mind-lang-novice-inter-error-msg/paper.pdf)).

As your question states, leaving some of the details up to students to figure out for themselves could help to develop some programming problem-solving skills. The important thing is presenting the information in a way that makes it most likely that the students can put the pieces together.
> **Comments:**
> **username_13** (2023-07-19): Actually, it's arguable whether **or** etc. is an operator within an expression, or a syntax-level keyword. If short-circuit ("early-out") evaluation is assumed then it might be better considered as a keyword, *but* I've come across some extremely bad explanations of that in older documentation and it might be that that scared people into trying to document it as an operator.  
> **username_2** (2023-07-19): @MarkMorganLloyd In languages that let you write lazy evaluation, it can be a normal operator. E.g. in Swift where it's `func || (lhs: Bool, rhs: @autoclosure () -> Bool) -> Bool` (`@autoclosure` is how you denote lazy evaluation).  
> **username_27** (2023-07-19): Bizarrely, Python itself has similar protection against misuse of these operators; e.g. `x == not y` is a `SyntaxError`. A bold community member could probably write a [PEP](https://peps.python.org/) to fix this issue with `or` (and `and`, presumably!)  
> **username_28** (2023-07-20): @Corbin: That's not a protection measure. It's a consequence of how precedence is implemented, combined with the very low precedence of `not`. No one went to any special effort to prohibit `x == not y`; instead, they would have had to go to special effort to allow it (and that effort would have awkward consequences in more complex expressions, making it not at all worthwhile).  
> **username_29** (2023-07-20): The demonstration of Pyret raises more questions than it answers :) Example 1: So in Pyret I cannot use `1 + 2 + 3` because the operations are at the same grouping level? Example 2: If logical operators are restricted to boolean then I would expect it to complain about non-boolean operands, not result :) ...or the error message is written an extremely confusing way.  
> **username_28** (2023-07-20): @pabouk-Ukrainestaystrong: Pyret mostly just [doesn't use operator precedence](https://pyret.org/docs/latest/op-precedence.html). You can write `1 + 2 + 3`, because that's all the same operator, but you can't write `1 + 2 - 3`. (There are exceptions, like `^`, which isn't officially classified as a binary operator in the docs despite essentially acting as a low-precedence binary operator.) As for your second question, it is complaining about the operands - the expression it's complaining about is `5`, which was one of the operands of the `or` in `5 or 12`.  
> **username_30** (2023-08-15): Its good if students learn to think in terms of «well typed ness = well formed ness» [See](https://stackoverflow.com/a/54734490/3700414). Languages like Python in which types are not part of the syntax work well in the wild because programmers have picked up an informal sense of well typing from elsewhere. But for beginners they fail to give this sense  
---
### Answer by username_31 on 2023-07-18. Score: 27
I suspect the problem here is that the Englishy keyword `or` seems like it groups values as in `number in set(5, 12)` but is actually a connector for boolean expressions.

Being strict about both sides of `or` being a boolean expression can help catch these kinds of problems earlier. I've written more about some [pitfalls of boolean coercion](https://dev.to/mikesamuel/boolean-coercion-pitfalls-with-examples-505k) elsewhere. Even if your language is untyped, first making sure that you have some kind of type-like error at least at runtime is a place to start, and then you can try to customize the error messages as you learn more about the ways your user population goes astray.

It's nice that you're working on a teaching variant of Python which started off as a fork of *ABC*, a BASIC variant also meant for teaching.

["*The Programmer's Brain*"](https://www.manning.com/books/the-programmers-brain) tackles the topic of how we read code, touching on human cognition and natural language use, and has quite a bit on how beginning programmers are led astray.

I believe Felienne Hermans, the author of that book, has a teaching language, [Hedy](https://hedy.org/) and I think it's also trying to build intuitions that mesh with Python. Maybe seeing how one expresses ideas that your users have trouble with in Hedy would give some ideas.
> **Comments:**
> **username_32** (2023-07-19): This! Helpful conversions are **harmful**, for students. Beginners should be _explicit_ about their intentions, any "implicit" magic will just throw them off and confound them.  
> **username_33** (2023-07-19): This answer together with @MatthieuM. comment is most important advice for programming in general: **Be explicit!** Everything that is implicit must be inferred and is therefor depending on the context of the thing itself and the understanding of the reader or writer, and therefor their knowledge, and there's a very, very good chance it *will* be, at least partially, misunderstood or wrongly derivated or misconstrued. Just be explicit and that problem goes away completely without much "upfront" cost.  
> **username_18** (2023-07-19): @Bobby being explicit everywhere about everything is hardly feasible though, unless you're happy to have expressions like `1.5 Double.+ (x Double.** (j Int.- 2))` instead of `1.5 + x**(j-2)`. What I would agree with is that everything implicit should follow some simple and universal principles and be statically traceable. Parametric polymorphism and typeclasses provide IMO the best framework for this, though it certainly can also cause confusion to beginners.  
> **username_31** (2023-07-19): @leftaroundabout, In the *pitfalls of boolean coercion* article I linked above, I propose a more lenient standard than "be explicit".  Instead I recommend "maybe ask yourself, could a proposed coercion semantics mask missing steps that the author should have performed?"  
> **username_10** (2023-07-21): In my experience, there are a **lot** of programmers who, when faced with a type error, will throw whatever coercion hammers they can find at the problem until it goes away, without attempting any reasoning for why the original problem occurred or what its existence could signify about the logic.  
> **username_31** (2023-07-21): @KarlKnechtel my point was that making it an error gives you an opportunity to fine tune the error message based on context.  If it's not an error per the language semantics, you're more limited in what you can do.  
> **username_34** (2023-07-21): I think your article about the pitfalls of boolean conversion is really relevant to my question here: https://softwareengineering.stackexchange.com/questions/446665/when-is-3-valued-logic-useful but the way that page renders for me (Chrome & Edge) is pretty terrible.  Is it just me?  
> **username_31** (2023-07-25): @JimmyJames, it renders ok for me on a recent Chrome, but idk.  
> **username_31** (2023-07-25): @JimmyJames Re your question, are you familiar with the [Liar's revenge](https://ndpr.nd.edu/reviews/revenge-of-the-liar-new-essays-on-the-paradox/) or [more accessibly on YT](https://www.youtube.com/watch?v=MsLQKGe_aiw)?  "This statement is third value or not true" recreates the liar's paradox "This statement is false."  3VLs do not save you from contradiction unless you sacrifice something: usually excluded middle.  So when using 3VLs, you need to be very careful about what the third value means and that depends on context, so there's not one 3VL but many.  
> **username_31** (2023-07-25): @JimmyJames I vaguely remember discussions of 3VLs from circuit design courses where you could have two lines that may either pull a gate output towards true or false or not pull at all.  That was useful in some cases, but if you had some logic or timing problem that caused the two inputs to pull in opposite directions then things tended to catch on fire.  
> **username_34** (2023-07-25): @MikeSamuel It's showing in a narrow div or whatever that's maybe 6 characters wide.  I was able to copy it into a text file and read it.  Must be something on my end I guess.  
> **username_34** (2023-07-25): @MikeSamuel If I wasn't clear before, I've realized that a lot of the struggles I've had with SQL are centered around 'downmixing' from 3VL to 2VL.  For example, a CASE statement takes three values (true, false, and unknown) and maps them to THEN and ELSE.  I've also come to the conclusion that this is a big factor in why ORM is such a difficult problem.  Your point here seemed to align with those issues even though it's somewhat different than what this answer is about, I think.  
> **username_31** (2023-07-27): @JimmyJames Oh, is this the filling a portion of a joined row when one side of an outer join does not correspond to a row in one of the joining tables?  
> **username_34** (2023-07-27): @MikeSamuel That's one scenario where nulls can come into play, yes. Nulls can also be the 'explicit' value of a cell/field in a table. The thing is that in many scenarios, null/unknown is treated like `false` (but not always). For example, in a select, you either get a row, or you don't. If I select people who like in country X, I will not get people whose country is recorded as 'null'. Where things get weird is if I say I want everyone who is not from that country, I will not get the null rows.  In both scenarios, the condition result was unknown but the select treats it the same as false.  
> **username_34** (2023-07-27): This article explains this well: https://www.linkedin.com/pulse/null-bas-van-gils  
> **username_31** (2023-07-27): Ah, I see "The fundamental point I want to make is that certain boolean expressions—and therefore certain queries—produce results that are correct according to three-valued logic but not correct in the real world."  It's been a long time since I've slung SQL but I remember that being a pain; I remember a problem in an account creation flow where in between assigning a uid and entering their credentials in another table, anyone could log in as them because there was NULL in the salted-password column.  
---
### Answer by username_3 on 2023-07-19. Score: 15
Expressions containing `AND` and `OR` should not contain any part that is always truthy, or falsey - this should produce a warning or even an error.
If it's for learning, maybe errors are scary, a tip might be better.

TIP: `5 OR 12` always equals 5

TIP: `5 AND 12` always equals 12
---
### Answer by username_20 on 2023-07-18. Score: 12
You could issue a warning when an integer literal (this is important -- using an integer variable or a function result in this way can absolutely be legitimate) is used in a boolean context, i.e. in an `if` condition, or as an argument to `and`, `or` or `not`.

But on a larger scale, you might want to look into partial operator precedence ordering. This is a slightly weird use case for it, but it will still be immensly helpful. Essentially, instead of associating a priority with every operator (which in modern Python is done implicitly by just nesting the grammar rules in a certain way), you say that some operators' precedences are not comparable. For example, this could be done for `and` and `or`. Then the construct `a and b or c` would be invalid, and the programmer would have to explicitly specify `(a and b) or c` or `a and (b or c)`. For your case, you want comparisons to be 'parallel' to `or`, and perhaps `and`. Then the hypothetical student making that mistake would probably write `if number == (5 or 12):`, which would still result in a silent mistake, but of a different, more easily observable sort: `5` would work, but `12` wouldn't. Hopefully, the reasons behind this would be more apparent to a student.

In my language, I'm actually using the same (albeit modified heavily, but keeping the syntax) parser generator as Python 3.9+ does. I actually employ partial precedence ordering, and you can see examples of it in the [grammar definition](https://github.com/abel1502/bondrewd/blob/master/grammar/bondrewd.gram#L212-L234). Hopefully that should help implement it for yourself
> **Comments:**
> **username_21** (2023-07-19): Why a warning and not an error? I can't think of a valid use case for an integer literal as a boolean expression.  
> **username_20** (2023-07-19): @JackAidley Generated code could have it, for example. My general approach is to use warnings for unexpected or unusual things, if they are otherwise legitimate in terms of composability  
> **username_22** (2023-07-19): @JackAidley I very frequently use integer/boolean equivalence as a practical feature: for example, in expressions like `(-1)**signShouldBeInverted`.  Another case would be the "accumulator int-bool" where you're trying to assess whether something is true for at least one of many conditions which are iterated over in a loop.  Set `anyAreTrue` to 0 before the loop, then within the loop increment it by 1 every time a true condition is encountered. When the loop finishes, use it as a boolean: `if (anyAreTrue) { //etc. }`.  Or, to do something when a number is not 0: `if (numberValue) { //etc. }`.  
> **username_22** (2023-07-19): I really *dislike* how Swift (for example) disallows this -- you can't even *cast* Int to Bool there!  It definitely seems like a case where you should warn the new programmer that they *may* not be getting the result they want, instead of assuming they *must* be wrong and disabling such a potentially useful feature.  At least in my opinion - which may very well be in the minority. (My first programming language was JS, so I tend to take implicit type conversion for granted :D)  
> **username_21** (2023-07-19): @QuackE.Duck Those aren't literals though. A naked literal as int/Boolean equivalence is a different matter.  
> **username_22** (2023-07-19): @JackAidley That's an excellent point!  
> **username_10** (2023-07-22): Any literal is worth a warning there, IMO. In particular, boolean literals are only really even necessary for assigment/initialization, anyway.  
---
### Answer by username_23 on 2023-07-19. Score: 11
Keep your types separated, only True or False are legitimate boolean values, and make sure that or only works with boolean. This is for example how Scratch does it, with different shapes for different types of values.
Since 12 cannot be used as an operand for or, your interpreter will detect it.
Alternatively, you may want to make parentheses mandatory around logical operators to avoid confusion with and vs or too, as in:
if x == 3 and y == 2 or y == 5:
    pass
> **Comments:**
> **username_24** (2023-07-21): Right.  I've never understood why Python claims to have "explicit over implicit" as one of its core values and yet includes implicit conversion to boolean whenever one is required.  Require an explicit comparison is much better than just blindly saying an integer looks like it should mean True or False depending on what its value is...  
> **username_10** (2023-07-21): @occipita It turns out that language - and library - design is hard. I could probably give a whole talk about "the Zen of Python" vs. Python out of box. Starting, of course, with the `this` module itself.  
> **username_25** (2023-07-21): @occipita I try to fix the issues with doing implicit conversions the C-way in Tyr and it turns out that you need a hell lot of things to get it to work including compile-time evaluation of implicit conversions. I'm not there after working on the language for five years. It's simply not that easy.  
> **username_23** (2023-07-21): I do like the fact that in Lisp only NIL is false and everything else is true, it is convenient to have `(or this that)` evaluate to the first non-nil value for example.  
---
### Answer by username_10 on 2023-07-21. Score: 5
Use any/all operators and compound operators instead

I could of course special case this syntax and transform it into number in (5, 12), but the goal here is not to just make it work but help the students build an intuition for how expressions and statements work, and ease the transition to real python later in the course.

Even so, this is langdev.SE, so I want to explore this possibility. Since "special cases aren't special enough to break the rules", I'd like to propose something more self-consistent.
This approach is inspired by the built-in functions any and all in Python - what if they were operators instead, and replaced and and or (so as to avoid confusion by trying to use both at once)?
x OP any a, b, c would have semantics equivalent to (x OP a) or (x OP b) or (x OP c). Similarly for all with and. You could perhaps require OP any to be joined up into a single token. Either way, the fact that the single keyword any is not infix, and is in the same place as the operator, avoids the weirdness of seeming special-case semantics. (Of course, it would be possible to do this with conventional or and and, but such multifix operators are harder to implement.)
Similarly, just any a, b, c could mean a or b or c - there's no OP, so nothing to apply separately to each value.
To recover the functionality of Python's builtin any/all functions, then, just allow Python-like unpacking generalizations to work with the expression list:
# python
any(f(x) == 3 for x in ys)
# new language
3 == any *(f(x) for x in ys)
# or even:
3 == any *map(f, ys)

The hard part is making it short-circuit ;)
> **Comments:**
> **username_11** (2023-07-23): This reminds me of [Raku's Junction type](https://docs.raku.org/type/Junction): in Raku, you can write `if number == any(5, 12)`, or even `if number == 5 | 12`, and get the intuitively expected answer. The `any` creates a "superposition" of values, the `==` is applied to each member via "auto-threading", and the boolean context "collapses" to a single value.  
> **username_12** (2023-07-24): *"Similarly, just any a, b, c could mean a or b or c - there's no OP, so nothing to apply separately to each value."* ─ But this would make the grammar ambiguous: `x OP any a, b, c` could mean `x OP (any a, b, c)`. It would also be confusing, because in the main intended use-case the purpose of `OP` is to *prevent* the code from accidentally meaning `a or b or c`, whereas in your other use-case it does mean exactly that.  
> **username_10** (2023-07-24): "But this would make the grammar ambiguous" - that would be one reason to introduce single-token compound operators. ;) But really I just didn't consider operator precedence in laying out the core idea, because I considered that those issues are orthogonal. "It would also be confusing..." - the use case is to make the behaviour match intuition. What `or` does in other languages simply doesn't factor in to the design rationale. But if we like, we could make the intent very explicit with, perhaps, a spelling like `istrue any`.  
---
### Answer by username_13 on 2023-07-19. Score: 4
I think there's a philosophical question to be considered here: is it better for a teaching language to be "Python-like", or for it to parse statements properly so that **==** and **or** are treated with the correct precedence?

One way round it would be to avoid the issue by using extremely strict type checking, and to never apply automatic conversions which are an unending source of problems.

I'd add that experience has led me to believe that **=** should be "considered harmful", use **==** for comparison and **:=** for assignment with a simple **=** being an error in all cases.

But again, the question remains of how closely a teaching language should stick to Python syntax and conventions: the simplest solution, which could be applied without syntax changes, would be rigorous type checking.

Edited to add: as an historical note, <https://en.wikipedia.org/wiki/Plankalk%C3%BCl#Assignment_operation> describes controversy relating to whether Zuse favoured [![Zuse's claimed assignment operator](https://i.sstatic.net/MfM7U.png)](https://i.sstatic.net/MfM7U.png) as an assignment operator, which became ← (supported by ASCII-63 and several 6-bit character sets) or the digraph :=
> **Comments:**
> **username_14** (2023-07-19): I remember how my secondary school Physics teacher used to curse early programming language design for using `=` for assignment, when that symbol a long history of the equality sign in mathematics. Whenever he would see `x = x + 1`, he would cringe.  
> **username_15** (2023-07-20): At around age 11, I was learning BASIC and hit an example in a book which had a pair of lines like  `x = x + 1` and `y = x + 2` or something like that. I thought, what, is the machine supposed to solve those equations? But there is no solution: `x` cannot be equal to `x + 1`; why not use an example that has solutions?  
> **username_16** (2023-07-20): @Kaz Through the magic of floating-point, there are actually loads of values for _x_ that would satisfy (to a computer) the equation x = x + 1. The smallest single-precision float _x_ for which that is true is `16777216.`. For a double-precision float, it's `9007199254740992.d`.  
> **username_15** (2023-07-20): @AR-solidarityforModstrike Yes; that and saturation math.  
---
### Answer by username_17 on 2023-07-19. Score: 4
The underlying problem is that, unlike COBOL, python doesn't try to make sense when read as English.

COBOL was designed so that non-technical managers could mostly understand the code, and it worked well: One doesn't need a degree in mathematics to understand the meaning of `a IS GREATER THAN b AND c, OR EQUALS d`.

On the other hand, python is happy to combine different things in strange ways that make sense mathematically, but not to the "common reasonable man", who is not going to be able to understand that `if 12` has any sensible meaning.

Not surprisingly, kids and beginner programmers don't think like mathematicians (who can blindly accept rules like "`==` binds more tightly than `OR`").

You might consider requiring that for `a or b`, `a` and `b` must have the same type of semantics.  
Or better yet (as suggested by @coredump), don't allow implicit conversion to Boolean.  
E.g. reject Python's `if x > y or z` and require that it be entered as `if x > y or z != 0`.

Similarly reject expressions that in English either are ambiguous or have different interpretations.  
E.g. `x+y*a+b` should be an error, requiring `(x+y)*(a+b)` or `x+(y*a)+b`.

(To be honest, I think those would be good changes to make to the *real* Python, but I won't hold my breath.)
> **Comments:**
> **username_18** (2023-07-19): The underlying problem is that, unlike most programming languages, English doesn't try to make sense mathematically.  
---
### Answer by username_15 on 2023-07-20. Score: 3
If the problem were purely one of being confused about syntax, we could avoid the confusion by eliminating all implicit associativity and precedence in the infix operators (except perhaps for addition, subtraction, multiplication, division and perhaps exponents, if we have them).
Thus A == B or C would be a syntax error, requiring parentheses: either A == (B or C) or (A == B) or C.
However, even with the mandatory parentheses, students will still get tripped up and believe that A == (B or C) means (A == B) or (A == C).
Parentheses do not preclude distributive laws, whether real or imaginary. After all A * (B + C) is A * B + A * C!
Experienced programmers in C have a momentary lapse of reason and write something like if (state == (STATE_INITIAL || STATE_REQUEST_RECEIVED)) ...!!! This is a thing that happens; I've seen it in a code review more than once.
That particular expression pattern could be targeted by a code walker in your language, and subject to a diagnostic:
foo.lang:42:warning: X == (Y or Z) doesn't mean (X == Y) or (X == Z).

This will irks someone who knows what they are doing and wants that expression. That programmer wants to compare A with B, except when B is missing, in which case C is taken instead and compared with A.
One possibility would be to have a class of warnings that are targeted at neophytes. All learning materials would steer students toward operating the language in a way that the newbie warnings are turned on.
Ultimately, the way to reduce the semantic confusion would be to make a "Blub" languages. Don't have a clever or operator that returns one operand or the other. Have a pure Boolean or that yields true or false.
Then make it a type error to compare some X which is not Boolean to a Boolean value!
X == (Y or Z)     # error: comparing integer X with Boolean expression (Y or Z)

The idea of A or B or C yielding the leftmost one of A, B and C that is not falsy, and not evaluate the rest, comes from Lisp:
  (or A B C ...) ; yield A if that is true, else B if that is true, else ..

However, while it is that way in Common Lisp and related dialects, it was not so in Lisp 1, according to the 1960 manual.*  While Lisp 1 had N-ary AND and OR operators, which were short-circuiting, they yielded a strictly Boolean value. Doing anything like (EQL EXPR (OR A B)) in Lisp-1 would have meant that EXPR has a Boolean value, T or F, and that is being compared to the truth value of the OR.  If EXPR contained, say, a number, the comparison would be false even if both A and B contained that number.
So before Python could borrow the Lisp or; first the original Lisp one had to be upgraded to the more clever semantics.
Under the new, clever or, you will sometimes get the expected behavior. If expr evaluates to 42 and so does a, then (eql expr (or a b)) will in that case behave consistently with the belief that expr is being separately compared with a and with b. The student will run the code and not notice the problem. The failing test case is something like (eql 42 (or 73 42)). Cases like (eql 42 (or nil 42)) and (eql 42 (or 42 nil)) do not reproduce the issue, and can confirm the wrong belief.

See page 48.
> **Comments:**
> **username_16** (2023-07-20): I wonder if people who write `state == (STATE_INITIAL || STATE_REQUEST_RECEIVED)` are confused by the similar looking bitmap pattern `state & (STATE_INITIAL | STATE_REQUEST_RECEIVED)`.  
---
### Answer by username_19 on 2023-07-19. Score: 3
It may have something to do with my natural language isn't similar to English, but when I learned, I was fully aware that the name of this operation is "logical or". There is also "bitwise or" which is a different thing. I started in languages that have them sharing the same operator, but they are introduced differently nevertheless. Neither is the same thing as "or as in the common sense".

Depending on how old the kids are, it may remove some ambiguity by using the longer name in teaching.

Doing that in language design may not sound clever. It would be something like extending the keyword to `logical_or`. Or you could use the C-style operator `||`, and say its name is "logical or" and there isn't a shortened "or" mentioned anywhere.
> **Comments:**
> **username_13** (2023-07-20): I know that this isn't strictly accurate, but I tend to think of || && == as always yielding a boolean, hence possibly allowing !! to normalise an integer expression to a boolean without an explicit cast.  
---
