Beginner

Control Flow in MUMPS

Learn conditionals, loops, and branching in MUMPS, including its unique postconditionals, $SELECT, and the argumentless FOR/QUIT idiom that replaces the missing WHILE keyword

Control flow in MUMPS looks unusual to anyone coming from a C-family language. There are no curly braces, no switch, no while, and no for (init; condition; step) header. Instead, MUMPS gives you a small set of commands — IF, ELSE, FOR, DO, GOTO, QUIT — and a couple of distinctive features that do a surprising amount of work: postconditionals and the $SELECT function.

Two ideas drive almost everything. First, an IF doesn’t guard a block — it guards the rest of the line. When the expression is true, every command after it on that line runs; when it’s false, the line is abandoned and a system variable called $TEST is set to 0. Second, almost any command can carry a postconditional — a :condition suffix that lets the command run only when the condition is true. WRITE:x>0 "positive" writes nothing unless x is greater than zero.

Because MUMPS is typeless, a condition is just a number: zero is false and any non-zero value is true. Comparisons like age>17 produce 1 or 0, exactly the values the control-flow commands expect.

This tutorial covers conditionals, the four flavors of FOR loop, the WHILE-style idiom MUMPS uses in place of a while keyword, the $SELECT function that stands in for a ternary and a switch, and GOTO. As always, commands can be abbreviated to their first letter — I for IF, F for FOR, W for WRITE — but we’ll spell them out for clarity.

Conditionals: IF, ELSE, and Postconditionals

IF evaluates its argument and, if true, executes the remaining commands on the line. ELSE has no argument at all — it runs the rest of its line whenever $TEST (set by the most recent IF) is 0. Postconditionals attach a condition directly to a single command.

Create a file named controlflow.m:

controlflow ; Conditionals in MUMPS
 ; --- IF executes the rest of the line when the expression is true ---
 set age=20
 if age>17 write "Adult",!
 if age<13 write "Child",!
 ;
 ; --- IF / ELSE rely on $TEST, set by the most recent IF ---
 set score=45
 if score>59 write "Pass",!
 else  write "Fail",!
 ;
 ; --- Postconditionals: a colon after the command name ---
 ; The command runs only when the condition is true.
 set balance=-30
 write:balance<0 "Account overdrawn",!
 write:balance>0 "Account in credit",!
 ;
 ; --- Several commands can follow one IF on the same line ---
 set hour=14
 if hour<12 write "Good morning",! quit
 if hour<18 write "Good afternoon",! quit
 write "Good evening",!
 quit

A few things worth noticing. ELSE is followed by two spaces — because it takes no argument, MUMPS needs the extra space to know where the (empty) argument ends and the next command begins. The same rule applies to the argumentless FOR and QUIT you’ll see below. On the last block, if hour<18 write "Good afternoon",! quit runs both the write and the quit when the hour is before 18, so execution never reaches the Good evening line.

Loops: The Four Faces of FOR

MUMPS has exactly one loop command, FOR, but it comes in several forms. The numeric form takes variable=start:increment:end. A negative increment counts down. The argumentless form loops forever until a QUIT stops it — which, combined with a postconditional, is how MUMPS expresses a while loop.

Create a file named loops.m:

loops ; Loops in MUMPS
 ; --- Numeric FOR: variable=start:increment:end ---
 write "Up:   "
 for i=1:1:5 write i," "
 write !
 ;
 ; --- A negative increment counts down ---
 write "Down: "
 for i=5:-1:1 write i," "
 write !
 ;
 ; --- Step by 2 ---
 write "Even: "
 for i=2:2:10 write i," "
 write !
 ;
 ; --- While-style loop: argumentless FOR with a QUIT postconditional ---
 ; MUMPS has no WHILE keyword; this is the idiom.
 set n=1,total=0
 for  quit:n>5  set total=total+n,n=n+1
 write "Sum 1..5: ",total,!
 ;
 ; --- Block form: argumentless DO runs the dot-indented lines ---
 set count=0
 for i=1:1:3 do
 . set count=count+i
 . write "i=",i," count=",count,!
 quit

The while-style line is the most important idiom here: for quit:n>5 set total=total+n,n=n+1. The argumentless FOR (note the two spaces) repeats the rest of the line forever. On each pass it first runs quit:n>5 — a QUIT that fires only once n exceeds 5, breaking the loop — and otherwise falls through to the SET. To loop over more than a line’s worth of work, end the FOR with an argumentless DO and write the body as dot-indented lines (.), as the final block shows.

