Commands are case insensitive, and have a shortened abbreviation, often the first letter. Commands have zero or more arguments,depending on the command. M is whitespace-aware. Spaces are treated as a delimiter between commands and arguments. Each command is separated from its arguments by 1 space. Commands with zero arguments are followed by 2 spaces.
#### W(rite)
Print data to the current device.
```
WRITE !,"hello world"
```
! is syntax for a new line. Multiple statements can be provided as additional arguments:
```
w !,"foo bar"," ","baz"
```
#### R(ead)
Retrieve input from the user
```
READ var
r !,"Wherefore art thou Romeo? ",why
```
Multiple arguments can be passed to a read command. Constants are outputted. Variables are retrieved from the user. The terminal waits for the user to enter the first variable before displaying the second prompt.
```
r !,"Better one, or two? ",lorem," Better two, or three? ",ipsum
```
#### S(et)
Assign a value to a variable
```
SET name="Benjamin Franklin"
s centi=0.01,micro=10E-6
w !,centi,!,micro
;.01
;.00001
```
#### K(ill)
Remove a variable from memory or remove a database entry from disk.
```
KILL centi
k micro
```
### Globals and Arrays
In addition to local variables, M has persistent variables stored to disk called _globals_. Global names must start with a __caret__ (__^__). Globals are the built-in database of M.
Any variable can be an array with the assignment of a _subscript_. Arrays are sparse and do not have a predefined size. Arrays should be visualized like trees, where subscripts are branches and assigned values are leaves. Not all nodes in an array need to have a value.
```
s ^cars=20
s ^cars("Tesla",1,"Name")="Model 3"
s ^cars("Tesla",2,"Name")="Model X"
s ^cars("Tesla",2,"Doors")=5
w !,^cars
; 20
w !,^cars("Tesla")
; null value - there's no value assigned to this node but it has children
Arrays are automatically sorted in order. Take advantage of the built-in sorting by setting your value of interest as the last child subscript of an array rather than its value.
```
; A log of temperatures by date and time
s ^TEMPS("11/12","0600",32)=""
s ^TEMPS("11/12","1030",48)=""
s ^TEMPS("11/12","1400",49)=""
s ^TEMPS("11/12","1700",43)=""
```
### Operators
```jinja
; Assignment: =
; Unary: + Convert a string value into a numeric value.
; Arthmetic:
; + addition
; - subtraction
; * multiplication
; / floating-point division
; \ integer division
; # modulo
; ** exponentiation
; Logical:
; & and
; ! or
; ' not
; Comparison:
; = equal
; '= not equal
; > greater than
; <lessthan
; '> not greater / less than or equal to
; '<notless/greaterthanorequalto
; String operators:
; _ concatenate
; [ contains a contains b
; ]] sorts after a comes after b
; '[ does not contain
; ']] does not sort after
```
#### Order of operations
Operations in M are _strictly_ evaluated left to right. No operator has precedence over any other.
You should use parentheses to group expressions.
```
w 5+3*20
;160
;You probably wanted 65
w 5+(3*20)
```
### Flow Control, Blocks, & Code Structure
A single M file is called a _routine_. Within a given routine, you can break your code up into smaller chunks with _tags_. The tag starts in column 1 and the commands pertaining to that tag are indented.
A tag can accept parameters and return a value, this is a function. A function is called with '$$':
```
; Execute the 'tag' function, which has two parameters, and write the result.
w !,$$tag^routine(a,b)
```
M has an execution stack. When all levels of the stack have returned, the program ends. Levels are added to the stack with _do_ commands and removed with _quit_ commands.
#### D(o)
With an argument: execute a block of code & add a level to the stack.
```
d ^routine ;run a routine from the begining.
; ;routines are identified by a caret.
d tag ;run a tag in the current routine
d tag^routine ;run a tag in different routine
```
Argumentless do: used to create blocks of code. The block is indented with a period for each level of the block:
```
set a=1
if a=1 do
. write !,a
. read b
. if b > 10 d
. . w !, b
w "hello"
```
#### Q(uit)
Stop executing this block and return to the previous stack level.
Quit can return a value.
#### N(ew)
Clear a given variable's value _for just this stack level_. Useful for preventing side effects.
Putting all this together, we can create a full example of an M routine:
```
; RECTANGLE - a routine to deal with rectangle math
q ; quit if a specific tag is not called
main
n length,width ; New length and width so any previous value doesn't persist
w !,"Welcome to RECTANGLE. Enter the dimensions of your rectangle."
r !,"Length? ",length,!,"Width? ",width
d area(length,width) ;Do a tag
s per=$$perimeter(length,width) ;Get the value of a function
w !,"Perimeter: ",per
q
area(length,width) ; This is a tag that accepts parameters.
; It's not a function since it quits with no value.
w !, "Area: ",length*width
q ; Quit: return to the previous level of the stack.
perimeter(length,width)
q 2*(length+width) ; Quits with a value; thus a function
```
### Conditionals, Looping and $Order()
F(or) loops can follow a few different patterns:
```jinja
;Finite loop with counter
;f var=start:increment:stop
f i=0:5:25 w i," " ;0 5 10 15 20 25
; Infinite loop with counter
; The counter will keep incrementing forever. Use a conditional with Quit to get out of the loop.
;f var=start:increment
f j=1:1 w j," " i j>1E3 q ; Print 1-1000 separated by a space
;Argumentless for - infinite loop. Use a conditional with Quit.
; Also read as "forever" - f or for followed by two spaces.
M has an if/else construct for conditional evaluation, but any command can be conditionally executed without an extra if statement using a _postconditional_. This is a condition that occurs immediately after the command, separated with a colon (:).
```jinja
; Conditional using traditional if/else
r "Enter a number: ",num
i num>100 w !,"huge"
e i num>10 w !,"big"
e w !,"small"
; Postconditionals are especially useful in a for loop.
; This is the dominant for loop construct:
; a 'for' statement
; that tests for a 'quit' condition with a postconditional
; then 'do'es an indented block for each iteration
s var=""
f s var=var_"%" q:var="%%%%%%%%%%" d ;Read as "Quit if var equals "%%%%%%%%%%"
. w !,var
;Bonus points - the $L(ength) built-in function makes this even terser
s var=""
f s var=var_"%" q:$L(var)>10 d ;
. w !,var
```
#### Array Looping - $Order
As we saw in the previous example, M has built-in functions called with a single $, compared to user-defined functions called with $$. These functions have shortened abbreviations, like commands.
One of the most useful is __$Order()__ / $O(). When given an array subscript, $O returns the next subscript in that array. When it reaches the last subscript, it returns "".
```jinja
;Let's call back to our ^TEMPS global from earlier:
; A log of temperatures by date and time
s ^TEMPS("11/12","0600",32)=""
s ^TEMPS("11/12","0600",48)=""
s ^TEMPS("11/12","1400",49)=""
s ^TEMPS("11/12","1700",43)=""
; Some more
s ^TEMPS("11/16","0300",27)=""
s ^TEMPS("11/16","1130",32)=""
s ^TEMPS("11/16","1300",47)=""
;Here's a loop to print out all the dates we have temperatures for:
n date,time ; Initialize these variables with ""
; This line reads: forever; set date as the next date in ^TEMPS.
; If date was set to "", it means we're at the end, so quit.
; Do the block below
f s date=$ORDER(^TEMPS(date)) q:date="" d
. w !,date
; Add in times too:
f s date=$ORDER(^TEMPS(date)) q:date="" d
. w !,"Date: ",date
. f s time=$O(^TEMPS(date,time)) q:time="" d
. . w !,"Time: ",time
; Build an index that sorts first by temperature -
; what dates and times had a given temperature?
n date,time,temp
f s date=$ORDER(^TEMPS(date)) q:date="" d
. f s time=$O(^TEMPS(date,time)) q:time="" d
. . f s temp=$O(^TEMPS(date,time,temp)) q:temp="" d
. . . s ^TEMPINDEX(temp,date,time)=""
;This will produce a global like
^TEMPINDEX(27,"11/16","0300")
^TEMPINDEX(32,"11/12","0600")
^TEMPINDEX(32,"11/16","1130")
```
## Further Reading
There's lots more to learn about M. A great short tutorial comes from the University of Northern Iowa and Professor Kevin O'Kane's [Introduction to the MUMPS Language][1] presentation.
To install an M interpreter / database on your computer, try a [YottaDB Docker image][2].
YottaDB and its precursor, GT.M, have thorough documentation on all the language features including database transactions, locking, and replication: