<p>Novices writing in mainstream languages often don't realise that conditions like <code>x &lt; y</code> are expressions which can occur anywhere an expression is syntactically expected; they may think that such expressions can only occur in the condition of an <code>if</code> statement, <code>while</code> statement or so on.</p>
<p>For example, beginners <a href="https://softwareengineering.stackexchange.com/a/12922/404182">often write code like this</a>, where <code>return condition;</code> would be simpler:</p>
<pre><code>if (condition) {
    return true;
} else {
    return false;
}
</code></pre>
<p>Novices with this misconception also often don't realise that booleans are values which can be compared with <code>==</code>, so they might write <code>(x &lt; 0 &amp;&amp; y &lt; 0) || (x &gt;= 0 &amp;&amp; y &gt;= 0)</code> instead of the simpler <code>(x &lt; 0) == (y &lt; 0)</code>.</p>
<p>Perhaps that is because in natural languages, we rarely refer to a condition's <em>truth value</em> as a noun. Conditions and the word &quot;if&quot; usually go together in natural language, so simplifying the above might not cross beginners' minds.</p>
<p>But in every programming language I have seen, conditions are expressions like any other, and the boolean type is like any other. They can be used anywhere any other expression or type can be used.</p>
<p><strong>My question:</strong> are there any languages which are designed in a way which reflects the beginners' mindset, where conditions are different to other expressions, and cannot be handled in the same way?</p>
<ul>
<li>Such a language might <em>require</em> a construct for handling conditions, which programmers of mainstream languages would consider redundant.</li>
<li>They might lack the ability to store a condition's result in a variable.</li>
<li>The comparison and logical operators might only be syntactically allowed in the conditions of <code>if</code> statements, <code>while</code> statements or so on.</li>
<li>Or if conditions <em>are</em> expressions, some operations (e.g. <code>return</code>) might still not be allowed for booleans, or only allowed for the literal values <code>true</code> and <code>false</code>.</li>
</ul>

<language-design><conditional><booleans>
Are there any languages where conditions are not first-class expressions?
CC BY-SA 4.0
2024-04-26T09:41:38.303Z
You seem to imply that programmers who don't use "==" as a boolean operator didn't realize it can be done.  What can be done is not necessarily a good thing.  I would reject such code in a code review because there are boolean operator for that.  Code like `a == b == c` is nonsense.  Use `a ^ b ^ c` instead.
Florian F
CC BY-SA 4.0
2024-04-26T10:56:17.647Z
@FlorianF There is no such implication, merely a statement that many novices don't realise `==` is possible to use with booleans. Personally I think `(x < 0) == (y < 0)` is a lot clearer than `!((x < 0) ^ (y < 0))`, and your example with three variables only works because the double negation cancels out.
kaya3
CC BY-SA 4.0
2024-04-26T18:22:29.170Z
@FlorianF If I recall correctly, in some strongly typed languages, including Java, you can compare booleans with `==` but not with `^`.
CPlus
CC BY-SA 4.0
2024-04-26T19:55:22.983Z
Java has a perfectly good operator `==` to compare any value.  Why would you use `^` to *compare* boolean?  That is still worse than using `==` for a negative xor.
Florian F
CC BY-SA 4.0
2024-04-26T20:01:06.043Z
@kaya3 I wanted to show that if you extend the principle with more than 2 values xored, the `==` becomes confusing.  And imho `x < 0 ^ y < 0` is clearer than `!((x < 0) == (y < 0))`.  You also chose the example that works well for you.
Florian F
CC BY-SA 4.0
2024-04-26T20:58:20.893Z
@FlorianF In the expression `(x < 0) == (y < 0)` you are *comparing* the signs of the two numbers, since that's what the comparisons determine; so it's perfectly sane to use `==` in this example. I don't read it as an XNOR and I doubt OP does. And to be clear, I didn't choose the example, OP did; and *of course* they chose an example which demonstrates what they are talking about! Nobody is saying that `==` should always be used with booleans, the point is that it *can* be used but beginners often don't realise that. Also why would you write `!(... == ...)` instead of `... != ...`?
kaya3
CC BY-SA 4.0
2024-04-27T10:12:02.903Z
I would have expected this to only become contentious with `p <= q` for “p implies q”, not already for `==`.
Carsten S
CC BY-SA 4.0
2024-04-28T12:12:43.270Z
@FlorianF *"Code like `a == b == c` is nonsense"* - Maybe in your preferred language. But not in general. In Python it simply means that `a` and `b` are equal and that `b` and `c` are equal. Just like you'd write and read "a=b=c" in math.
no comment
CC BY-SA 4.0
2024-04-28T13:57:54.203Z
@nocomment I didn't know that.  That's probably what it should mean intuitively, but in java it means `(a == b) == c`.
Florian F
CC BY-SA 4.0
2024-07-15T05:13:54.950Z
@FlorianF I would agree that `a == b == c` is nonsense. But `(a == b) == c` or `a == (b == c)` could make sense.
CPlus
<h3>csh/tcsh</h3>
<p>From the manpages [<a href="https://linux.die.net/man/1/csh" rel="nofollow noreferrer">1</a>] [<a href="https://man.freebsd.org/cgi/man.cgi?tcsh" rel="nofollow noreferrer">2</a>]:</p>
<blockquote>
<p>Expressions</p>
<p>The if, while and exit builtin commands use expressions with a common syntax. [...] Note that the @ builtin command (q.v.) has its own separate syntax.</p>
</blockquote>
<p>I haven't used <code>tcsh</code> much, but from this I gather that the expressions described in this section can only be used for conditionals (and returning an exit status). <code>@</code> is apparently how you do math on variables, but like it says right there, it's a separate syntax.</p>
<h3>CPP (C Preprocessor)</h3>
<p>There's also languages to consider like CPP, that use this type of conditional:</p>
<pre><code>#ifdef FOO
</code></pre>
<p>This conditional isn't built with any sort of expression.</p>
<p>Though, <a href="https://gcc.gnu.org/onlinedocs/cpp/If.html" rel="nofollow noreferrer">CPP also has</a>:</p>
<blockquote>
<p>The ‘#if’ directive allows you to test the value of an arithmetic
expression, rather than the mere existence of one macro.  Its syntax is</p>
<pre><code> #if EXPRESSION

 CONTROLLED TEXT

 #endif /* EXPRESSION */
