A U/W FOCAL V4E Implementation of Project Euler Problem #1
FOCAL as a Case Study in Programming Language Tradeoffs
The FOCAL programming language is almost unknown these days. It was created for the PDP-8 and was reimplemented for later PDP computers as well as a few non-PDP computers, but the language didn't really survive the PDP era. We cannot speak of "modern FOCAL" in the way we can for the subjects of most of the other articles in this series: BASIC, C, and FORTRAN. All of those languages are still very much alive today, and hence much evolved relative to the PDP-8 version, whereas FOCAL didn't evolve beyond the 1970s.
The PiDP-8/I software project currently ships U/W FOCAL V4E as its primary FOCAL implementation, primarily for the reasons given in our U/W FOCAL manual supplement. When we say "FOCAL" in the remainder of this document, we mean U/W FOCAL specifically when it differs from other FOCAL implementations, such as DEC's original 1969 version FOCAL.
As a language, FOCAL roughly splits the difference between FORTRAN and BASIC, its two primary competitors.
FOCAL vs BASIC
Like the canonical implementations of contemporary BASIC, FOCAL...
- is interpreted
- is implemented as a REPL
- uses line numbers for stored programs
- is heavily dependent on GOTO for program flow control
- papers over many hardware issues, presenting all machines it ran on as equivalent
Unlike contemporary BASIC, FOCAL lacks strong string support. FOCAL is primarily a numeric programming language, aimed at scientists and engineers. FOCAL lacks a string data type, so dealing with text input is awkward, but on the positive side, it usually had a broader set of built-in mathematical functions than contemporary BASIC implementations, and its numeric formatting capabilities are nearly as good as those of C's
printf(), which set the standard by which numeric formatting was judged for decades.
Unlike BASIC's simple integer line numbers, FOCAL's line numbers are a 2-level decimal line numbering scheme, turning a common praxis BASIC programmers engaged in for juggling line numbers into a codified part of the language. Whereas a BASIC programmer of the early 1970s might mentally reserve line numbers divisible by 1000 for subroutine entry points, FOCAL uses whole numbers for subroutine entry points and decimal fractions for the line numbers within each subroutine. When you
WRITE a FOCAL program to the terminal, groups are separated by a blank line, making for more readable programs than the wall-of-text you normally get when you
LIST a BASIC program.
A programming language implementor who chooses to provide syntax and tool support for a recommended option vs unlimited flexibility chooses not to be attainted by The Lisp Curse.
Line numbering style is not just a superficial difference relative to BASIC. FOCAL's GOTO syntax, for example, differs depending on whether you are jumping within the current group of lines or out a different group:
GOTO .2 means "jump to line XX.20 in the current group of lines," while
GOTO 12.34 means "jump to line .34 within group 12." Note also that the trailing zero in the first example could be dropped; FOCAL truly does use a decimal line numbering system, such that
GOTO .20 means exactly the same thing. Not only that, you could do computed GOTO and subroutine calls, where the result of an arithmetic expression determined which line number to jump to!
FOCAL vs FORTRAN
In many cases where FOCAL and BASIC differ, it is because FOCAL's creators chose to make it more like FORTRAN:
- strong numeric formatting capabilities
- more suited to construction of larger programs
- greater focus on numerics than string and character operations
The main ways OS/8 FORTRAN differs from FOCAL are in the ways that give it additional power over FOCAL, at a cost of complexity:
- compiled vs interpreted
- longer identifier names, important for larger programs
- multi-module programs: static linkage plus dynamic overlays vs.
You should use FORTRAN if you're making something that needs to be large, fast, or both. You use FOCAL when ease of development and modification are more important considerations.
Here is the code I came up with to solve Project Euler Problem #1:
01.10 SET TOTAL = 0 01.20 FOR I = 3, 999 ; DO -.3 01.30 IF (FRAC(I / 3)) , 1.5 01.40 IF (FRAC(I / 5)) , 1.5, 1.6 01.50 SET TOTAL = TOTAL + I 01.60 NEXT 01.70 TYPE "TOTAL: ", %6, TOTAL, !
(See the companion article Getting Text In for instructions on getting that program text into FOCAL. You cannot simply paste it in via SSH, due to a limitation of U/W FOCAL's terminal I/O handling.)
Almost every line number in that program contains a surprise to those not versed in FOCAL, so let's take each one in turn.
01.10 SET TOTAL = 0
Like BASIC and languages that followed in its spiritual footsteps, such as Python, FOCAL doesn't require that you declare your variables before using them. The first time you run FOCAL and use the
TOTAL variable, its value will indeed be zero, that being FOCAL's standard interpretation of "undefined value." However, because the values for variables are not reset between program runs, if you leave this line out, running the same program a second time will double the result printed at the end of the program!
01.20 FOR I = 3, 999 ; DO -.3
As with the other programs in this series of articles, this one is based on a
for loop, but due to a difference in the way FOCAL implements this loop, we give the end point as
I = 999 rather than
i < 1000, as in the C version.
Also of note is that in FOCAL, a multi-line
FOR loop is implemented in terms of
GOTO. This says "for each iteration of the loop, start with line .3 in the current group`, meaning line 1.30 in this case. Unlike with BASIC, it is not implicit that we start with the next line of the program! You could start the loop in another group entirely if you wanted. I do not view this as a good feature, but it is what we have to work with.
01.30 IF (FRAC(I / 3)) , 1.5
FOCAL uses a 3-way
IF expression much like early versions of FORTRAN, where you specified what happens based on whether the tested expression was negative, zero, or positive. Here, we only care to specify what happens if the result of dividing
I by 3 gives zero for the result, in which case we jump to line 1.50. Otherwise, we just fall through to the next line since we don't give line numbers for the negative and positive cases. The result cannot be negative since both
I and the divisor are positive, and for the case of the positive result, we do want to go on to the next line, FOCAL's default here.
01.40 IF (FRAC(I / 5)) , 1.5, 1.6
This is much like the previous line except that we do give a line number for the positive case, since if we get here, it means
I is evenly divisible by neither 3 nor 5. Notice that we must give a line number; we can't just say
NEXT here, we must GOTO a line number where a
NEXT for our
FOR loop exists to achieve that end.
01.50 SET TOTAL = TOTAL + I 01.60 NEXT
These lines are not remarkable. The only reason they get separately numbered lines is so we can
GOTO each one separately depending on the results of the prior tests. If it were not for that consideration, I might have put both statements onto a single line,
which FOCAL supports with the semicolon, like so many more familiar modern languages.
01.70 TYPE "TOTAL: ", %6, TOTAL, !
This prints the end result, in its full 6-digit form, a somewhat remarkable thing for a 12-bit computer running an interpreted programming language that takes only about 6 kB on disk!
If we did not have the
%6 bit on that line, it would have printed our answer in scientific notation instead. We happen to know that the answer is 6 digits in advance, so we could specify that here, giving somewhat nicer output. This is another consequence of FOCAL's focus on scientific computing.
This solution is a bit more compact than the OS/8 BASIC alternative, though I find the BASIC version easier to read. That may simply be because I grew up with BASIC, however.
My sense is that FOCAL is generally more powerful than contemporary versions of BASIC, within its scope. Though it is a disappointingly limited language from a modern standpoint — for example, there is no ability to say "if this or that" — when compared to other languages of its time, it is actually a strong option for serious work. Given the choices available to PDP-8 programmers of the 1970s, I do not find it surprising in the least that many of them chose to use FOCAL.
Other Programs in the Series
I've solved this same problem in many other languages available for the PiDP-8/I:
You may find it interesting to compare their solutions.
Copyright © 2017 by Warren Young. This document is licensed under the terms of the SIMH license.