<p>In C++, <code>3 || 4</code> returns true. But in Python, <code>3 or 4</code> returns 3 instead. That's a logical operator that returns its operand. I don't know if there is a better term.</p>
<p>In dynamically typed languages, doing this is simple. But in statically typed languages, what could the operators with operands in different types reasonably return? And are there statically typed languages already doing this?</p>
<p>I think it has to satisfy at least these two criteria:</p>
<ul>
<li>Changing one of the operand to a subclass that doesn't change relevant things should not make the result significantly different. (Better if it could potentially support indirect implicit casting, but I guess that might be too difficult.)</li>
<li>If the expression is used as a boolean value, it matches the result of the logical operator on the two operands cast to boolean values beforehand, at least if both always gives a consistent boolean value.</li>
</ul>

<static-typing><type-inference><booleans><logical-operators>
How to make logical operators that return operands consistent in a statically typed language?
CC BY-SA 4.0
2023-08-18T12:00:27.687Z
Python's `x or y` is a bit like what `is_truthy(x) ? x : y` would be in C(++), so that's one place to perhaps look at. e.g. https://en.cppreference.com/w/cpp/language/operator_other
ilkkachu
CC BY-SA 4.0
2023-08-18T14:39:50.703Z
Isn't there a trivial solution of requiring that both operands be the same type?
Barmar
CC BY-SA 4.0
2024-07-26T20:38:33.303Z
I think it depends on how sophisticated your type system is. https://arxiv.org/pdf/2111.03354 discusses a language in which the type of the negation operator isn't just `bool -> bool`, but `(true -> false) & (false -> true)`, where `true` and `false` represent singleton subtypes of `bool` containing only the corresponding value, and `&` computes intersection types.  Using some obvious(?) hypothetical constructs, Python's `or` could be typed as `(Truthy[T] -> U -> T) & (Falsey[T] -> U -> U)`.
chepner
CC BY-SA 4.0
2024-07-26T22:47:31.217Z
That's not only a type system, but also almost a precise description of the function itself. The extreme in this direction is to say the type of a variable is the set of all values that are not proven to be impossible to contain. Not very helpful to actually design a type system, but a good place to start thinking.
user23013
CC BY-SA 4.0
2024-09-14T00:18:16.133Z
3 or 4 is 3 under bit logic...
Roddy MacPhee
CC BY-SA 4.0
2024-09-14T04:41:53.050Z
@RoddyMacPhee In bitwise operation `3 or 4` is 7.
user23013
<p>TL;DR: yes and no</p>
<p>We need to take a step back to understand why the question is a bit strange and why programming languages do not behave like this. I'll still provide a Tyr implementation for Integer to sketch a solution.</p>
<p>First of all, there is no such thing as a logical operator. There is a truth type, e.g. <code>bool</code> that might have operators. These operator symbols might be reused by other types. Usually, there is <code>||</code> and <code>|</code>.</p>
<p>The first strange thing about the question is that <code>||</code> is usually lazy and implemented with an <a href="https://github.com/tyr-lang/stdlib/blob/923229873df2e8a6373ac22e8940fe5301b9fed3/lang/src/bool.tyr#L24" rel="nofollow noreferrer">if then else</a>. Most languages allowing to overload operators treat operators like functions, i.e. the right argument will always be evaluated preventing libraries to provide this behavior.</p>
<p>So, the remaining question is how can a statically typed language implement something like 3 or 4 in Python.</p>
<h2>Step one, we do it just for <code>Integer[_]</code>.</h2>
<pre><code>public def || (x : Block[Integer[Size]]) : Integer[Size]
     &lt;: operator.precedence[30], operator.rightAssociative =
if this.toBool() this else x.eval()
</code></pre>
<p>I'll assume that there is a <code>toBool</code> function; it could be <code>0 == this</code> or similar. It does not matter. What matters here is the <code>Block</code> type and <code>eval()</code> call. Because this is what causes evaluation to happen in the correct branch of the CFG.</p>
<p>With this pattern, you can define your operator for every type where you consider this behavior reasonable. My personal taste is that it is never a good idea. Especially if you provide an <em>implicit</em> conversion to <code>bool</code>.</p>
<p>If you are looking for the eager evaluation semantic, you could simply define it as</p>
<pre><code>public def | (x : Integer[Size]) : Integer[Size]
     &lt;: operator.precedence[50], operator.rightAssociative =
if this.toBool() this else x
</code></pre>
<h2>Step two, extension to arbitrary types.</h2>
<p>Here, we have several options that all come with downsides.</p>
<p>One option would be to add an implicit conversion from any type to bool. This would result in C++'s solution if there wouldn't be an overload that results in a value.</p>
<p>We could define the operator in any as <code>any || any : any</code>. This would require any to have a physical representation, which it usually does not have. Also, it would cause ambiguities with actual logical operations.</p>
<p>We could try to define an operator <code>∀T,U. T || U : union(T,U)</code>. This, again, would require that <code>union(T,U)</code> is representable, which it is not in most languages. E.g. <code>0 || &quot;hello&quot;</code> would be either a pointer to string literal or a 32bit integer. To fix this, we'd need some sort of constraint requiring that T and U share the <em>same</em> physical representation. Even if there were a language that could express this relation, you'd still also need inference for all the types and means to provide sane error messages if this constraint is not fulfilled. My experience is that people who build such languages simply do not like implicit conversions from anything to bool. Hence, they won't have the operator you are looking for.</p>

CC BY-SA 4.0
2023-08-18T15:19:02.550Z
2023-08-18T15:19:02.550Z
feldentm
<p>VHDLs boolean operators are overloaded functions. Rather than just a single <code>&lt;boolish&gt; or &lt;boolish&gt;</code> there is a family of such functions, where the appropriate one is resolved based on the input arguments. This is perticuarly significant in VHDL, because there are at least 4 commonly used booleanish types: boolean, bit, std_logic and std_logic_vector. You can do boolean logic, only if both arguments are the same type, and the function that binds as a result will return that same type. The caller must explicitly convert to a common type if the input arguments are different.</p>

CC BY-SA 4.0
2023-08-18T13:55:44.133Z
2023-08-18T13:55:44.133Z
user1937198
<p>Such behavior appears semantically equivalent to what functional operations on the <code>Optional</code> type do in several languages. I'll refer to Rust in my example (except it's called <code>Option</code> there).</p>
<ul>
<li>There's an <code>or</code> operation, which takes two <code>Option</code>s and returns the first one if it has a value, or the second otherwise.</li>
<li>There's also an <code>and</code> operation, which returns the first one if it has no value, or the second otherwise.</li>
</ul>
<p>Note that those operations are only defined on <code>Option</code>s containing the same type, and it's the user's responsibility to explicitly cast the operands in advance. It doesn't have to be the case in an arbitrary language, though. As other answers have suggested, casting could also be done to the least upper bound type, but this approach at its core doesn't depend on implicit casts at all.</p>
<p>A statically typed language could allow boolean <code>&amp;&amp;</code> and <code>||</code> operators to be overloadable for types other than booleans, as long as they implement boolean coercion (like Rust's <code>Option</code> intuitively does, with <code>Some(...)</code> being <code>True</code> and <code>None</code> -- <code>False</code>). (Rust <strong>doesn't</strong> do it, though. <em>Although it could have benefitted from its lazy evaluation, letting it ditch <code>or_then</code> and <code>and_then</code></em>). This way, to achieve your desired behavior, the user would have to wrap arguments into <code>Option</code>s to explicitly signify their intent. The resulting semantics would be exactly as you'd want:</p>
<ul>
<li><code>Some(3) || Some(4)</code> would be <code>Some(3)</code>.</li>
<li>The result, coerced to a boolean, is exactly the same as if the arguments had been coerced to boolean in advance.</li>
<li>If you were to allow implicit lifting to superclasses <sub>(not talking about Rust at all anymore, assume some Rust-Java hybrid)</sub>, <code>Some(new Parent()) || Some(new Child())</code> would return <code>Option&lt;Parent&gt;</code>, since the second argument would have been implicitly cast to the base class due to <code>Option</code>'s covariance.</li>
</ul>