</code></pre>
<p>EXPRESSION is a C expression of integer type, subject to stringent
restrictions.</p>
</blockquote>
<p>It can do arithmetic fine:</p>
<pre><code>$ gcc -Wall -xc - &lt;&lt; EOF &amp;&amp; ./a.out
#include &lt;stdio.h&gt;
int main() {
#if 1 + 1 == 2
  puts(&quot;success&quot;);
#else
  puts(&quot;fail&quot;);
#endif
} 
EOF
success
</code></pre>
<p>However, it's its own syntax, different than whatever you can put anywhere else. For example, you can't put function calls there:</p>
<pre><code>$ gcc -Wall -xc - &lt;&lt; EOF
#if printf(&quot;foo&quot;)  
#endif
int main() {}
EOF 
&lt;stdin&gt;:1:11: error: missing binary operator before token &quot;(&quot;
</code></pre>
<p>Nor strings:</p>
<pre><code>$ gcc -Wall -xc - &lt;&lt; EOF
#if &quot;foo&quot;
#endif
int main() {}
EOF
&lt;stdin&gt;:1:5: error: token &quot;&quot;foo&quot;&quot; is not valid in preprocessor expressions
</code></pre>
<p>Even though you can in other places where expressions are allowed.</p>

CC BY-SA 4.0
2024-04-26T21:45:42.790Z
2024-04-27T20:19:46.380Z
2024-04-27T20:19:46.380Z
JoL
<p>Early versions of Basic only allowed relational operators like &gt; and &lt; in IF statements, not in expressions.</p>

CC BY-SA 4.0
2024-04-30T20:44:21.437Z
2024-04-30T20:44:21.437Z
Peter Norvig
<p>This is not uncommon in older versions of BASIC. <a href="https://en.wikipedia.org/wiki/GW-BASIC" rel="noreferrer">GW-BASIC</a>, for example, does not have a boolean data type, so there is no way to use a boolean expression except in statements that specifically expect a boolean expression.</p>
<p>This makes it easy for GW-BASIC to reuse the single = operator for both equality expressions and assignment statements.</p>

CC BY-SA 4.0
2024-04-26T10:52:29.560Z
2024-04-26T10:52:29.560Z
FrederikVds
CC BY-SA 4.0
2024-04-26T11:31:09.267Z
That's also true for SmallBasic.
FlatAssembler
CC BY-SA 4.0
2024-04-26T13:26:02.103Z
Don't they simply use an integer value instead?  (IIRC, at least some BASICs do that: conditional operators return 0 for false and -1 for true, AND and OR are bitwise, and IF/WHILE treat 0 as false and non-zero as true.  But there's no problem about storing a condition in an integer variable.  I think very early versions of C worked that way, too, before the logical `&&` and `||` were added, and arguably it's still much like that under the covers.)  If so, I'm not sure that fits the question as asked.  Interesting, though.
gidds
CC BY-SA 4.0
2024-04-26T13:50:38.487Z
@gidds: BBC Basic certainly works the way you describe!
psmears
CC BY-SA 4.0
2024-04-26T15:14:24.943Z
It's been a long time, but I'm pretty sure it was true in the original Dartmouth BASIC.
Barmar
CC BY-SA 4.0
2024-04-26T16:06:51.800Z
@psmears Yes, that's the one I'm most familiar with :-)
gidds
CC BY-SA 4.0
2024-04-26T19:00:34.157Z
@gidds I don't think that's true for all versions of BASIC. We have some old BASIC code at work, and I'm pretty sure you can't do that in that version. I'll check on Monday.
FrederikVds
CC BY-SA 4.0
2024-04-26T20:59:33.370Z
When I was using GWBasic `a=b=c`  would compare b and c and put the result in a.
Jasen
<p>Going back to ancient/archaic languages: traditional COBOL has no concept of boolean and therefore it is impossible to assign an expression to any variable.</p>
<p>I believe FORTRAN, and RPG also may have this limitation, but I haven't seen them in more than 30 years, so they may have gained this capability since I last used them.</p>

CC BY-SA 4.0
2024-04-26T18:26:15.610Z
2024-04-26T18:26:15.610Z
millebi
CC BY-SA 4.0
2024-04-28T00:54:18.463Z
Original FORTRAN didn't, but 'LOGICAL' was added by FORTRAN IV in 1962, and standardized in 1966 -- that's quite a bit more than 30 years now :-)
dave_thompson_085
<p>Tcl is another example of such a language.</p>
<p>Tcl, the language, has no concept of expressions at all.</p>
<p>Tcl only has:</p>
<ul>
<li><p>statements of which the first word must be a command/function name. Basically statements are nothing more than a function call - tcl only has function calls and nothing else.</p>
</li>
<li><p>rules for what is a word and what is whitespace that separate words</p>
</li>
<li><p>rules for substituting/interpolating function calls and variable values inside strings</p>
</li>
</ul>
<p>That's all, it has nothing more than that.</p>
<p>Tcl has no <code>if</code> statement. Instead <code>if</code> is a function available in the standard library. Also, tcl does not have a <code>for</code> loop. Instead <code>for</code> is a function available in the standard library.</p>
<p>You can if you want implement your own <code>if</code> function which will replace the <code>if</code> from the standard library - you wouldn't want to do this though as it may break other parts of your code but you can if you want.</p>
<p>So the first argument to functions such as <code>if</code> or <code>while</code> is interpreted according to how the functions are defined. So if you don't like how &quot;expressions&quot; work in the built-in <code>if</code>, <code>while</code>, <code>for</code> etc. you can always implement your own expression parser in tcl and write your own <code>if</code> or <code>while</code> functions.</p>
<p>Tcl does have a function called <code>expr</code> that evaluates its arguments the way the built-in functions expect expressions to behave. So if you want to just evaluate an expression without calling an <code>if</code> etc. (for example if you just want to add two numbers and assign it to a variable) you can just use the <code>expr</code> function:</p>
<pre><code># adding numbers in tcl requires a function call:

set x [expr 1 + 2]

# In the code above we pass 3 strings to the `expr` function:
# the sting &quot;1&quot;, the string &quot;+&quot; and the string &quot;2&quot;.
# The `expr` function (not the tcl language) will evaluate them
# as an expression.
</code></pre>

