Virtually all (imperative) languages offer the operators <
<=
>=
>
for lower, lower or equal, greater or equal and greater. But why no ternary range operator like (10 < x < 100)
to test if x is between 10 and 100 or an inclusive one 10 <= x <= 100
. Would this make parsing prohibitively complicated? Or is there even a language that can do this?
3 Answers
Would this make parsing prohibitively complicated?
No, modern parsers are more than capable. But it does complicate the documentation, in order to explain the functionality to new users.
Or is there even a language that can do this?
Yes. Python, perl6, mathematica, and indeed all Turing-equivalent languages can do this. Not only the imperative ones you suggested, but pure functional ones, as well.
Python offers 10 <= f(x) < 100
expressions,
which match math notation and are very convenient.
In other languages you could sprinkle a bit of
syntactic sugar on top, some simple pre-processing,
to accomplish the same thing:
temp = f(x); 10 <= temp and temp < 100
.
(Of course a
hygienic
temp var name would be needed.)
In languages offering macros or functions,
pretty much all of them, you could ask
is_in_halfopen_interval(f(x), (10, 100))
,
perhaps with 3 args if tuples are not conveniently available.
(I am using the example of half-open intervals because of how nicely they compose, so they work well as API args.)
Given some scalars that should be strictly monotonic, python will happily verify that:
if a < b < c < d:
It comes down to custom and convention. If standard libraries provide and consume some "usual" way of expressing a concept, then apps within the ecosystem will follow that.
Let's ignore other kinds of numbers and
focus just on intervals over integers.
Python offers the
range
type to model this. (Assume for the moment step
is always 1
.)
So you can assign r = range(10, 100)
and then ask if int(f(x)) in r:
These ranges are a bit specialized since the emphasis
is on producing subscript indexes.
But lots of app functions are integer valued,
so it can be very natural to request
schedule(job_id, start_within=range(6, 9))
,
and the documentation burden is quite low
due to use of the familiar range
abstraction.
In contrast someone designing an API like this:
schedule(job_id, start_within=(6.5, 9))
would have to carefully describe the meaning of that generic tuple
and whether 9.0
is a valid starting time (closed interval?) or not.
In summary, popular languages can and do support range operators.
From the parser’s point of view it’s no big deal. It may not fit nicely into a grammar, but who says we can only parse languages with a nice grammar?
You can actually parse it quite easily and distinguish between a<b<c and a<(b<c). Then when you turn the parsed grammar into code, you have to be careful.
But first you need to decide what are actual valid chains. We start with any number of expression separated by ==, !=, <= etc. which is not compatible for example with C where different operators have different priorities. The trivial case of one operator only should have the same result.
What about evaluating the expression? Either each operand is evaluated exactly once, or until the result is known. You decide.
Can < and > be mixed? Can == and != be mixed? In a != b != c, do all operands have to be different, or only a != b and b != c?
In Swift I believe this could be implemented in the standard library with quite strong demands on the optimiser.
-
intersting point.As a thought experiment how about an "in" operator with two operands
x IN (l, h)
would test exclusive andx IN [l, h]
inclusive or evenx IN ]l, h[
for exclusive. This would remove the ambiguity of the normal comparison operators and would document great if a basic math class was visited .– theking2Mar 13 at 21:26 -
-
SQL has the Between operator: x BETWEEN y AND z
.
The main problem with triadic infix operators within expressions, is distinguishing them from a series of dyadic infix operators.
That is, given x < y < z
, it is ambiguous whether this is one operator (with three operands), or whether it is two operators (where the result of the first operation forms one of the operands for the second).
In the case of SQL, the ambiguity is resolved by there being no dyadic Between operator, as well as by other aspects of the language which restrict the context in which the dyadic logical-And can appear.
In C, there is no range comparison operator, but the conditional operator c ? t : f
avoids ambiguity by the fact that neither the question mark nor the colon represent any dyadic operator, nor do they form part of any other operator.
So when you want a triadic infix operator, you are caught between either designing a careful system of naming which avoids reusing keywords/symbols for different operators, or designing a variety of special cases into the language which erode the generality of how operators can be composed into expressions.
The two horns of that dilemma are easily avoided by simply expecting the programmer to compose the range test from dyadic operators (e.g. (x >= y) AND (x <= z)
), or by using the function-call notation (e.g. IsBetween(x, y, z)
), and many language designers wouldn't have felt that more was needed.
-
The SQL operator BETWEEN is a bit of a pickle as it quite specific of testing between the two extrema not inclusive.
InRange(x, l, u)
with an optional boolean or an array of exactly two boolean stating enclusion of the extrema would indeed solve this.– theking2Mar 13 at 21:33 -
@theking2, actually it is inclusive in SQL. I've settled into being one of those people who prefers my ranges to be fully inclusive, and my indexes to start at 1!– SteveMar 14 at 3:45
-
-
@theking2, indeed the behaviour with datetimes, when a range is specified by date only, is truly absurd.– SteveMar 14 at 21:21
(10 < x and x < 100)
to perhaps make it more readable. But addand
and more so duplicating the operand might be problematic.x < y < z
has the same semantics asx < y and y < z
except thaty
is only evaluated once.x in range(a,b)
. And for most applications, the overhead of the extra function calls to facilitate this is not significant.