CC BY-SA 4.0
2023-08-18T09:24:37.797Z
2023-08-18T09:24:37.797Z
abel1502
<p>The logical operators <code>&amp;&amp;</code> and <code>||</code> are statically typed in Typescript, where the result is a <a href="https://en.wikipedia.org/wiki/Union_type" rel="nofollow noreferrer">union type</a>.</p>
<p>Many languages don't have union types, though. In general, the result will be a &quot;least upper bound&quot; (also called the <a href="https://en.wikipedia.org/wiki/Join_and_meet" rel="nofollow noreferrer">join</a>) of the two types. What exactly a &quot;least upper bound&quot; is will depend on what the language's type system supports, but in general terms, an upper bound is any common supertype, and &quot;least&quot; means it's a subtype of all other upper bounds.</p>
<p>For example, in Java there are no union types, but the type system does support intersection types*. The least upper bound of two classes might be the most specific common superclass, or it might be a common interface, or it might be an intersection of multiple such supertypes (the precise spec is <a href="https://docs.oracle.com/javase/specs/jls/se19/html/jls-4.html#jls-4.10.4" rel="nofollow noreferrer">here</a>).</p>
<p>On the other hand, in Typescript the least upper bound of <code>S</code> and <code>T</code> is always equal to <code>S | T</code>, because the type system includes union types.</p>
<p>As André L F S Bacci notes, the ternary operator <code>a ? b : c</code> has the same issue, also requiring a least upper bound of the types of <code>b</code> and <code>c</code>. Likewise, a list literal like <code>[a, b, c, d, e]</code> will typically have a type like <code>T[]</code> or <code>List&lt;T&gt;</code>, where <code>T</code> is the least upper bound of the types of those expressions which make up the list. So this is a problem your compiler probably already has to solve.</p>
<p><sup>*Intersection types in Java are not syntactically allowed in most contexts, but <a href="https://docs.oracle.com/javase/specs/jls/se19/html/jls-4.html#jls-4.9" rel="nofollow noreferrer">they are types</a> nonetheless.</sup></p>
<hr />
<p>Extra TypeScript note: it's worth pointing out that the type of <code>a &amp;&amp; b</code> is not strictly <code>S | T</code>, but more like <code>S &amp; Falsy | T</code>, and likewise the type of <code>a || b</code> is <code>Exclude&lt;S, Falsy&gt; | T</code>, where <code>Falsy</code> is the type of falsy values (i.e. <code>0 | '' | false | null | undefined | ...</code>). If your language's type system is expressive enough to do this, it's a nice extra feature.</p>

CC BY-SA 4.0
2023-08-17T23:42:36.277Z
2024-10-05T10:49:29.293Z
2024-10-05T10:49:29.293Z
kaya3
CC BY-SA 4.0
2023-08-18T15:20:21.303Z
The relevant part is that the union type is actually representable. This is usually only the case for scripting languages because they box and runtime type everything.
feldentm
CC BY-SA 4.0
2023-08-18T15:42:55.063Z
@feldentm That's a fair point. But Java could have union types too, for reference types at least, without changing the value representation ─ it just wouldn't be that useful without some kind of control-flow narrowing, e.g. `if(x instanceof Foo) { ... } else { ... }` where `x` is known to be `Bar` in the `else` branch by elimination.
kaya3
<blockquote>
<p>That's a logical operator that returns its operand. I don't know if there is a better term.</p>
</blockquote>
<p>Python's <code>or</code> return the first true <em>object</em>. if any, or returns the last object. It is a true coalescing operator, of sorts.</p>
<p>Note that <code>True</code> and <code>False</code> are also objects in Python, so there is not really anything special <code>or</code> being used in boolean expressions or arbitrary objects.</p>
<blockquote>
<p>But in statically typed languages, what could the operators with operands in different types reasonably return?</p>
</blockquote>
<p>A common implemented object in operands ancestry, or a common interface, and at least the top type (<code>Object</code>, <code>any</code>...).</p>
<p>In case of multiple inheritance or multiple interfaces, explicit casting will be necessary to select a common type.</p>
<p>Note that is the exact same problem of ternary operators in the form: <code>a = b ? c : d</code>. <code>a</code> will have a &quot;common&quot; type or interface inferred from <code>c</code> and <code>d</code>.</p>
<p>A true coalescing operator is essentially a specialized form of the ternary operator in the form <code>( a ? a : b )</code>.</p>

CC BY-SA 4.0
2023-08-17T20:24:05.457Z
2023-08-17T20:30:24.823Z
2023-08-17T20:30:24.823Z
André L F S Bacci
# How to make logical operators that return operands consistent in a statically typed language?
Posted by: username_0 on 2023-08-17
In C++, `3 || 4` returns true. But in Python, `3 or 4` returns 3 instead. That's a logical operator that returns its operand. I don't know if there is a better term.

In dynamically typed languages, doing this is simple. But in statically typed languages, what could the operators with operands in different types reasonably return? And are there statically typed languages already doing this?

I think it has to satisfy at least these two criteria:

* Changing one of the operand to a subclass that doesn't change relevant things should not make the result significantly different. (Better if it could potentially support indirect implicit casting, but I guess that might be too difficult.)
* If the expression is used as a boolean value, it matches the result of the logical operator on the two operands cast to boolean values beforehand, at least if both always gives a consistent boolean value.
**Tags:** static-typing type-inference booleans logical-operators
> **Comments:**
> **username_1**: Python's `x or y` is a bit like what `is_truthy(x) ? x : y` would be in C(++), so that's one place to perhaps look at. e.g. https://en.cppreference.com/w/cpp/language/operator_other  
> **username_2**: Isn't there a trivial solution of requiring that both operands be the same type?  
> **username_3**: I think it depends on how sophisticated your type system is. https://arxiv.org/pdf/2111.03354 discusses a language in which the type of the negation operator isn't just `bool -> bool`, but `(true -> false) & (false -> true)`, where `true` and `false` represent singleton subtypes of `bool` containing only the corresponding value, and `&` computes intersection types.  Using some obvious(?) hypothetical constructs, Python's `or` could be typed as `(Truthy[T] -> U -> T) & (Falsey[T] -> U -> U)`.  
> **username_0**: That's not only a type system, but also almost a precise description of the function itself. The extreme in this direction is to say the type of a variable is the set of all values that are not proven to be impossible to contain. Not very helpful to actually design a type system, but a good place to start thinking.  
> **username_4**: 3 or 4 is 3 under bit logic...  
> **username_0**: @RoddyMacPhee In bitwise operation `3 or 4` is 7.  
---
## 5 answers
### Answer by username_8 on 2023-08-17. Score: 13 | Accepted answer
The logical operators `&&` and `||` are statically typed in Typescript, where the result is a [union type](https://en.wikipedia.org/wiki/Union_type).

Many languages don't have union types, though. In general, the result will be a "least upper bound" (also called the [join](https://en.wikipedia.org/wiki/Join_and_meet)) of the two types. What exactly a "least upper bound" is will depend on what the language's type system supports, but in general terms, an upper bound is any common supertype, and "least" means it's a subtype of all other upper bounds.

For example, in Java there are no union types, but the type system does support intersection types\*. The least upper bound of two classes might be the most specific common superclass, or it might be a common interface, or it might be an intersection of multiple such supertypes (the precise spec is [here](https://docs.oracle.com/javase/specs/jls/se19/html/jls-4.html#jls-4.10.4)).

On the other hand, in Typescript the least upper bound of `S` and `T` is always equal to `S | T`, because the type system includes union types.

As André L F S Bacci notes, the ternary operator `a ? b : c` has the same issue, also requiring a least upper bound of the types of `b` and `c`. Likewise, a list literal like `[a, b, c, d, e]` will typically have a type like `T[]` or `List`, where `T` is the least upper bound of the types of those expressions which make up the list. So this is a problem your compiler probably already has to solve.

\*Intersection types in Java are not syntactically allowed in most contexts, but [they are types](https://docs.oracle.com/javase/specs/jls/se19/html/jls-4.html#jls-4.9) nonetheless.

---

Extra TypeScript note: it's worth pointing out that the type of `a && b` is not strictly `S | T`, but more like `S & Falsy | T`, and likewise the type of `a || b` is `Exclude | T`, where `Falsy` is the type of falsy values (i.e. `0 | '' | false | null | undefined | ...`). If your language's type system is expressive enough to do this, it's a nice extra feature.
> **Comments:**
> **username_5** (2023-08-18): The relevant part is that the union type is actually representable. This is usually only the case for scripting languages because they box and runtime type everything.  
> **username_8** (2023-08-18): @feldentm That's a fair point. But Java could have union types too, for reference types at least, without changing the value representation ─ it just wouldn't be that useful without some kind of control-flow narrowing, e.g. `if(x instanceof Foo) { ... } else { ... }` where `x` is known to be `Bar` in the `else` branch by elimination.  
---
### Answer by username_9 on 2023-08-17. Score: 5
> That's a logical operator that returns its operand. I don't know if there is a better term.

Python's `or` return the first true *object*. if any, or returns the last object. It is a true coalescing operator, of sorts.

Note that `True` and `False` are also objects in Python, so there is not really anything special `or` being used in boolean expressions or arbitrary objects.

> But in statically typed languages, what could the operators with operands in different types reasonably return?

A common implemented object in operands ancestry, or a common interface, and at least the top type (`Object`, `any`...).

In case of multiple inheritance or multiple interfaces, explicit casting will be necessary to select a common type.

Note that is the exact same problem of ternary operators in the form: `a = b ? c : d`. `a` will have a "common" type or interface inferred from `c` and `d`.

A true coalescing operator is essentially a specialized form of the ternary operator in the form `( a ? a : b )`.
---
### Answer by username_6 on 2023-08-18. Score: 3
VHDLs boolean operators are overloaded functions. Rather than just a single  `or`  there is a family of such functions, where the appropriate one is resolved based on the input arguments. This is perticuarly significant in VHDL, because there are at least 4 commonly used booleanish types: boolean, bit, std\_logic and std\_logic\_vector. You can do boolean logic, only if both arguments are the same type, and the function that binds as a result will return that same type. The caller must explicitly convert to a common type if the input arguments are different.
---
### Answer by username_7 on 2023-08-18. Score: 3
Such behavior appears semantically equivalent to what functional operations on the `Optional` type do in several languages. I'll refer to Rust in my example (except it's called `Option` there).

* There's an `or` operation, which takes two `Option`s and returns the first one if it has a value, or the second otherwise.
* There's also an `and` operation, which returns the first one if it has no value, or the second otherwise.

Note that those operations are only defined on `Option`s containing the same type, and it's the user's responsibility to explicitly cast the operands in advance. It doesn't have to be the case in an arbitrary language, though. As other answers have suggested, casting could also be done to the least upper bound type, but this approach at its core doesn't depend on implicit casts at all.

A statically typed language could allow boolean `&&` and `||` operators to be overloadable for types other than booleans, as long as they implement boolean coercion (like Rust's `Option` intuitively does, with `Some(...)` being `True` and `None` -- `False`). (Rust **doesn't** do it, though. *Although it could have benefitted from its lazy evaluation, letting it ditch `or_then` and `and_then`*). This way, to achieve your desired behavior, the user would have to wrap arguments into `Option`s to explicitly signify their intent. The resulting semantics would be exactly as you'd want:

* `Some(3) || Some(4)` would be `Some(3)`.
* The result, coerced to a boolean, is exactly the same as if the arguments had been coerced to boolean in advance.
* If you were to allow implicit lifting to superclasses (not talking about Rust at all anymore, assume some Rust-Java hybrid), `Some(new Parent()) || Some(new Child())` would return `Option`, since the second argument would have been implicitly cast to the base class due to `Option`'s covariance.
---
### Answer by username_5 on 2023-08-18. Score: 1
TL;DR: yes and no
We need to take a step back to understand why the question is a bit strange and why programming languages do not behave like this. I'll still provide a Tyr implementation for Integer to sketch a solution.
First of all, there is no such thing as a logical operator. There is a truth type, e.g. bool that might have operators. These operator symbols might be reused by other types. Usually, there is || and |.
The first strange thing about the question is that || is usually lazy and implemented with an if then else. Most languages allowing to overload operators treat operators like functions, i.e. the right argument will always be evaluated preventing libraries to provide this behavior.
So, the remaining question is how can a statically typed language implement something like 3 or 4 in Python.
Step one, we do it just for Integer[_].
public def || (x : Block[Integer[Size]]) : Integer[Size]
     <: operator.precedence[30], operator.rightAssociative =
if this.toBool() this else x.eval()

I'll assume that there is a toBool function; it could be 0 == this or similar. It does not matter. What matters here is the Block type and eval() call. Because this is what causes evaluation to happen in the correct branch of the CFG.
With this pattern, you can define your operator for every type where you consider this behavior reasonable. My personal taste is that it is never a good idea. Especially if you provide an implicit conversion to bool.
If you are looking for the eager evaluation semantic, you could simply define it as
public def | (x : Integer[Size]) : Integer[Size]
     <: operator.precedence[50], operator.rightAssociative =
if this.toBool() this else x

Step two, extension to arbitrary types.
Here, we have several options that all come with downsides.
One option would be to add an implicit conversion from any type to bool. This would result in C++'s solution if there wouldn't be an overload that results in a value.
We could define the operator in any as any || any : any. This would require any to have a physical representation, which it usually does not have. Also, it would cause ambiguities with actual logical operations.
We could try to define an operator ∀T,U. T || U : union(T,U). This, again, would require that union(T,U) is representable, which it is not in most languages. E.g. 0 || "hello" would be either a pointer to string literal or a 32bit integer. To fix this, we'd need some sort of constraint requiring that T and U share the same physical representation. Even if there were a language that could express this relation, you'd still also need inference for all the types and means to provide sane error messages if this constraint is not fulfilled. My experience is that people who build such languages simply do not like implicit conversions from anything to bool. Hence, they won't have the operator you are looking for.
---