$SELECT and GOTO

MUMPS has no switch statement and no ?: ternary operator. The $SELECT function fills both roles: it scans a list of condition:value pairs left to right and returns the value of the first condition that is true. A trailing 1:default acts as the catch-all else. GOTO (abbreviated G) jumps to a label and, like every command, accepts a postconditional.

Create a file named dispatch.m:

dispatch ; $SELECT and GOTO in MUMPS
 ; --- $SELECT returns the value after the first true condition ---
 for temp=50,72,95 do
 . set label=$select(temp<60:"cold",temp<80:"mild",1:"hot")
 . write temp,"F is ",label,!
 ;
 ; --- $SELECT as an inline ternary (if/else expression) ---
 set n=7
 write n," is ",$select(n#2=0:"even",1:"odd"),!
 ;
 ; --- GOTO transfers control to a label (use sparingly) ---
 set tries=0
retry ; loop target
 set tries=tries+1
 write "Attempt ",tries,!
 goto:tries<3 retry
 write "Done after ",tries," attempts",!
 quit

$SELECT requires at least one condition to be true — if none match, it raises an error — which is why the idiomatic catch-all 1: (always true) appears last. The FOR temp=50,72,95 form loops over an explicit comma-separated list of values rather than a numeric range, a handy variant when the values aren’t evenly spaced. The GOTO block builds a counted loop by hand: the postconditional goto:tries<3 retry jumps back to the retry label until tries reaches 3.

Putting It Together: FizzBuzz

FizzBuzz combines a counted FOR, the modulo operator (#), IF with postconditional-style early QUIT, and a subroutine call. DO label(arg) calls a subroutine, passing arg to the formal parameter named in the label.

Create a file named fizzbuzz.m:

fizzbuzz ; FizzBuzz - combining MUMPS control flow
 for i=1:1:15 do check(i)
 quit
 ;
check(n) ; Classify one number, n
 if n#15=0 write "FizzBuzz",! quit
 if n#3=0 write "Fizz",! quit
 if n#5=0 write "Buzz",! quit
 write n,!
 quit

Each IF line ends with a QUIT that returns from the check subroutine the moment a match is found, so only one label is ever printed per number. Remember that MUMPS has no operator precedence: n#15=0 evaluates strictly left to right as (n#15)=0, which is exactly what we want.

Running with Docker

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pull the YottaDB image
docker pull yottadb/yottadb-base:latest-master

# Run the conditionals routine
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run controlflow

# Run the loops routine
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run loops

# Run the $SELECT / GOTO routine
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run dispatch

# Run FizzBuzz
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run fizzbuzz

Expected Output

Running controlflow:

Adult
Fail
Account overdrawn
Good afternoon

Running loops:

Up:   1 2 3 4 5 
Down: 5 4 3 2 1 
Even: 2 4 6 8 10 
Sum 1..5: 15
i=1 count=1
i=2 count=3
i=3 count=6

Running dispatch:

50F is cold
72F is mild
95F is hot
7 is odd
Attempt 1
Attempt 2
Attempt 3
Done after 3 attempts

Running fizzbuzz:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Key Concepts

  • IF guards the rest of the line, not a block. When the expression is true, every command after it on the same line runs; when false, the line is abandoned and $TEST is set to 0.
  • ELSE takes no argument and runs its line whenever $TEST is 0. Argumentless commands (ELSE, argumentless FOR, argumentless QUIT) must be followed by two spaces.
  • Postconditionals are everywhere. Almost any command accepts a :condition suffix — write:x>0, quit:done, goto:more retry — so it runs only when the condition is true.
  • There is one loop command, FOR, with four forms: numeric range (i=1:1:5), stepped or descending range (i=5:-1:1), explicit value list (temp=50,72,95), and argumentless (for ...) for open-ended looping.
  • MUMPS has no WHILE. The idiom for quit:condition body — argumentless FOR plus a QUIT postconditional — is the standard substitute.
  • $SELECT replaces both switch and the ternary operator. It returns the value after the first true condition:value pair; end with 1:default so it never falls through with no match.
  • Conditions are just numbers. Zero is false, any non-zero value is true, and comparisons return 1 or 0 — there is no distinct boolean type.
  • Blocks use dot indentation. End a FOR (or IF, or DO) with an argumentless DO and write the body as .-prefixed lines to group multiple statements under one controlling command.

Running Today

All examples can be run using Docker:

docker pull yottadb/yottadb-base:latest-master
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining