56

In the UNIX V7 version of the C language (but not yet in the V6 version), there were the /\ (min) and the \/ (max) operators. In the source of the scanner part of the compiler,

case BSLASH:
    if (subseq('/', 0, 1))
        return(MAX);
    goto unkn;

case DIVIDE:
    if (subseq('\\', 0, 1))
        return(MIN);
...

However, attempting to use them reveals that the corresponding part in the code generator is missing. Trying to compile

foo(a, b) { return a \/ b; } 

results in

1: No code table for op: \/

Granted, using the otherwise "magic" backslash character for those operators was not a good choice, but why abandon things half-way? I don't remember any mention of them in the literature. What's the story behind them? (Ken Thompson was reported — on another mailing list where I linked to this question — as saying that the piece of code is news to him.)

In the Xinu7 source dated 1986, the MIN and MAX operators in the parser are still there, and the code generation table still lacks the implementation.

In the "PDP-11 3+2" source, dated 1983, they are also there.

Leo B.
  • 19,082
  • 5
  • 49
  • 141
  • I'm not sure if they were actually implemented. I can't find where code is actually generated for these operators in any of the source code files that make up the compiler you linked. –  Nov 01 '17 at 02:05
  • @RossRidge In c01.c, it seems that they are converted to something else. – Leo B. Nov 01 '17 at 02:11
  • That just causes causes MAX and MIN to be changed to MAXP and MINP if either operand is a pointer. –  Nov 01 '17 at 02:19
  • @RossRidge Then it would be hard to explain why the half-backed code has survived in the source for 7 years (1979-1986, see update). – Leo B. Nov 01 '17 at 02:22
  • No, not really. As you'll notice from reading the README, the Xinu project didn't make many changes to it. –  Nov 01 '17 at 02:35
  • @RossRidge Still, in the main UNIX source (SystemIII or SysV for PDP-11, early 1908s) that code is still there. If it is indeed non-functional, it is strange that nobody cared to clean up the code for more than 10 years. – Leo B. Nov 01 '17 at 02:59
  • 1
    No, not really. Anyways, let met know when you find where it actually generates code for these operators. –  Nov 01 '17 at 03:11
  • Is this ever mentioned in any specification? I'm really curious about this. – Omar and Lorraine Nov 01 '17 at 08:51
  • The compiler included with Xinu is, to my knowledge, not a UNIX compiler at all. It seems to be the SunCHIP compiler (Cornell university virtual processor emulation running on Unix, but the OS inside the VM is apparently not UNIX). So whether this compiler has any relationship with "mainstream Unix C" is doubtful. – tofro Nov 01 '17 at 11:09
  • I would also assume such operators would only be of much use on a platform that has directly related machine instructions. The PDP-11 doesn't. – tofro Nov 01 '17 at 11:31
  • 2
    These sources https://github.com/mortdeus/legacy-cc/blob/master/last1120c/c00.c claim to be "the very first..." and look very similar, but without a trace of "MIN" and "MAX" operators. To me, that looks like a hack that someone added for an architecture that would probably have benefitted from these operators. – tofro Nov 01 '17 at 11:38
  • 3
    It doesn't seem to be in K&R 2ed., does anyone have a copy of the first edition from 1978? – snips-n-snails Nov 01 '17 at 15:48
  • I had K&R 1st and 2nd eds. I don't recall it being in either, but I don't have them anymore to check... – Brian Knoblauch Nov 01 '17 at 16:14
  • 2
    @traal: Nope, not in K&R 1st ed (yes, I checked). – Greg Hewgill Nov 01 '17 at 20:30
  • 3
    @RossRidge Alas, for foo(a, b) { return a \/ b; } I get 1: No code table for op: \/; similarly for /. Looks like an unfinished project whose traces lingered for years in the source. – Leo B. Nov 01 '17 at 22:19
  • Re: the Ken Thompson addition and, therefore, the idea of asking the people that were actually there; Kernighan remains an employee of Google and can sometimes be spotted at the NYC office. Sadly I recently left but if anybody else here is a Googler then the usual internal means might allow a query to be put if appropriate respect is applied. I attended a presentation he gave while I was there and he seems like a very approachable fellow. – Tommy Nov 09 '17 at 16:32
  • min(a, b) { return a < b ? a : b; }, max(a, b) { return a > b ? a : b; } are simple enough that a dedicated operator is not needed, really. – forest Mar 24 '18 at 22:19
  • 1
    However, C* for Thinking machines had <? and >? for min and max operators, as well as their update forms <?= and >?=. very handy. – mac3n Nov 09 '17 at 15:06