CC BY-SA 4.0
2024-04-27T08:49:49.257Z
2024-04-27T08:49:49.257Z
slebetman
CC BY-SA 4.0
2024-04-27T08:52:39.500Z
And yes, `[` is a function call
slebetman
<p><a href="https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html" rel="nofollow noreferrer">Exim string expansion</a> works this way, conditions are a different expression space that does not overlap with other expressions, can only be evaluated in places that expect conditions  ie: <code>${if</code> and <code>$filter</code> operations. And thus cannot be stored in, or used directly from variables.</p>

CC BY-SA 4.0
2024-04-26T21:16:58.780Z
2024-04-27T17:25:09.170Z
2024-04-27T17:25:09.170Z
Jasen
<p>In Microsoft SQL Server, the Boolean Type is not available as a general-purpose type, and exists only in contexts that require a boolean expression.</p>
<p>This is spelled out in <a href="https://learn.microsoft.com/en-us/sql/t-sql/language-elements/comparison-operators-transact-sql?view=sql-server-ver16" rel="noreferrer">the manual for Comparison Operators</a>:</p>
<blockquote>
<p>The result of a comparison operator has the Boolean data type. This has three values: TRUE, FALSE, and UNKNOWN. Expressions that return a Boolean data type are known as Boolean expressions.</p>
<p>Unlike other SQL Server data types, a Boolean data type cannot be specified as the data type of a table column or variable, and cannot be returned in a result set.</p>
</blockquote>
<p>In order to represent a boolean property within a table or a Transact-SQL variable, you have to instead use <a href="https://learn.microsoft.com/en-us/sql/t-sql/data-types/bit-transact-sql?view=sql-server-ver16" rel="noreferrer">the <code>bit</code> type</a> which is an integer constrained to the values 0 and 1 (and NULL).</p>
<p>A boolean expression cannot be converted directly to a bit or vice versa, so you have to explicitly use comparison operators every time:</p>
<pre class="lang-sql prettyprint-override"><code>DECLARE @IsValid Bit

IF ( @SomeInputNumber &gt; 0 )
BEGIN
   SELECT @IsValid = 1
END ELSE BEGIN
   SELECT @IsValid = 0
END

-- or

SELECT @IsValid = CASE
    WHEN @SomeInputNumber &gt; 0 THEN 1
    ELSE 0
END

-- then later

IF ( @IsValid = 0 )
BEGIN
   -- Error handling
END
</code></pre>

CC BY-SA 4.0
2024-04-26T13:57:39.373Z
2024-04-27T12:17:27.283Z
2024-04-27T12:17:27.283Z
IMSoP
<p>Many computer architectures work like this at the level of machine code / assembly language.  There may be a &quot;condition code&quot; as part of the machine state, such that conditional branches can only be taken based on the condition code, which is only set by specific compare instructions.</p>
<hr />
<p>Let's take x86 as an example.  (I've simplified many details in what follows, and left the assembly examples un-optimized for clarity.) The special <code>FLAGS</code> register, distinct from the general-purpose registers that hold ordinary integer data, has bits for carry, sign, zero and overflow.  The conditional jump instructions <code>Jcc</code> will branch or not according to the values of these bits.  For instance, <code>JZ foo</code> will jump to <code>foo</code> if the zero flag is set; <code>JNC foo</code> will jump if the carry flag is not set, and so on.  And the flags are set by integer arithmetic and bitwise logic instructions, most notably <code>CMP</code>.</p>
<p>But there isn't a single instruction to conditionally jump based on a boolean value in a general-purpose register or memory.  You would first need a compare instruction to set the flags based on the value of the boolean, and then a conditional jump based on the flags.  So high-level code like</p>
<pre><code>bool b;
if (b) { stuff; }
</code></pre>
<p>would need to be compiled as</p>
<pre><code>    cmp bl, 0   ; alternative: test bl, bl
    je over     ; jump if zero flag set, meaning bl was equal to 0
    ; stuff...
over:
</code></pre>
<p>which looks more like</p>
<pre><code>if (b != false) { stuff; }
</code></pre>
<hr />
<p>Conversely, prior to the 386, conditional jumps were the natural way on x86 to materialize the result of a comparison in a boolean variable.  If you had high-level code like</p>
<pre><code>int c,d;
bool b;
b = (c &lt; d);
</code></pre>
<p>you would typically have to compile it into something like</p>
<pre><code>    cmp cx, dx
    jl was_less
    mov bl, 0
    jmp proceed
was_less:
    mov bl, 1
proceed:
</code></pre>
<p>which looks more like</p>
<pre><code>if (c &lt; d) {
    b = true;
} else {
    b = false;
}
</code></pre>
<p>The 386 added <code>SETcc</code> which let you set a register to 0 or 1 based on the flag values, so at that point you could instead do</p>
<pre><code>    cmp cx, dx
    setl bl
</code></pre>
<p>But still, the result of the comparison has to pass through the flag register before it can be materialized as a 0/1 byte value.</p>
<hr />
<p>If I have more time later, I'll try to come back and write a little about the pros and cons of this design feature, and mention how some other architectures do it.</p>

