12

The if keyword is so prevalent in programming that it seems to just be part of it. However, with an integer value and a goto statement, one can simulate the functionality of an if.

Which means it's not necessary for a language to have dedicated conditional keywords in order to achieve that functionality. Which in turn makes me think that there must have been a time before somebody created the first if to be used in a language.

So, my question is: What was the first time a language offered a dedicated keyword for conditional execution?

The word itself doesn't really matter here, it could be AS_IT_OCCURS_THAT instead of if for all I care.

user3840170
  • 23,072
  • 4
  • 91
  • 150
R. Schmitz
  • 393
  • 2
  • 8
  • 5
    "However, with an integer value and a goto statement"? I'm not seeing the conditional here. Are you assuming a "Jump if Zero" (or equivalent) machine instruction for the goto? –  Apr 18 '19 at 12:18
  • 1
    @Caleth No, I mean jumping to different line numbers. – R. Schmitz Apr 18 '19 at 12:21
  • 1
    @HighPerformanceMark I don't know LISP history, so this might be a later addition, but it seems to have a literal if - so yeah, that absolutely counts for this question! (But it's not the correct answer because it's already older than FORTRAN.) – R. Schmitz Apr 18 '19 at 12:27
  • 4
    The original FORTRAN had IF (value) 10, 20, 30 which would branch to statement with label 10 if value was negative, 20 if zero, and 30 if positive. –  Apr 18 '19 at 13:41
  • @BobDalgleish While that is interesting in the context here, it doesn't fit the structured programming description "one or a number of statements is executed depending on the state". Arguably. Thank you for pointing that out to me, I will adjust the question. – R. Schmitz Apr 18 '19 at 14:49
  • Are you deliberately excluding languages where what follows the if has to be a goto statement? – JeremyP Apr 18 '19 at 15:16
  • 1
    You should probably look into the machine language of the first machines to see how they did conditional branching. – Thorbjørn Ravn Andersen Apr 18 '19 at 16:09
  • @Caleth To do it without a "jump if zero", you could do something like: "jump ahead n lines", where n is zero or one; the next line is "jump to the else clause"; and the line after that begins the then clause. Of course you'd have to ensure that your true value is exactly one, not any other non-zero value. (I have no idea if any old architecture actually did this, vs. having some kind of conditional branch.) – dgould Apr 18 '19 at 19:02
  • 5
    "Computed goto" is actually a later development than conditional branching. – Mark Apr 18 '19 at 20:50
  • 2
    Your premise "with an integer value and a goto statement, one can simulate the functionality of an IF" is unclear, do you mean a GOTO statement whose destination is computed, rather than a constant label: IF <cond> GOTO $n ? Because most languages, like BASIC and the Fortran@BobDalgleish mentioned uses constant GOTO destinations, so IF (value) [GOTO] 10, [ELSE IF value==0 GOTO] 20, [ELSE GOTO] 30. The answer to your question depends on how exactly you define your question. Computed gotos are later and are frowned upon. for one thing they make debugging (/static analysis) a pain. – smci Apr 19 '19 at 12:59
  • 2
    Integers and gotos -- the target of a goto is typically not an integer in the same sense as the integers used in arithmetic. GOTO N probably compiles as an instruction containing the address of line N, not the value N. You generally cannot write N = 50; GOTO 2 * N and expect execution to continue at line 100. – dave Apr 23 '19 at 19:06
  • 1
    As can be seen by the answers, which are all over the map, the question is unclear. Not clear what counts as in or out. – Drew Jun 04 '19 at 22:21
  • 1
    I don't consider "line numbers" to be integers in the sense that you can do computation with them (GOTO 2+2, anyone?). They are "labels" whose denotation happens to coincide with integers, but they're a different type, generally distinguishable by context (notwithstanding that little issue in Algol60). Though maybe APL is an exception, since the target of -> could be calculated. – dave Jun 14 '21 at 23:33
  • 1
    @another-dave - also SNOBOL (target of branch (absolute or conditional) was a label which could be calculated. (Nothing wrong with your comment at all, just wanted to enjoy the opportunity to mention SNOBOL.) – davidbak Jan 06 '22 at 22:55
  • Indeed. As it happens, I was reasonably familiar with the insides of Dewar and McCann's MACRO SPITBOL implementation. – dave Jan 06 '22 at 22:59
  • @another-dave - I had the extreme and enlightening pleasure of working with RBKD at Alsys. He gave me a copy of PC-SPITBOL and access to the sources. I had a lot of fun with that. And, even at the time, I marveled at all the intricate and very cool coding tricks that were embedded in there ... that interpreter was top-notch. I would have loved to see what'd he'd do if he had wanted a non-portable JITTer ... – davidbak Jan 07 '22 at 18:21
  • @davidbak - McCann was my prof when I was an undergrad. He persuaded me to attempt to write MACRO APL based on MACRO SPITBOL. Alas, never finished. – dave Jan 07 '22 at 23:42

4 Answers4

21

The Analytical Engine designed in 1837 was capable of conditional branches

From the wiki (emphasis mine):

The programming language to be employed by users was akin to modern day assembly languages. Loops and conditional branching were possible, and so the language as conceived would have been Turing-complete as later defined by Alan Turing.

The wiki later provides an example of the conditional being executed (with the code represented on cards):

For example, a factorial program would be written as:

N0 6 
N1 1 
N2 1 
× 
L1 
L0 
S1
- 
L0 
L2 
S0 
L2 
L0 
CB?11 

where the CB is the conditional branch instruction or "combination card" used to make the control flow jump, in this case backwards by 11 cards.

Ada Lovelace may very well be the first to have written a program for this machine.

Ada Lovelace's notes were labelled alphabetically from A to G. In note G, she describes an algorithm for the Analytical Engine to compute Bernoulli numbers. It is considered to be the first published algorithm ever specifically tailored for implementation on a computer, and Ada Lovelace has often been cited as the first computer programmer for this reason.

bitsoflogic
  • 326
  • 1
  • 3
  • 6
    It should be noted however that the Analytical Engine was never actually built, so only exists as theory. – Darrel Hoffman Apr 18 '19 at 17:17
  • 1
    It was made functional in an emulator though: https://retrocomputing.stackexchange.com/a/6287/11796. – bitsoflogic Apr 19 '19 at 13:49
  • 1
    Either way, I figured the language was the most important thing to note for this conversation. – bitsoflogic Apr 19 '19 at 13:50
  • 2
    And just to confirm: Ada Lovelace's code (large image) does indeed contain a conditional. She also makes a comment about that in her notes, writing “if we were calculating for n = 1 instead of n = 4, Operation 6 would have completed the computation of B1 itself, in which case the engine instead of continuing its processes, would have to put B1 on V21[...]” – leo Apr 19 '19 at 18:02
11

Computers since ENIAC have had conditional branch instructions (like jump if zero), which means assembly languages have had such a statement since the beginning, and there is no logical reason for higher-level languages to ever have forgone using it.

While it's probably theoretically possible to make programs with just your indirect jump idea, you sort of have a chicken and egg problem where it's difficult to get the branch address into the register conditionally in the first place. Implementing a conditional branch using only indirect jumps would add several instructions every time you needed to make a choice, using precious time, memory, and registers,

Karl Bielefeldt
  • 371
  • 1
  • 3
7

The answer would, by definition, be the first programming language.

A little bit of free CS101 here...

All algorithms can be expressed using 3 elements: sequence, selection, and iteration. Those are the basic building-blocks of a computer program. In order to express an algorithm with a programming language, it has to support those 3 elements in some form. An "if" check is of course a kind of selection.

Sequence, Selection, and Iteration are the basic elements that we use to tell the computer what to do. The code will definitely look different depending on the programming language we use, but the algorithm will be the same.

So let’s describe these elements:

  • Sequence– the order we want the computer to execute the instructions we provide as programmers. For example, do this first, then do this, then do that, and so forth.
  • Selection– selecting which path of an algorithm to execute depending on some criteria. For example, if you passed a class in school, then we execute the operations that clap and cheer and play a song. But if you didn’t pass the class, then maybe we would say, “Better luck next time, hang in there!”
  • Iteration– looping or repeating. Many times, we want to be able to repeat a set of operations a specific number of times or until some condition occurs.

To get down deeper into computability theory, we call the ability of a model to express any algorithm Turing completeness, and selection is required for this. Most CS types will tell you than anything that isn't Turing complete isn't really a programming language.

So by definition every programming language has some means of selection, and always has. Without that, you don't really have a programming language.

T.E.D.
  • 1,479
  • 9
  • 11
  • 1
    I agree with your main these, but have a program with the 'sequence, selection, and iteration' point: declarative languages don't have sequence and yet are certainly programming language. – Quelklef Apr 18 '19 at 17:39
  • 1
    "by definition every programming language has some means of selection" - but the question explicitly isn't asking for the first programming language with some means of selection; it's asking for the first language with "a dedicated keyword for conditional execution". So for example, the untyped lambda calculus is certainly a programming language, but it certainly does not have a dedicated keyword for conditional execution. – Tanner Swett Apr 18 '19 at 18:04
  • @Quelklef - That's a bit of a gray area. "Pure" declarative languages are typically not Turing complete, and the ones that are Turing complete I believe all allow some sense of sequence. (Arguably, the sequencing and iteration are still there, but buried in their interpreter). Does that mean pure declarative languages aren't proper "programming languages"? They are languages (often called DSLs), but "programming languages"? – T.E.D. Apr 18 '19 at 18:07
  • @T.E.D. Is Haskell (sans IO, for simplicity, though I would argue that it still is both even with IO) not turing complete and missing sequencing? – Quelklef Apr 18 '19 at 18:47
  • @Quelklef - Don't know much about Haskell, but as near as I can tell it does support sequenceing, and even has a "sequence" keyword. – T.E.D. Apr 18 '19 at 19:11
  • @T.E.D. AFAIK, the closest you can get to sequencing with Haskell is that evaluating f(g(x)) will (probably) evaluate g(x) and then f(g(x)), because the latter relies on the former. Even here, though, the compiler isn't mandated to execute any particular code in any specific order. It will likely run the code generated for g with input x and then pass that to f, but that's strictly an implementation detail. This kind of 'sequencing' is also how >>= works, which only emulates sequencing. – Quelklef Apr 18 '19 at 19:29
  • 2
    @T.E.D. sequence is just a metaphor. All of the "sequencing" that arises from monads is a metaphor; it's not central to the language. The monadic sequence metaphor is, however, usually built on the real notion of sequence. The real source of sequencing is that, if you want to evaluate case scrut of { D _vars -> _arm; _ -> _etc } (with data-con D), you must first evaluate scrut, match against D _vars, then evaluate _arm. The root of the sequence/evaluation/"strictness" is the runtime system, an entity "external" to Haskell that methodically tears down the data structure main. – HTNW Apr 19 '19 at 00:33
  • 1
    @Quelklef Actually, when Haskell evaluates f (g x) it will almost always evaluate f first, not g. This is the essence of call by need evaluation order, aka "laziness". (There are probably some strictness annotations you can put on f to make it evaluate g first, but it's not the default.) As HTNW says, the real sequencing operation in a lazy language is case (specifically, scrut must be evaluated to weak head normal form before _arm can begin evaluation). – Mario Carneiro Apr 19 '19 at 18:49
  • @MarioCarneiro Ah, I totally forgot about lazy evaluation, thank you. In regards to case, is it truly sequencing? The compiler is free to do evaluation in whatever order it would like, as long as the result conforms to the spec, right? (Though I suppose that's also true for imperative languages, so the question of "what does sequencing mean, exactly?" gets sticky) – Quelklef Apr 20 '19 at 17:14
  • 1
    @Quelklef If you consider the core Haskell language, after any optimizations, then case is effectively a sequence point. It introduces a data dependency that forces the relative evaluation order of the two expressions. There are other expressions that could be done before, after, or in parallel with these evaluations, but you know that the case scrutinee will always be evaluated before the arm. Something like f(g(x)) has a similar effect in (strict) imperative languages - you can't call f until you know what value to call it with, so g has to evaluate first. – Mario Carneiro Apr 20 '19 at 19:14
6

I'm going to go out on a limb here and assume that what is wanted is not a language that has conditional branches i.e. an equivalent to

if condition then goto someLabel

because Turing complete computers have had that since the beginning. I'm going to assume we are talking about a block structured conditional like

if condition
    some arbitrary sequence of statements including perhaps nested ifs
else 
    some other arbitrary sequence of statements including perhaps nested ifs

Some early contenders for that would be:

  • Lisp (1958) which has an if and cond function. I think condpredates if. When I learned Lisp in the 1980's I'm fairly sure that if wasn't there.
  • Algol 60 (1960) which has the structured if inherited by most modern imperative languages

Early versions of FORTRAN and COBOL did not have structured if statements as far as I know.

JeremyP
  • 11,631
  • 1
  • 37
  • 53
  • I want to say yes to your going-out-on-a-limb, but that changes the question and kinda invalidates the other answers. I don't want to do that to the others, even though what you wrote is what I really wanted to know. So I asked a new question and would be happy if you just copy-pasted your answer there. – R. Schmitz Apr 18 '19 at 15:48
  • 1
    Early LISP had conditional support but not IF. Conditionals in M-expressions were handled via syntax. Conditionals in S-expressions were handled via the COND symbol/keyword. – Kelvin Sherlock Apr 18 '19 at 22:53
  • 1
    The lack of if in LISP extends to at least the mid-1960s. Neither LISP 1.5 (on the IBM 7090) nor L. Peter Deutsch's version for the PDP-1 ever had it, as far as I can tell. Nor does it appear to have been in MACLISP (p. 1-19) even as late as 1975. However, this is mere surface syntax; (if (p) (f) (g)) is of course identical to (cond (p (f)) (T (g))); I guess nobody saw much point in introducng a less flexible version of what you could already do almost as easily with cond. – cjs Sep 18 '19 at 12:10