2 Answers2

20

(speculation, but I'm pretty confident in it) Assuming \/ and /\ were actually used in early C to be max and min, someone probably quickly realized how totally stupid that symbology is. They look like the long-accepted boolean operators for OR () and AND (). Having something you've been taught since high school means AND now stands for min in C would have been an extremely bad idea. It is equivalent to a computer language designer deciding that, in their language, the plus symbol (+) should mean divide (÷).

user3840170
  • 23,072
  • 4
  • 91
  • 150
RichF
  • 9,006
  • 4
  • 29
  • 55
  • The notation was pretty crummy, but such operators could have been useful in the days when many compilers lacked common-sub-expression-elimination logic. On many platforms, the expression a max= b could be efficiently evaluated by computing b-a and conditionally adding that result to a based upon the flags. I think a bigger omission, though, is the fact that x || y is equivalent to x ? !!x : !!y [but evaluating x once] rather than `x ? x : y [likewise]. The coercion of the result to 0 or 1 requires a compiler to either generate extra code or waste time optimizing it out... – supercat Nov 01 '17 at 16:14
  • ...while having the expression yield x or y (whichever is non-zero, if any) would eliminate the need for such code. – supercat Nov 01 '17 at 16:15
  • 34
    Well, the OR and AND boolean operators are also the MAX and MIN operators for boolean values, so it does make a certain amount of sense. –  Nov 01 '17 at 17:05
  • As I've said in my question, regardless of the notation, what's the reason to eliminate the concept? After all, without function inlining, there is no efficient way to implement min and max for operands with side effects, as, I believe, many a junior programmer learned the hard way after using something like ++i as an argument to a min or max macro. – Leo B. Nov 01 '17 at 17:35
  • @LeoB. I apologize for focusing only on the notation. It just seemed so askew from reality. I have no idea why the concepts were removed. – RichF Nov 02 '17 at 09:49
  • 3
    @RossRidge okay, I'll accept that the numeric values for TRUE and FALSE are typically thought of as 1 and 0. And of course, 1 > 0, making 1 max, just as 1 ∨ 0 is true (1) . However, thinking of these boolean values as numbers is a mistake. I recognize that C does not have a boolean data bype, meaning that effectively, true could have a numeric value of 1 or -7 or any non-zero value. Here it becomes apparent that the concept of max as or is flawed. It becomes even messier with it being unclear whether the OR under consideration is a boolean or binary operation. – RichF Nov 02 '17 at 10:06
  • @RichF "However, thinking of these boolean values as numbers is a mistake". Indeed it is, but that is what C does And perhaps that gives us a clue as to why the operator was abandoned. MAX(-7,0) is 0 and so would be -7 \/ 0 had it been implemented, but, in K&R C -7 || 0 could be any integer value except 0. – JeremyP Nov 02 '17 at 10:46
  • 14
    C syntax was cluncky and non-standard. ^ To mean XOR and | to mean OR, as well as == to mean equality and = to mean assignation are non-standard, and would have remained so if it weren't for C language's major influence. – Bregalad Nov 03 '17 at 12:31
  • @Bregalad Clunky is fine, especially when your language is limited to 7-bit ASCII characters. The problem comes when the designer decides to use symbology long-accepted as meaning one mathematical or logical operator, and say, "In my language it don't mean that anymore -- it's this entirely different operator instead." Using, say, .x for max and .n for min would have avoided conflict with accepted meanings. – RichF Nov 03 '17 at 14:51
  • @Bregalad Non-standard according to what standard? At least using = to mean assignation is in FORTRAN. – Leo B. Nov 03 '17 at 21:14
  • There was possibly a problem with /\ being mistaken for ^ when the code was hand written. Also, at that time, some languages had min(a, b, c, ...) so it is possible that that idea was being debated. Either way, not removing dead code is a sin that many systems suffer from. I've worked on systems which have had redundant/dead/abandoned code for over 30 years. Sometimes it is left in because nobody knew it was there. – cup Nov 04 '17 at 05:11
  • 7
    @RichF That notation might not be extremely common, but it's fairly standard and well understood, eg in probability theory. It's a natural extension of OR and AND in fuzzy logic, too. Compare https://math.stackexchange.com/questions/32137/what-are-the-common-abbreviation-for-minimum-in-equations – Fab Nov 09 '17 at 13:23
  • 2
    A better example IMO is that some languages choose to use + for concatenation when it really means addition. – Kristopher Ives Nov 09 '17 at 13:30
  • 11
    \/ and /\ have the semantics of MAX and MIN regardless of how true and false are actually represented, because they form a boolean algebra, which, mathematically speaking, is just a very special lattice, where those MAX and MIN operators are defined as least upper bound and greatest lower bound. Integers also form a lattice, so it's OK to use those operators. – Sebastian Graf Nov 09 '17 at 14:36
  • Plus, on booleans they are the more semantically correct alternative to bitwise | and &, which can only be used because of the fact that false is represented as 0. – Sebastian Graf Nov 09 '17 at 14:36
  • I would have expected /\ to be MAX and / to be MIN. (Think of /\ as an arrow pointing up to the larger value, and / as an arrow pointing down to the smaller value.)` – Keith Thompson Nov 09 '17 at 16:57
  • 14
    @RichF As Sebastian mentioned, there is a fairly deep connection between boolean algebra and order comparisons of numbers. In both the ordering of booleans and the ordering of integers, those operations being discussed correspond to the join operation and the meet operation, which are represented with exactly those two symbols (in ASCII form, here). In fact, I strongly suspect this is where those symbols came from UNIX V7 C. There are other important connections between true and false and integer math too (in particular, integer arithmetic mod 2). – David Nov 19 '17 at 06:45
  • @KristopherIves Actually, + is a common notation for the binary operation of a semigroup (because it's the same operation as a ring's additive operation +· is used for the ring multiplicative binary operation), and both strings under concatenation and integers under addition are semigroups (albeit the latter is a commutative semigroup). – cjs Sep 11 '19 at 09:28
9

Assuming these operators were part of "original C" (I don't think so, see some comments):

The main reason why these weren't followed up would probably be: C being what it is - a close-to-the-machine language - an operator in C would need to be aligned with at least some closely related machine instruction - Most of the C operators are.

I am not aware of any wide-spread architecture that had a machine instruction that produced the bigger (or smaller) of two values. Without that machine instruction, a compiler would have had to revert to compiler intrinsics, something that wasn't known as a concept back then (for good reasons).

I have the strong feeling that these operators were some sort of hack that was introduced (or partially introduced) to port C to some architecture that actually had such instructions, and then forgotten in the code. This text states, which might be a hint:

Also during this period [Unix kernel programmed in C for the first time], the compiler was retargeted to other nearby machines, particularly the Honeywell 635 and IBM 360/370;

tofro
  • 34,832
  • 4
  • 89
  • 170
  • 3
    Most of the C operators are but far from all. Conversion of the Boolean result of a comparison into an integer is a "compiler intrinsic". If you could write foo(a, b) { return a > b; } and that would compile into a sequence of instructions moving 0 or 1 into the return register, the incremental step to doing the same with one compared value or the other isn't that big. Wasn't Unix V7 originally PDP-11 only? – Leo B. Nov 01 '17 at 21:55
  • 1
    Neither Honeywell 635 nor IBM/360 had min/max instructions. I think it was a (side) project that was abandoned at a late stage (one missing part is templates in the code generation table), likely for size reasons. It is funny for how long that abandoned code lingered in the source. – Leo B. Nov 01 '17 at 22:40
  • Maybe one of the "non-particular" architectures had such an instruction? I understand this as a non-exhaustive list. – tofro Nov 02 '17 at 10:36
  • 4
    It's a myth that C operators had to be closely aligned to the machine language. – JeremyP Nov 02 '17 at 10:37
  • @Wilson Both Z80 and PDP-11 have ASR and ASH, SLA and SRA instructions to shift a register. – tofro Nov 05 '17 at 08:39
  • 2
    @Wilson For Z-80 (that wasn't really driving C development), yes, for PDP-11, no. – tofro Nov 05 '17 at 09:38
  • 1
    The PDP-11 ASH (Arithmetic Shift High) instruction can shift left and right over multiple bits, according to http://pages.cpsc.ucalgary.ca/~dsb/PDP11/DoOpReg.html . It is an EIS instruction with 1.5 operands, meaning it operates on a register and a normal operand which supplies the shift count. – Rhialto supports Monica Nov 05 '17 at 19:55
  • @Wilson I didn't claim that anyhow. – tofro Nov 07 '17 at 16:13
  • 1
    Whether or not an architecture has a min or max instruction, it would on most platforms be simpler for a compiler to generate efficient machine code for a min or max operator than for it to generate machine code that was anywhere near as efficient given any other means of expressing the concept in C source code. On many platforms, subtract will set flags according to which source operand was greater, and code to perform a subtraction and then conditionally add the result to one of the operands would be much more efficient than code which can't exploit the flags from the subtract. – supercat Jul 06 '21 at 22:26