CC BY-SA 4.0
2024-04-26T15:19:49.643Z
2024-04-26T15:19:49.643Z
Nate Eldredge
CC BY-SA 4.0
2024-04-26T20:20:59.147Z
If in some programming language, "condition" expanded to either "conditionfactor" or "condition OR condition", "conditionfactor" expanded to "innercondition" or "conditionfactor AND conditionfactor" and "innercondition" expanded to either "(condition)", "NOT innercondition", or "value comparator value", conditions could be easily handled by a recursive-descent parser function that received arguments for "target label if true" and "target label if false", without ever having to treat the relational operators as yielding any kind of numeric value.
supercat
<p>This is true in Bourne Shell, and most of the shells that are derived from it, such as POSIX shell, <code>bash</code> and <code>ksh</code>. There are no first-class boolean values, condition testing is done by testing the exit status of a command. The exit status of the last command is stored in the variable <code>$?</code>, but you can't just test this directly, you have to write another command to test it, e.g.</p>
<pre><code>if some_program; then do_something; fi
</code></pre>
<p>is equivalent to</p>
<pre><code>some_program
if test $? -eq 0; then do_something; fi
</code></pre>
<p>We have to use the <code>test</code> command (which has the synonym <code>[</code>, giving the illusion that it's part of the syntax of <code>if</code>).</p>
<p>We can also write it as</p>
<pre><code>some_program &amp;&amp; do_sometihng
</code></pre>
<p>but this still isn't an example of boolean values; the <code>&amp;&amp;</code> and <code>||</code> operators for boolean expressions are like <code>if</code> in that they test the exit status of the command. You can't write something like <code>$? &amp;&amp; do_something</code> because <code>$?</code> isn't a command.</p>

CC BY-SA 4.0
2024-04-26T15:24:53.970Z
2024-04-26T15:24:53.970Z
Barmar
CC BY-SA 4.0
2024-04-28T20:14:46.560Z
Traditional C doesn't have booleans either, just various integer types. But what it does have, is builtin comparison operators and implicit interpretation of the integers into truth values for `if` and such. That comparison as part of the language is what sh doesn't have, instead having `test`/`[` is a command same as any other.
ilkkachu
CC BY-SA 4.0
2024-04-28T21:28:13.863Z
@ilkkachu While it doesn't have a distinct boolean type, any integer type acts as a first-class boolean value because it can be stored and passed around as a value.
Barmar
<p>Yes, there are languages where conditional expressions do not work like C and produce values, which I think is what you mean by &quot;normal language&quot; here.</p>
<p>One recent example is <a href="https://dev.epicgames.com/documentation/en-us/uefn/verse-language-reference" rel="noreferrer">Verse</a>, where the conditions of an <code>if</code> are failable expressions — computations that may succeed or fail — and the branch of the statement taken depends on the success, not the value. The conventional motivating example is something like this:</p>
<pre><code>if (Element := MyArray[Index]):
    Log(Element)
else:
    Print(&quot;No such element&quot;)
</code></pre>
<p>but <em>every</em> condition is failable, including relational operators. Anything that could fail — including something like a potential division by zero — must be in a context that allows failure. Those contexts are the if condition and a few other places, and there must always be a failable expression in those. More specifically, failable operations <a href="https://dev.epicgames.com/documentation/en-us/uefn/failure-in-verse" rel="noreferrer">have the <code>transacts</code> effect</a>, and the purpose is that any other side effects within the failure context are rolled back.</p>
<p>Comparisons like the ones in your examples are within the scope of this. <a href="https://dev.epicgames.com/documentation/en-us/uefn/operators-in-verse#comparison" rel="noreferrer"><code>&lt;</code> and <code>&gt;=</code> are a failable operations</a>, as is <code>and</code>. These operators don't have useful return values that you can store in a variable, return, or compare to one another: if you did hypothetically run <code>(x &lt; 0) = (y &lt; 0)</code>, a failure on either side would preempt the rest of it, and so you can't actually write that. You would always write out the explicit version instead.</p>
<p>The &quot;logic&quot; type is the typical Boolean true-or-false value, distinct from conditions used for control flow. These are real values that you can store and compare, and they can be lifted into failable contexts using the <code>?</code> operator, which just fails if the value is false. Those can be compared and combined in the way that you want, but getting the base values into that form in the first place is quite verbose.</p>
<p>This model is not enormously common, but it wasn't novel when Verse picked it up. It's also been used in research systems previously, as has the more monadic-value version of the same thing.</p>
<hr />
<p>Another instance is common in languages built around pattern matching of some kind; SNOBOL has been mentioned in the comments, and there are a number of others whose core approach is &quot;try to match the subject against this pattern, and then do something based on whether it matched or not&quot;.
It's often possible to combine multiple patterns together (match all/some/none of these), and those patterns could include things like numeric comparisons, but there isn't ever a &quot;result&quot; value — the result is always a direct control-flow change rather than a data value.</p>
<p>This class includes widespread tools like sed, but also a number of embedded domain-specific languages within other tools.</p>
<hr />
<p>Bespoke templating languages, especially those intended for end-user use, often have quite limited and specific control-flow structures that embed the test operations within themselves. A construct like <code>(x &lt; 0) == (y &lt; 0)</code> is liable to be non-obvious to an end user, and treating a decision as a value is more abstract than desired here. There will be either specific decision combinators only or control structures that internally integrate multiple branches (<code>IF ... AND ... THEN ...</code>). You've likely also encountered this approach in user interfaces for building up complex conditions in non-programming application software, which are reductive cases of visual programming languages.</p>
<p><a href="https://i.sstatic.net/lGjOMrP9.png" rel="noreferrer"><img src="https://i.sstatic.net/lGjOMrP9.png" alt="A Factorio screenshot with conditions OR(AND(Empty, 5s inactivity), 30s passed)" /></a></p>
<hr />
<p>One more concern that can come up is in languages that decouple Boolean truth and falsity from the true or false values themselves: these values may be true or false when used as conditions, but not be <em>equal</em> to other values in the same category.</p>
<ul>
<li>Relational operators may return one of their values in some cases to allow chaining, or else some false-coalescing value; these return values are naturally not equal.</li>
<li>Types may be able to have user-defined coercions to Boolean that do not align with equality of the original values; many languages have these, including dynamically-typed Python and statically-typed C#.</li>
<li>Yes-or-no results may exist in a separate space from data values, such as only being implicit results of an <em>action</em>; some command languages work this way.</li>
<li>The language may let any individual value be given a Boolean meaning; Raku allows <code>0 but True</code> to do this for any individual value.</li>
</ul>
<p>It's often not <em>impossible</em> to do what you're wanting in these languages, but it isn't the simple syntax-driven transformation you've given.</p>

CC BY-SA 4.0
2024-04-26T01:35:37.397Z
2024-04-27T01:24:52.163Z
2024-04-27T01:24:52.163Z
Michael Homer
CC BY-SA 4.0
2024-04-26T01:36:22.713Z
This seems more like Java's `try(var element = ...) { ... } catch { ... }`, or Rust's `if let Some(element) = ... { ... } else { ... }`, than a conventional `if` statement. I wouldn't call `Element := MyArray[Index]` a condition in the sense of this question; it can't be composed with `&&` and `||`, for example.
kaya3
CC BY-SA 4.0
2024-04-26T01:40:59.983Z
@kaya3 Throwing an exception is also a kind of effect, and you could transpose a lot of this model into e.g. Java by turning all your `if`s into `try`s with the then block below the conditions and the else block in a catch, and making all your comparison operations throw a checked exception. You wouldn't get the transactional behaviour out of that, though, since mutations that had already happened would persist, and non-failable conditions would be permitted. It's not hugely similar except that you can do a partial translation between these effects.
Michael Homer
CC BY-SA 4.0
2024-04-26T01:42:07.640Z
Maybe I need to be more explicit that the conditional operators are also failable; the assignment statements are the motivation for them having this, while the conditions are the impact for this question.
Michael Homer
CC BY-SA 4.0
2024-04-26T01:53:29.677Z
If I understand correctly, conditions like `x < y` are more analogous to `if(!(x < y)) { throw ...; }` in traditional languages? Could I write `x < y` by itself for the purpose of asserting a precondition, for example?
kaya3
CC BY-SA 4.0
2024-04-26T02:00:05.323Z
You can only write `x < y` in a failure context, which includes `if`, the `not` prefix operator, certain parts of for loops, and a few others. You can write `x < y` on its own within the body of an `if` for a sort of general assertion (and it will fail and roll back the transaction), but not really as a precondition because you're not allowed to fail in most places. You can write something like `if: set x = 1 ; x < y ; Print("1 < y")`, and it will roll back the assignment to `x` when the test fails, because the whole `if` is a failure context, but I don't think it's relevant to the question.
Michael Homer
CC BY-SA 4.0
2024-04-26T02:01:00.903Z
You could look at them as analogous to Java methods throwing a checked exception, but it's not a perfect analogy.
Michael Homer
CC BY-SA 4.0
2024-04-26T06:12:15.950Z
This sounds a lot like [SNOBOL](https://en.wikipedia.org/wiki/SNOBOL) and [ICON](https://en.wikipedia.org/wiki/Icon_(programming_language)).  These languages have a special "fail" value rather than boolean type.  So comparisons either fail or return their right operand, allowing things like `A < B < C`.  You can't store a failure in a variable -- you need to catch it and branch to a label (SNOBOL) or use an "if" or "while" (ICON) that does the control flow based on the fail.
Chris Dodd
CC BY-SA 4.0
2024-04-26T20:35:56.513Z
@kaya3 Also note that the fallible thing in the `Element := MyArray[Index]` example is not the assignment, but the array indexing. Thus it can be composed with `&&` and `||`, which operate on the fallibility rather than the values of their operands.
rpjohnst
CC BY-SA 4.0
2024-04-26T20:52:28.127Z
@rpjohnst If you combine it with `||`, then `Element` doesn't necessarily have a binding in the `if` statement's "success" block.
kaya3
CC BY-SA 4.0
2024-04-26T21:28:02.950Z
@kaya3 Verse treats that as an issue of scoping, and doesn't otherwise prevent you from composing fallible expressions with `and` and `or`.

(And not having a binding is also not necessarily fatal, as Verse is a logic language.)
rpjohnst
CC BY-SA 4.0
2024-04-27T12:37:33.097Z
The raku [infix `but`](https://docs.raku.org/routine/but) is kind of the *opposite* of this question: `Bool` is a first-class type, but objects can define how they will be _coerced_ to that type. `0 but True` creates an object with a `Bool` method, which is called when conversion to `Bool` is needed - it's essentially short-hand for `role CoercesToTrue { method Bool { True } }; my $x = 0 but CoercesToTrue;`. You can mix in any definition of that conversion method, e.g. `role RandomlyTrue { method Bool { [True, False].pick } }; my $x = 0 but RandomlyTrue;`
IMSoP
CC BY-SA 4.0
2024-04-27T18:36:47.943Z
@IMSoP Yes, it’s like the previous step about implicit coercions, but on the individual value level rather than type level. Either way, you can get something that is treated as true/false, can be used with`&&`, but isn’t equal to the other values that are also treated as true, so the shorthand doesn’t work.
Michael Homer
# Are there any languages where conditions are not first-class expressions?
Posted by: username_0 on 2024-04-26
Novices writing in mainstream languages often don't realise that conditions like x < y are expressions which can occur anywhere an expression is syntactically expected; they may think that such expressions can only occur in the condition of an if statement, while statement or so on.
For example, beginners often write code like this, where return condition; would be simpler:
if (condition) {
    return true;
} else {
    return false;
}

Novices with this misconception also often don't realise that booleans are values which can be compared with ==, so they might write (x < 0 && y < 0) || (x >= 0 && y >= 0) instead of the simpler (x < 0) == (y < 0).
Perhaps that is because in natural languages, we rarely refer to a condition's truth value as a noun. Conditions and the word "if" usually go together in natural language, so simplifying the above might not cross beginners' minds.
But in every programming language I have seen, conditions are expressions like any other, and the boolean type is like any other. They can be used anywhere any other expression or type can be used.
My question: are there any languages which are designed in a way which reflects the beginners' mindset, where conditions are different to other expressions, and cannot be handled in the same way?

Such a language might require a construct for handling conditions, which programmers of mainstream languages would consider redundant.
They might lack the ability to store a condition's result in a variable.
The comparison and logical operators might only be syntactically allowed in the conditions of if statements, while statements or so on.
Or if conditions are expressions, some operations (e.g. return) might still not be allowed for booleans, or only allowed for the literal values true and false.
**Tags:** language-design conditional booleans
> **Comments:**
> **username_1**: You seem to imply that programmers who don't use "==" as a boolean operator didn't realize it can be done.  What can be done is not necessarily a good thing.  I would reject such code in a code review because there are boolean operator for that.  Code like `a == b == c` is nonsense.  Use `a ^ b ^ c` instead.  
> **username_2**: @FlorianF There is no such implication, merely a statement that many novices don't realise `==` is possible to use with booleans. Personally I think `(x < 0) == (y < 0)` is a lot clearer than `!((x < 0) ^ (y < 0))`, and your example with three variables only works because the double negation cancels out.  
> **username_0**: @FlorianF If I recall correctly, in some strongly typed languages, including Java, you can compare booleans with `==` but not with `^`.  
> **username_1**: Java has a perfectly good operator `==` to compare any value.  Why would you use `^` to *compare* boolean?  That is still worse than using `==` for a negative xor.  
> **username_1**: @kaya3 I wanted to show that if you extend the principle with more than 2 values xored, the `==` becomes confusing.  And imho `x < 0 ^ y < 0` is clearer than `!((x < 0) == (y < 0))`.  You also chose the example that works well for you.  
> **username_2**: @FlorianF In the expression `(x < 0) == (y < 0)` you are *comparing* the signs of the two numbers, since that's what the comparisons determine; so it's perfectly sane to use `==` in this example. I don't read it as an XNOR and I doubt OP does. And to be clear, I didn't choose the example, OP did; and *of course* they chose an example which demonstrates what they are talking about! Nobody is saying that `==` should always be used with booleans, the point is that it *can* be used but beginners often don't realise that. Also why would you write `!(... == ...)` instead of `... != ...`?  
> **username_3**: I would have expected this to only become contentious with `p <= q` for “p implies q”, not already for `==`.  
> **username_4**: @FlorianF *"Code like `a == b == c` is nonsense"* - Maybe in your preferred language. But not in general. In Python it simply means that `a` and `b` are equal and that `b` and `c` are equal. Just like you'd write and read "a=b=c" in math.  
> **username_1**: @nocomment I didn't know that.  That's probably what it should mean intuitively, but in java it means `(a == b) == c`.  
> **username_0**: @FlorianF I would agree that `a == b == c` is nonsense. But `(a == b) == c` or `a == (b == c)` could make sense.  
---
## 10 answers
### Answer by username_11 on 2024-04-26. Score: 19
This is true in Bourne Shell, and most of the shells that are derived from it, such as POSIX shell, bash and ksh. There are no first-class boolean values, condition testing is done by testing the exit status of a command. The exit status of the last command is stored in the variable $?, but you can't just test this directly, you have to write another command to test it, e.g.
if some_program; then do_something; fi

is equivalent to
some_program
if test $? -eq 0; then do_something; fi

We have to use the test command (which has the synonym [, giving the illusion that it's part of the syntax of if).
We can also write it as
some_program && do_sometihng

but this still isn't an example of boolean values; the && and || operators for boolean expressions are like if in that they test the exit status of the command. You can't write something like $? && do_something because $? isn't a command.
> **Comments:**
> **username_19** (2024-04-28): Traditional C doesn't have booleans either, just various integer types. But what it does have, is builtin comparison operators and implicit interpretation of the integers into truth values for `if` and such. That comparison as part of the language is what sh doesn't have, instead having `test`/`[` is a command same as any other.  
> **username_11** (2024-04-28): @ilkkachu While it doesn't have a distinct boolean type, any integer type acts as a first-class boolean value because it can be stored and passed around as a value.  
---
### Answer by username_20 on 2024-04-26. Score: 19
Yes, there are languages where conditional expressions do not work like C and produce values, which I think is what you mean by "normal language" here.
One recent example is Verse, where the conditions of an if are failable expressions — computations that may succeed or fail — and the branch of the statement taken depends on the success, not the value. The conventional motivating example is something like this:
if (Element := MyArray[Index]):
    Log(Element)
else:
    Print("No such element")

but every condition is failable, including relational operators. Anything that could fail — including something like a potential division by zero — must be in a context that allows failure. Those contexts are the if condition and a few other places, and there must always be a failable expression in those. More specifically, failable operations have the transacts effect, and the purpose is that any other side effects within the failure context are rolled back.
Comparisons like the ones in your examples are within the scope of this. < and >= are a failable operations, as is and. These operators don't have useful return values that you can store in a variable, return, or compare to one another: if you did hypothetically run (x < 0) = (y < 0), a failure on either side would preempt the rest of it, and so you can't actually write that. You would always write out the explicit version instead.
The "logic" type is the typical Boolean true-or-false value, distinct from conditions used for control flow. These are real values that you can store and compare, and they can be lifted into failable contexts using the ? operator, which just fails if the value is false. Those can be compared and combined in the way that you want, but getting the base values into that form in the first place is quite verbose.
This model is not enormously common, but it wasn't novel when Verse picked it up. It's also been used in research systems previously, as has the more monadic-value version of the same thing.

Another instance is common in languages built around pattern matching of some kind; SNOBOL has been mentioned in the comments, and there are a number of others whose core approach is "try to match the subject against this pattern, and then do something based on whether it matched or not".
It's often possible to combine multiple patterns together (match all/some/none of these), and those patterns could include things like numeric comparisons, but there isn't ever a "result" value — the result is always a direct control-flow change rather than a data value.
This class includes widespread tools like sed, but also a number of embedded domain-specific languages within other tools.

Bespoke templating languages, especially those intended for end-user use, often have quite limited and specific control-flow structures that embed the test operations within themselves. A construct like (x < 0) == (y < 0) is liable to be non-obvious to an end user, and treating a decision as a value is more abstract than desired here. There will be either specific decision combinators only or control structures that internally integrate multiple branches (IF ... AND ... THEN ...). You've likely also encountered this approach in user interfaces for building up complex conditions in non-programming application software, which are reductive cases of visual programming languages.


One more concern that can come up is in languages that decouple Boolean truth and falsity from the true or false values themselves: these values may be true or false when used as conditions, but not be equal to other values in the same category.

Relational operators may return one of their values in some cases to allow chaining, or else some false-coalescing value; these return values are naturally not equal.
Types may be able to have user-defined coercions to Boolean that do not align with equality of the original values; many languages have these, including dynamically-typed Python and statically-typed C#.
Yes-or-no results may exist in a separate space from data values, such as only being implicit results of an action; some command languages work this way.
The language may let any individual value be given a Boolean meaning; Raku allows 0 but True to do this for any individual value.

It's often not impossible to do what you're wanting in these languages, but it isn't the simple syntax-driven transformation you've given.
> **Comments:**
> **username_2** (2024-04-26): This seems more like Java's `try(var element = ...) { ... } catch { ... }`, or Rust's `if let Some(element) = ... { ... } else { ... }`, than a conventional `if` statement. I wouldn't call `Element := MyArray[Index]` a condition in the sense of this question; it can't be composed with `&&` and `||`, for example.  
> **username_20** (2024-04-26): @kaya3 Throwing an exception is also a kind of effect, and you could transpose a lot of this model into e.g. Java by turning all your `if`s into `try`s with the then block below the conditions and the else block in a catch, and making all your comparison operations throw a checked exception. You wouldn't get the transactional behaviour out of that, though, since mutations that had already happened would persist, and non-failable conditions would be permitted. It's not hugely similar except that you can do a partial translation between these effects.  
> **username_20** (2024-04-26): Maybe I need to be more explicit that the conditional operators are also failable; the assignment statements are the motivation for them having this, while the conditions are the impact for this question.  
> **username_2** (2024-04-26): If I understand correctly, conditions like `x < y` are more analogous to `if(!(x < y)) { throw ...; }` in traditional languages? Could I write `x < y` by itself for the purpose of asserting a precondition, for example?  
> **username_20** (2024-04-26): You can only write `x < y` in a failure context, which includes `if`, the `not` prefix operator, certain parts of for loops, and a few others. You can write `x < y` on its own within the body of an `if` for a sort of general assertion (and it will fail and roll back the transaction), but not really as a precondition because you're not allowed to fail in most places. You can write something like `if: set x = 1 ; x < y ; Print("1 < y")`, and it will roll back the assignment to `x` when the test fails, because the whole `if` is a failure context, but I don't think it's relevant to the question.  
> **username_20** (2024-04-26): You could look at them as analogous to Java methods throwing a checked exception, but it's not a perfect analogy.  
> **username_21** (2024-04-26): This sounds a lot like [SNOBOL](https://en.wikipedia.org/wiki/SNOBOL) and [ICON](https://en.wikipedia.org/wiki/Icon_(programming_language)).  These languages have a special "fail" value rather than boolean type.  So comparisons either fail or return their right operand, allowing things like `A < B < C`.  You can't store a failure in a variable -- you need to catch it and branch to a label (SNOBOL) or use an "if" or "while" (ICON) that does the control flow based on the fail.  
> **username_22** (2024-04-26): @kaya3 Also note that the fallible thing in the `Element := MyArray[Index]` example is not the assignment, but the array indexing. Thus it can be composed with `&&` and `||`, which operate on the fallibility rather than the values of their operands.  
> **username_2** (2024-04-26): @rpjohnst If you combine it with `||`, then `Element` doesn't necessarily have a binding in the `if` statement's "success" block.  
> **username_22** (2024-04-26): @kaya3 Verse treats that as an issue of scoping, and doesn't otherwise prevent you from composing fallible expressions with `and` and `or`.

(And not having a binding is also not necessarily fatal, as Verse is a logic language.)  
> **username_16** (2024-04-27): The raku [infix `but`](https://docs.raku.org/routine/but) is kind of the *opposite* of this question: `Bool` is a first-class type, but objects can define how they will be _coerced_ to that type. `0 but True` creates an object with a `Bool` method, which is called when conversion to `Bool` is needed - it's essentially short-hand for `role CoercesToTrue { method Bool { True } }; my $x = 0 but CoercesToTrue;`. You can mix in any definition of that conversion method, e.g. `role RandomlyTrue { method Bool { [True, False].pick } }; my $x = 0 but RandomlyTrue;`  
> **username_20** (2024-04-27): @IMSoP Yes, it’s like the previous step about implicit coercions, but on the individual value level rather than type level. Either way, you can get something that is treated as true/false, can be used with`&&`, but isn’t equal to the other values that are also treated as true, so the shorthand doesn’t work.  
---
### Answer by username_16 on 2024-04-26. Score: 12
In Microsoft SQL Server, the Boolean Type is not available as a general-purpose type, and exists only in contexts that require a boolean expression.
This is spelled out in the manual for Comparison Operators:

The result of a comparison operator has the Boolean data type. This has three values: TRUE, FALSE, and UNKNOWN. Expressions that return a Boolean data type are known as Boolean expressions.
Unlike other SQL Server data types, a Boolean data type cannot be specified as the data type of a table column or variable, and cannot be returned in a result set.

In order to represent a boolean property within a table or a Transact-SQL variable, you have to instead use the bit type which is an integer constrained to the values 0 and 1 (and NULL).
A boolean expression cannot be converted directly to a bit or vice versa, so you have to explicitly use comparison operators every time:
DECLARE @IsValid Bit

IF ( @SomeInputNumber > 0 )
BEGIN
   SELECT @IsValid = 1
END ELSE BEGIN
   SELECT @IsValid = 0
END

-- or

SELECT @IsValid = CASE
    WHEN @SomeInputNumber > 0 THEN 1
    ELSE 0
END

-- then later

IF ( @IsValid = 0 )
BEGIN
   -- Error handling
END
---
### Answer by username_17 on 2024-04-26. Score: 12
Many computer architectures work like this at the level of machine code / assembly language.  There may be a "condition code" as part of the machine state, such that conditional branches can only be taken based on the condition code, which is only set by specific compare instructions.

Let's take x86 as an example.  (I've simplified many details in what follows, and left the assembly examples un-optimized for clarity.) The special FLAGS register, distinct from the general-purpose registers that hold ordinary integer data, has bits for carry, sign, zero and overflow.  The conditional jump instructions Jcc will branch or not according to the values of these bits.  For instance, JZ foo will jump to foo if the zero flag is set; JNC foo will jump if the carry flag is not set, and so on.  And the flags are set by integer arithmetic and bitwise logic instructions, most notably CMP.
But there isn't a single instruction to conditionally jump based on a boolean value in a general-purpose register or memory.  You would first need a compare instruction to set the flags based on the value of the boolean, and then a conditional jump based on the flags.  So high-level code like
bool b;
if (b) { stuff; }

would need to be compiled as
    cmp bl, 0   ; alternative: test bl, bl
    je over     ; jump if zero flag set, meaning bl was equal to 0
    ; stuff...
over:

which looks more like
if (b != false) { stuff; }


Conversely, prior to the 386, conditional jumps were the natural way on x86 to materialize the result of a comparison in a boolean variable.  If you had high-level code like
int c,d;
bool b;
b = (c < d);

you would typically have to compile it into something like
    cmp cx, dx
    jl was_less
    mov bl, 0
    jmp proceed
was_less:
    mov bl, 1
proceed:

which looks more like
if (c < d) {
    b = true;
} else {
    b = false;
}

The 386 added SETcc which let you set a register to 0 or 1 based on the flag values, so at that point you could instead do
    cmp cx, dx
    setl bl

But still, the result of the comparison has to pass through the flag register before it can be materialized as a 0/1 byte value.

If I have more time later, I'll try to come back and write a little about the pros and cons of this design feature, and mention how some other architectures do it.
> **Comments:**
> **username_18** (2024-04-26): If in some programming language, "condition" expanded to either "conditionfactor" or "condition OR condition", "conditionfactor" expanded to "innercondition" or "conditionfactor AND conditionfactor" and "innercondition" expanded to either "(condition)", "NOT innercondition", or "value comparator value", conditions could be easily handled by a recursive-descent parser function that received arguments for "target label if true" and "target label if false", without ever having to treat the relational operators as yielding any kind of numeric value.  
---
### Answer by username_7 on 2024-04-26. Score: 8
This is not uncommon in older versions of BASIC. [GW-BASIC](https://en.wikipedia.org/wiki/GW-BASIC), for example, does not have a boolean data type, so there is no way to use a boolean expression except in statements that specifically expect a boolean expression.

This makes it easy for GW-BASIC to reuse the single = operator for both equality expressions and assignment statements.
> **Comments:**
> **username_8** (2024-04-26): That's also true for SmallBasic.  
> **username_9** (2024-04-26): Don't they simply use an integer value instead?  (IIRC, at least some BASICs do that: conditional operators return 0 for false and -1 for true, AND and OR are bitwise, and IF/WHILE treat 0 as false and non-zero as true.  But there's no problem about storing a condition in an integer variable.  I think very early versions of C worked that way, too, before the logical `&&` and `||` were added, and arguably it's still much like that under the covers.)  If so, I'm not sure that fits the question as asked.  Interesting, though.  
> **username_10** (2024-04-26): @gidds: BBC Basic certainly works the way you describe!  
> **username_11** (2024-04-26): It's been a long time, but I'm pretty sure it was true in the original Dartmouth BASIC.  
> **username_9** (2024-04-26): @psmears Yes, that's the one I'm most familiar with :-)  
> **username_7** (2024-04-26): @gidds I don't think that's true for all versions of BASIC. We have some old BASIC code at work, and I'm pretty sure you can't do that in that version. I'll check on Monday.  
> **username_12** (2024-04-26): When I was using GWBasic `a=b=c`  would compare b and c and put the result in a.  
---
### Answer by username_5 on 2024-04-26. Score: 4
csh/tcsh
From the manpages [1] [2]:

Expressions
The if, while and exit builtin commands use expressions with a common syntax. [...] Note that the @ builtin command (q.v.) has its own separate syntax.

I haven't used tcsh much, but from this I gather that the expressions described in this section can only be used for conditionals (and returning an exit status). @ is apparently how you do math on variables, but like it says right there, it's a separate syntax.
CPP (C Preprocessor)
There's also languages to consider like CPP, that use this type of conditional:
#ifdef FOO

This conditional isn't built with any sort of expression.
Though, CPP also has:

The ‘#if’ directive allows you to test the value of an arithmetic
expression, rather than the mere existence of one macro.  Its syntax is
 #if EXPRESSION

 CONTROLLED TEXT

 #endif /* EXPRESSION */

EXPRESSION is a C expression of integer type, subject to stringent
restrictions.

It can do arithmetic fine:
$ gcc -Wall -xc - << EOF && ./a.out
#include 
int main() {
#if 1 + 1 == 2
  puts("success");
#else
  puts("fail");
#endif
} 
EOF
success

However, it's its own syntax, different than whatever you can put anywhere else. For example, you can't put function calls there:
$ gcc -Wall -xc - << EOF
#if printf("foo")  
#endif
int main() {}
EOF 
:1:11: error: missing binary operator before token "("

Nor strings:
$ gcc -Wall -xc - << EOF
#if "foo"
#endif
int main() {}
EOF
:1:5: error: token ""foo"" is not valid in preprocessor expressions

Even though you can in other places where expressions are allowed.
---
### Answer by username_6 on 2024-04-30. Score: 4
Early versions of Basic only allowed relational operators like > and < in IF statements, not in expressions.
---
### Answer by username_13 on 2024-04-26. Score: 3
Going back to ancient/archaic languages: traditional COBOL has no concept of boolean and therefore it is impossible to assign an expression to any variable.

I believe FORTRAN, and RPG also may have this limitation, but I haven't seen them in more than 30 years, so they may have gained this capability since I last used them.
> **Comments:**
> **username_14** (2024-04-28): Original FORTRAN didn't, but 'LOGICAL' was added by FORTRAN IV in 1962, and standardized in 1966 -- that's quite a bit more than 30 years now :-)  
---
### Answer by username_15 on 2024-04-27. Score: 2
Tcl is another example of such a language.
Tcl, the language, has no concept of expressions at all.
Tcl only has:

statements of which the first word must be a command/function name. Basically statements are nothing more than a function call - tcl only has function calls and nothing else.

rules for what is a word and what is whitespace that separate words

rules for substituting/interpolating function calls and variable values inside strings


That's all, it has nothing more than that.
Tcl has no if statement. Instead if is a function available in the standard library. Also, tcl does not have a for loop. Instead for is a function available in the standard library.
You can if you want implement your own if function which will replace the if from the standard library - you wouldn't want to do this though as it may break other parts of your code but you can if you want.
So the first argument to functions such as if or while is interpreted according to how the functions are defined. So if you don't like how "expressions" work in the built-in if, while, for etc. you can always implement your own expression parser in tcl and write your own if or while functions.
Tcl does have a function called expr that evaluates its arguments the way the built-in functions expect expressions to behave. So if you want to just evaluate an expression without calling an if etc. (for example if you just want to add two numbers and assign it to a variable) you can just use the expr function:
# adding numbers in tcl requires a function call:

set x [expr 1 + 2]

# In the code above we pass 3 strings to the `expr` function:
# the sting "1", the string "+" and the string "2".
# The `expr` function (not the tcl language) will evaluate them
# as an expression.
> **Comments:**
> **username_15** (2024-04-27): And yes, `[` is a function call  
---
### Answer by username_12 on 2024-04-26. Score: 0
[Exim string expansion](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html) works this way, conditions are a different expression space that does not overlap with other expressions, can only be evaluated in places that expect conditions ie: `${if` and `$filter` operations. And thus cannot be stored in, or used directly from variables.
---
