MUMPS

MUMPS
MUMPS
Paradigm(s) Procedural
Appeared in 1966
Designed by Neil Pappalardo
Stable release ANSI X11.1-1995 (December 8, 1995; 15 years ago (1995-12-08))
Typing discipline typeless
Influenced by JOSS
Influenced PSL, Caché ObjectScript
OS Cross-platform

MUMPS (Massachusetts General Hospital Utility Multi-Programming System), or alternatively M, is a programming language created in the late 1960s, originally for use in the healthcare industry. It was designed for the production of multi-user database-driven applications. It predates C and most other popular languages in current usage, and has very different syntax and terminology.

It was largely adopted during the 1970s and early 1980s in healthcare and financial information systems/databases, and continues to be used by many of the same clients today. It is currently used in electronic health record systems as well as by multiple banking networks and online trading/investment services.

Contents

History

MUMPS was developed by Neil Pappalardo and colleagues in Dr. Octo Barnett's animal lab at Massachusetts General Hospital (MGH) in Boston during 1966 and 1967. The original MUMPS system was, like Unix a few years later, built on a spare DEC PDP-7.

Octo Barnett and Neil Pappalardo were also involved with MGH's planning for a Hospital Information System, obtained a backward compatible PDP-9, and began using MUMPS in the admissions cycle and laboratory test reporting. MUMPS was then an interpreted language, yet even then, incorporated a hierarchical database file system to standardize interaction with the data. Some aspects of MUMPS can be traced from Rand Corporation's JOSS through BBN's TELCOMP and STRINGCOMP. The MUMPS team deliberately chose to include portability between machines as a design goal. Another feature, not widely supported for machines of the era, in operating systems or in hardware, was multitasking, which was also built into the language itself.

The portability was soon useful, as MUMPS was shortly adapted to a DEC PDP-15, where it lived for some time. MUMPS was developed with the support of a government research grant, and so MUMPS was released to the public domain (no longer a requirement for grants), and was soon ported to a number of other systems including the popular DEC PDP-8, the Data General Nova and the DEC PDP-11 and the Artronix PC12 minicomputer. Word about MUMPS spread mostly through the medical community, and by the early 1970s was in widespread use, often being locally modified for their own needs.

By the early 1970s, there were many and varied implementations of MUMPS on a range of hardware platforms. The most widespread was DEC's MUMPS-11 on the PDP-11, and MEDITECH's MIIS. In 1972, many MUMPS users attended a conference which standardized the then-fractured language, and created the MUMPS Users Group and MUMPS Development Committee (MDC) to do so. These efforts proved successful; a standard was complete by 1974, and was approved, on September 15, 1977, as ANSI standard, X11.1-1977. At about the same time DEC launched DSM-11 (Digital Standard MUMPS) for the PDP-11. This quickly dominated the market, and became the reference implementation of the time.

During the early 1980s several vendors brought MUMPS-based platforms that met the ANSI standard to market. The most significant were Digital Equipment Corporation with DSM (Digital Standard MUMPS), InterSystems with ISM (InterSystems M) on VMS and UNIX, and M/11+ on the PDP-11 platform. Other companies developed important MUMPS implementations:

  • Greystone Technology Corporation with a compiled version called GT.M
  • DataTree Inc. with an Intel PC based product called DTM
  • Micronetics Design Corporation with a product line called MSM for UNIX and Intel PC platforms (later ported to IBM's VM operating system, VAX-VMS platforms and Alpha-VMS platforms)
  • Comp Consultants (later renamed MGlobal), a Houston-based company originally created CCSM on 6800, then 6809 processors, and eventually a port to the 68000, which later became MacMUMPS, a Mac OS based product. They also worked on a the MGM MUMPS implementation. MGlobal also ported their implementation to the DOS platform where it ran as a guest operating system started from DOS.

MGlobal MUMPS was the first commercial MUMPS for the IBM PC and the only Mac implementation. DSM-11 was superseded by VAX/DSM for the VAX/VMS platform, and that was ported to the Alpha in two variants: DSM for OpenVMS, and as DSM for Ultrix.

This period also saw considerable MDC activity. The second revision of the ANSI standard for MUMPS (X11.1-1984) was approved on November 15, 1984. On November 11, 1990 the third revision of the ANSI standard (X11.1-1990) was approved. In 1992 the same standard was also adopted as ISO standard 11756-1992. Use of M as an alternative name for the language was approved around the same time. On December 8, 1995 the fourth revision of the standard (X11.1-1995) was approved by ANSI, and by ISO in 1999 as ISO 11756-1999. The MDC finalized a further revision to the standard in 1998 but this has not been presented to ANSI for approval. On 6 January 2005, and later again on 25 June 2010, ISO re-affirmed its MUMPS-related standards: ISO/IEC 11756:1999, language standard, ISO/IEC 15851:1999, Open MUMPS Interconnect and ISO/IEC 15852:1999, MUMPS Windowing Application Programmers Interface.

By 2000, the middleware vendor InterSystems had become the dominant player in the MUMPS market with the purchase of several other vendors. Initially they acquired DataTree Inc. in the early 1990s. And, on December 30, 1995, InterSystems acquired the DSM product line from DEC.[1] InterSystems consolidated these products into a single product line, branding them, on several hardware platforms, as OpenM. In 1997, InterSystems essentially completed this consolidation by launching a unified successor named Caché. This was based on their ISM product, but with influences from the other implementations. Micronetics Design Corporation assets were also acquired by InterSystems on June 21, 1998. InterSystems remains today (2011) the dominant MUMPS vendor, selling Caché to MUMPS developers who write applications for a variety of operating systems.

Greystone Technology Corporation's GT.M implementation was sold to Sanchez Computer Associates Inc. (now part of Fidelity National Financial Inc.) in the mid 1990s. On November 7, 2000 Sanchez made GT.M for Linux available under the GPL license[2] and on October 28, 2005 GT.M for OpenVMS and Tru64 UNIX were also made available under the AGPL license.[3] GT.M continues to be available on other UNIX platforms under a traditional license.

The newest implementation of MUMPS, released in April 2002, is an MSM derivative called M21 from the Real Software Company of Rugby, UK.

There are also several open source implementations of MUMPS, including some research projects. The most notable of these is Mumps/II, by Professor Kevin O'Kane (now at the University of Northern Iowa) and students' project. Dr. O'Kane and Ryan Murphy have also ported the interpreter to Mac OS X.[4]

One of the original creators of the MUMPS language, Neil Pappalardo, early founded a company called MEDITECH. They extended and built on the MUMPS language, naming the new language MIIS (and later, another language named MAGIC). Unlike InterSystems, MEDITECH no longer sells middleware, so MIIS and MAGIC are now only used internally at MEDITECH.

Current users of MUMPS applications

The U.S. Department of Veterans Affairs (formerly the Veterans Administration) was one of the earliest major adopters of the MUMPS language. Their development work (and subsequent contributions to the free MUMPS application codebase) was an influence on many medical users worldwide. In 1995, the Veterans Affairs' patient Admission/Tracking/Discharge system, Decentralized Hospital Computer Program (DHCP) was the recipient of the Computerworld Smithsonian Award for best use of Information Technology in Medicine. A decade later (July, 2006), the Department of Veterans Affairs (VA) / Veterans Health Administration (VHA) was the recipient of the Innovations in American Government Award presented by the Ash Institute of the John F. Kennedy School of Government at Harvard University for its extension of DHCP into the Veterans Health Information Systems and Technology Architecture (VistA) in July, 2006. Nearly the entire VA hospital system in the United States and the Indian Health Service, as well as major parts of the Department of Defense CHCS hospital system all run systems using MUMPS databases for clinical data tracking.

Large companies currently using MUMPS include AmeriPath (now part of Quest Diagnostics), Care Centric, Epic Systems Corporation, EMIS, Partners HealthCare, MEDITECH, GE Healthcare (formerly IDX Systems and Centricity). Many reference laboratories, such as Quest Diagnostics [5] and Dynacare, use MUMPS software written or based on by Antrim Corporation code. Antrim, and its parent Sunquest, was acquired by Misys in 2001.[6]

Coventry Healthcare and Massachusetts Hospital have also been reported to use MUMPS.

MUMPS is also widely used in financial applications. MUMPS gained an early following in the financial sector, and MUMPS applications are in use at many banks and credit unions. It is used by Ameritrade, the largest online trading service in the US with over 12 billion transactions per day, as well as by the Bank of England and Barclays Bank, among others.[7][8][9]

The European Space Agency announced on May 13, 2010 that it will use MUMPS (InterSystems Caché) to support the Gaia mission. This mission aims to map the Milky Way with unprecedented precision.[10]

As of 2005 most use of M is either in the form of GT.M or InterSystems Caché. The latter is being aggressively marketed by InterSystems and has had success in penetrating new markets, such as telecommunications, in addition to existing markets.

Overview

MUMPS is a language intended for and designed to build database applications. Secondary language features were included to help programmers make applications using minimal computing resources. The original implementations were interpreted, though modern implementations may be fully or partially compiled. Individual "programs" run in memory "partitions". Early MUMPS memory partitions were limited to 2048 bytes so aggressive abbreviation greatly aided multi-programming on severely resource limited hardware, because more than one MUMPS job could fit into the very small memories extant in hardware at the time. The ability to provide multi-user systems was another language design. The Multi-Programming in the acronym of language name point to this. Even the earliest machines running MUMPS supported multiple jobs running at the same time. With the change from mini-computers to micro-computers a few years later, even a "single user PC" with a single 8-bit CPU with 16K or 64K of memory could support multiple users, running dumb terminals in command line mode. (without any trace of a graphical user interface).

Since memory was tight originally, the language design for MUMPS valued very terse code. Thus, every MUMPS command or function name could be abbreviated from one to three letters in length, e.g. Quit (exit program) as Q, $P = $Piece function, R = Read command, $TR = $Translate function. Spaces and end-of-line markers are significant in MUMPS because line scope promoted the same terse language design. Hence, an entire line of program code could express the same idea a small number of characters that other programming languages might easily take 5 to 10 times as many characters to express. Abbreviation was a common feature of languages designed in this period (e.g., FOCAL-69, early BASICs such as Tiny BASIC, etc.). An unfortunate side effect of this coupled with the early need to write minimalist code was that MUMPS programmers routinely did not comment code and used extensive abbreviations, meaning that even an expert MUMPS programmer could not just skim through a page of code to see its function but would have to analyze it line by line.

Database interaction is transparently built into the language. The MUMPS language provides a hierarchical database made up of persistent sparse arrays, which is implicitly "opened" for every MUMPS application. All variable names prefixed with the caret character ("^") use permanent (instead of RAM) storage, will maintain their values after the application exits, and will be visible to (and modifiable by) other running applications. Variables using this shared and permanent storage are called Globals in MUMPS, because the scoping of these variables is "globally available" to all jobs on the system. The more recent and more common use of the name "global variables" in other languages is a more limited scoping of names, coming from the fact that unscoped variables are "globally" available to any programs running in the same process, but not shared among multiple processes. The MUMPS Storage mode (i.e. Globals stored as persistent sparse arrays), gives the MUMPS database the characteristics of a document-oriented database.[11]

All variable names which are not prefixed with caret character ("^") are temporary and private. Like global variables, they also have a hierarchical storage model, but are only "locally available" to a single job, thus they are called "locals". Both "globals" and "locals" can have child nodes (called subscripts in MUMPS terminology). Subscripts are not limited to numerals—any ASCII character or group of characters can be a subscript identifier. While this is not uncommon for modern languages such as Perl or JavaScript, it was a highly unusual feature in the late 1970s. This capability was not universally implemented in MUMPS systems before the 1984 ANSI standard, as only canonically numeric subscripts were required by the standard to be allowed[12]) Thus, the variable named 'Car' can have subscripts "Door", "Steering Wheel" and "Engine", each of which can contain a value and have subscripts of their own. The variable ^Car("Door") could have a nested variable subscript of "Color" for example. Thus, you could say

SET ^Car("Door","Color")="BLUE"

to modify a nested child node of ^Car. In MUMPS terms, "Color" is the 2nd subscript of the variable ^Car (both the names of the child-nodes and the child-nodes themselves are likewise called subscripts). Hierarchical variables are similar to objects with properties in many object oriented languages. Additionally, the MUMPS language design requires that all subscripts of variables are automatically kept in sorted order. Numeric subscripts (including floating-point numbers) are stored from lowest to highest. All non-numeric subscripts are stored in alphabetical order following the numbers. In MUMPS terminology, this is canonical order. By using only non-negative integer subscripts, the MUMPS programmer can emulate the arrays data type from other languages. Although MUMPS does not natively offer a full set of DBMS features such as mandatory schemas, several DBMS systems have been built on top of it that provide application developers with flat-file, relational and network database features.

Additionally, there are built-in operators which treat a delimited string (e.g., comma-separated values) as an array. Early MUMPS programmers would often store a structure of related information as a delimited string, parsing it after it was read in; this saved disk access time and offered considerable speed advantages on some hardware.

MUMPS has no data types. Numbers can be treated as strings of digits, or strings can be treated as numbers by numeric operators (coerced, in MUMPS terminology). Coercion can have some odd side effects, however. For example, when a string is coerced, the parser turns as much of the string (starting from the left) into a number as it can, then discards the rest. Thus the statement IF 20<"30 DUCKS" is evaluated as TRUE in MUMPS.

Other features of the language are intended to help MUMPS applications interact with each other in a multi-user environment. Database locks, process identifiers, and atomicity of database update transactions are all required of standard MUMPS implementations.

In contrast to languages in the C or Wirth traditions, some space characters between MUMPS statements are significant. A single space separates a command from its argument, and a space, or newline, separates each argument from the next MUMPS token. Commands which take no arguments (e.g., ELSE) require two following spaces. The concept is that one space separates the command from the (nonexistent) argument, the next separates the "argument" from the next command. Newlines are also significant; an IF, ELSE or FOR command processes (or skips) everything else til the end-of-line. To make those statements control multiple lines, you must use the DO command to create a code block.

"Hello, World!" example

A simple Hello world program in MUMPS might be:

hello()
 
write "Hello, World!",!
 
quit

and would be run from the MUMPS command line with the command 'do ^hello'. Since MUMPS allows commands to be strung together on the same line, and since commands can be abbreviated to a single letter, this routine could be made more compact:

hello() w "Hello, World!",! q

The ',!' after the text generates a newline. The 'quit' is not strictly necessary at the end of a function like this, but is good programming practice in case other functions are added below 'hello()' later.

Summary of key language features

The following summary seeks to give programmers familiar with other languages a feeling for what MUMPS is like. This is not a formal language specification, and many features and qualifiers have been omitted for brevity. ANSI X11.1-1995 gives a complete, formal description of the language; an annotated version of this standard is available online.[13]

Data types: There is one universal datatype, which is implicitly coerced to string, integer, or floating-point datatypes as context requires.

Booleans (called truthvalues in MUMPS): In IF commands and other syntax that has expressions evaluated as conditions, any string value is evaluated as a numeric value, and if that is a nonzero value, then it is interpreted as True. a<b yields 1 if a is less than b, 0 otherwise.

Declarations: None. All variables are dynamically created at the first time a value is assigned.

Lines: are important syntactic entities, unlike their status in languages patterned on C or Pascal. Multiple statements per line are allowed and are common. The scope of any IF, ELSE, and FOR command is "the remainder of current line."

Case sensitivity: Commands and intrinsic functions are case-insensitive. In contrast, variable names and labels are case-sensitive. There is no special meaning for upper vs. lower-case and few widely followed conventions. The percent sign (%) is legal as first character of variables and labels.

Postconditionals: execution of almost all commands can be controlled by following it with a colon and a truthvalue expression. SET:N<10 A="FOO" sets A to "FOO" if N is less than 10; DO:N>100 PRINTERR, performs PRINTERR if N is greater than 100. This construct provides a conditional whose scope is less than a full line.

Abbreviation: You can abbreviate nearly all commands and native functions to one, two, or three characters.

Reserved words: None. Since MUMPS interprets source code by context, there is no need for reserved words. You may use the names of language commands as variables. There has been no contest such as the International Obfuscated C Code Contest for MUMPS, despite the potential of examples such as the following, perfectly legal, MUMPS code:

GREPTHIS()
       NEW SET,NEW,THEN,IF,KILL,QUIT SET IF="KILL",SET="11",KILL="l1",QUIT="RETURN",THEN="KILL"
       IF IF=THEN DO THEN
       QUIT:$QUIT QUIT QUIT ; (quit)
THEN  IF IF,SET&KILL SET SET=SET+KILL QUIT

MUMPS can be made more obfuscated by using the contracted operator syntax, as shown in this terse example derived from the example above:

GREPTHIS()
       N S,N,T,I,K,Q S I="K",S="11",K="l1",Q="R",T="K"
       I I=T D T
       Q:$Q Q Q
T  I I,S&K S S=S+K Q

Arrays: are created dynamically, stored as B-trees, are sparse (i.e. use almost no space for missing nodes), can use any number of subscripts, and subscripts can be strings or numeric (including floating point). Arrays are always automatically stored in sorted order, so there is never any occasion to sort, pack, reorder, or otherwise reorganize the database. Built in functions such as $DATA, $ORDER, $NEXT(deprecated) and $QUERY functions provide efficient examination and traversal of the fundamental array structure, on disk or in memory.

for i=10000:1:12345 set sqtable(i)=i*i
set address("Smith","Daniel")="dpbsmith@world.std.com"

Local arrays: variable names not beginning with caret (i.e. "^") are stored in memory by process, are private to the creating process, expire when the creating process terminates. The available storage depends on implementation. For those implementations using partitions, it is limited to the partition size, (A small patition might be 32K). For other implementations, it may be several megabytes.

Global arrays: ^abc, ^def. These are stored on disk, are available to all processes, and are persistent when the creating process terminates. Very large globals (e.g., hundreds of gigabytes) are practical and efficient in most implementations. This is MUMPS' main "database" mechanism. It is used instead of calling on the operating system to create, write, and read files.

Indirection: in many contexts, @VBL can be used, and effectively substitutes the contents of VBL into another MUMPS statement. SET XYZ="ABC" SET @XYZ=123 sets the variable ABC to 123. SET SUBROU="REPORT" DO @SUBROU performs the subroutine named REPORT. This substitution allows for lazy evaluation and late binding as well as effectively the operational equivalent of "pointers" in other languages.

Piece function: This breaks variables into segmented pieces guided by a user specified separator string (sometimes called a "delimiter"). Those who know awk will find this familiar. $PIECE(STRINGVAR,"^",3) means the "third caret-separated piece of STRINGVAR." The piece function can also appear as an assignment (SET command) target. After

SET X="dpbsmith@world.std.com"

$PIECE("world.std.com",".",2) yields "std" SET $P(X,"@",1)="office" causes X to become "office@world.std.com" (note that $P is equivalent to $PIECE and could be written as such).

Order function: This function treats its input as a structure, and finds the next index that exists which has the same structure except for the last subscript. It returns the sorted value that is ordered after the one given as input. (This treats the array reference as a content-addressable data rather than an address of a value)

Set stuff(6)="xyz",stuff(10)=26,stuff(15)=""

$Order(stuff("")) yields 6, $Order(stuff(6)) yields 10, $Order(stuff(8)) yields 10, $Order(stuff(10)) yields 15, $Order(stuff(15)) yields "".

Set i="" For  Set i=$O(stuff(i)) Quit:i=""  Write !,i,10,stuff(i)

Here, the argument-less For repeats until stopped by a terminating Quit. This line prints a table of i and stuff(i) where i is successively 6, 10, and 15.

Multi-User/Multi-Tasking/Multi-Processor: MUMPS supports multiple simultaneous users and processes even when the underlying operating system does not (e.g., MS-DOS). Additionally, there is the ability to specify an environment for a variable, such as by specifying a machine name in a variable (as in SET ^|"DENVER"|A(1000)="Foo"), which can allow you to access data on remote machines.

For a thorough listing of the rest of the MUMPS commands, operators, functions and special variables, see these online resources:

  • MUMPS by Example, or the (out of print) book of the same name by Ed de Moel. Much of the language syntax is detailed there, with examples of usage.
  • The Annotated MUMPS Language Standard, showing the evolution of the language and differences between versions of the ANSI standard.

"MUMPS" vs. "M" naming debate

While of little interest to those outside the MUMPS/M community, this topic has been contentious there.

All of the following positions can, and have been, supported by knowledgeable people at various times:

  • The language's name became M in 1993 when the M Technology Association adopted it.
  • The name became M on December 8, 1995 with the approval of ANSI X11.1-1995
  • Both M and MUMPS are officially accepted names.
  • M is only an "alternate name" or "nickname" for the language, and MUMPS is still the official name.

Some of the contention arose in response to strong M advocacy on the part of one commercial interest, InterSystems, whose chief executive disliked the name MUMPS and felt that it represented a serious marketing obstacle. Thus, favoring M to some extent became identified as alignment with InterSystems. The dispute also reflected rivalry between organizations (the M Technology Association, the MUMPS Development Committee, the ANSI and ISO Standards Committees) as to who determines the "official" name of the language. Some writers have attempted to defuse the issue by referring to the language as M[UMPS], square brackets being the customary notation for optional syntax elements. A leading authority, and the author of an open source MUMPS implementation, Professor Kevin O'Kane, uses only 'MUMPS'.

The most recent standard (ISO/IEC 11756:1999, re-affirmed on 25 June 2010), still mentions both M and MUMPS as officially accepted names.

Epoch choice

In MUMPS, the current date and time is contained in a special system variable, $H (short for "HOROLOG"). The format is a pair of integers separated by a comma, e.g. "54321,12345" The first number is the number of days since December 31, 1840, i.e. day number 1 is January 1, 1841; the second is the number of seconds since midnight.

James M. Poitras has written that he chose this epoch for the date and time routines in a package developed by his group at MGH in 1969:

I remembered reading of the oldest (one of the oldest?) U.S. citizen, a Civil War veteran, who was 121 years old at the time. Since I wanted to be able to represent dates in a Julian-type form so that age could be easily calculated and to be able to represent any birth date in the numeric range selected, I decided that a starting date in the early 1840s would be 'safe.' Since my algorithm worked most logically when every fourth year was a leap year, the first year was taken as 1841. The zero point was then December 31, 1840.... I wasn't party to the MDC negotiations, but I did explain the logic of my choice to members of the Committee.

(More colorful versions have circulated in the folklore, suggesting, for example, that December 31, 1840 was the exact date of the first entry in the MGH records, but these seem to be urban legends. Another legend is that the date was chosen to commemorate the first use of ether as an anesthetic at Mass General.)

Sample programs

The direct execution of source code on historical computing platforms in an era of tiny disks, minuscule RAM and 300 baud serial connections led to a historical coding style that was terse, dense and expert friendly, even as contemporary MUMPS coding styles produce more readable code.

First, an example of M code from 2010, a solution to a benchmarking exercise based on calculating the longest sequence encountered when calculating the longest sequence of the Collatz conjecture for a range of integers. This example is written in GT.M and exploits and illustrates many features of MUMPS.

threeen1f
        ; Find the maximum number of steps for the 3n+1 problem for all integers through two input integers.
        ; See http://docs.google.com/View?id=dd5f3337_24gcvprmcw
        ; Assumes input format is 3 integers separated by a space with the first integer smaller than the second.
        ; The third integer is the number of parallel computation streams.  If it is less than twice the
        ; number of CPUs or cores, the parameter is modified to that value.  An optional fourth integer is the
        ; sizes of blocks of integers on which spawned child processes operate.  If it is not specified, the
        ; block size is approximately the range divided by the number of parallel streams.  If the block size is
        ; larger than the range divided by the number of execution streams, it is reduced to that value.
        ; No input error checking is done.
 
        ; Although the problem can be solved by using strictly integer subscripts and values, this program is
        ; written to show that the GT.M key-value store can use arbitrary strings for both keys and values -
        ; each subscript and value is spelled out using the strings in the program source line labelled "digits".
        ; Furthermore, the strings are in a number of international languages when GT.M is run in UTF-8 mode.
 
        ; K.S. Bhaskar 2010612
 
        ; No claim of copyright is made with respect to this program.
 
        ; Variables do not have to be declared before use, but are New'd in subprograms to ensure that they
        ; do not conflict with names in the caller.
 
        ; The program reads the program source at the label digits to get strings (separated by ;) for each language used.
digits  ;zero;eins;deux;tres;quattro;пять;ستة;सात;捌;ஒன்பது
        Do digitsinit                           ; Initialize data for conversion between integers and strings
 
        ; Get number of CPUs from /proc/cpuinfo and calculate minimum number of execution streams
        Open "cpus":(SHELL="/bin/sh":COMMAND="grep -i ^processor /proc/cpuinfo|wc -l":READONLY)::"PIPE"
        Use "cpus" Read streams Use $PRINCIPAL
        Close "cpus"
        Set streams=2*streams                   ; At least two execution streams per CPU
 
        ; At the top level, the program reads and processes input lines, one at a time.  Each line specifies
        ; one problem to solve.  Since the program is designed to resume after a crash and reuse partial
        ; results computed before the crash, data in the database at the beginning is assumed to be partial
        ; results from the previous run.  After computing and writing results for a line, the database is
        ; cleared for next line of input or next run of the program.
 
        ; Loop for ever, read a line (quit on end of file), process that line
        For  Read input Quit:$ZEOF!'$Length(input)  Do      ; input has entire input line
        .
        . Set i=$Piece(input," ",1)                         ; i - first number on line is starting integer for the problem
        . Set j=$Piece(input," ",2)                         ; j - second number on line is ending integer for the problem
        . Write $FNumber(i,",",0)," ",$FNumber(j,",",0)     ; print starting and ending integers, formatting with commas
        .
        . Set k=$Piece(input," ",3)                         ; k - third number on input line is number of parallel streams
        . If streams>k Do                                   ; print number of execution streams, optionally corrected
        ..  Write " (",$FNumber(k,",",0)
        ..  Set k=streams
        ..  Write "->",$FNumber(k,",",0),")"
        . Else  Write " ",$FNumber(k,",",0)
        .
        . Set blk=+$Piece(input," ",4)                          ; blk - size of blocks of integers is optional fourth piece
        . Set tmp=(j-i+k)\k                                     ; default / maximum block size
        . If blk&(blk'>tmp) Write " ",$FNumber(blk,",",0)       ; print block size, optionally corrected
        . Else  Do
        ..  Write " (",$FNumber(blk,",",0)
        ..  Set blk=tmp
        ..  Write "->",$FNumber(blk,",",0),")"
        .
        . ; Define blocks of integers for child processes to work on
        . Kill ^limits
        . Set tmp=i-1
        . For count=1:1 Quit:tmp=j  Do
        ..  Set ^limits(count)=$increment(tmp,blk)
        ..  Set:tmp>j (tmp,^limits(count))=j
        .
        . ; Launch jobs.  Grab lock l1, atomically increment counter, compute and launch one job for each block of numbers.
        . ; Each child job locks l2(pid), decrements the counter and tries to grab lock l1(pid).
        . ; When counter is zero, all jobs have started.  Parent releases lock l1 and tries to grab lock l2.
        . ; When all children have released their l2(pid) locks, they're done and parent can gather & report results.
        . Set ^count=0                                  ; Clear ^count - may have residual value if restarting from crash
        . Lock +l1                                      ; Set lock for process synchronization
        . For s=1:1:k Do
        ..  Set c=$Increment(^count)                    ; Atomic increment of counter in database for process synchronization
        ..  Set def=$ZTRNLNM("gtm_tmp") Set:'$Length(def) def=$ZTRNLNM("PWD")     ; Working directory for Jobbed process
        ..  Set err=$Text(+0)_"_"_$Job_"_"_s_".mje"                               ; STDERR for Jobbed process
        ..  Set out=$Extract(err,1,$Length(err)-1)_"o"                            ; STDOUT for Jobbed process
        ..  Set cmd="doblk(i):(ERROR="""_err_""":OUTPUT="""_out_""":DEFAULT="""_def_""")"     ; Command to Job
        ..  Job @cmd                             ; Job child process for next block of numbers
        . For  Quit:'^count  Hang 0.1            ; Wait for processes to start (^count goes to 0 when they do)
        . Lock -l1                               ; Release lock so processes can run
        . Set startat=$HOROLOG                   ; Get starting time
        . Lock +l2                               ; Wait for processes to finish
        .
        . ; When parent gets lock l2, child processes have completed and parent gathers and reports results.
        . set endat=$HOROLOG                     ; Get ending time - time between startat and endat is the elapsed time
        . ; Calculate duration
        . Set duration=(86400*($Piece(endat,",",1)-$Piece(startat,",",1)))+$Piece(endat,",",2)-$Piece(startat,",",2)
        . Write " ",$FNumber(^result,",",0)     ; Show largest number of steps for the range i through j
        . Write " ",$FNumber(^highest,",",0)    ; Show the highest number reached during the computation
        . Write " ",$FNumber(duration,",",0)    ; Show the elapsed time
        . Write " ",$FNumber(^updates,",",0)    ; Show number of updates
        . Write " ",$FNumber(^reads,",",0)      ; Show number of reads
        . ; If duratation is greater than 0 seconds, display update and read rates
        . Write:duration " ",$FNumber(^updates/duration,",",0)," ",$FNumber(^reads/duration,",",0)
        . Write !
        . Lock -l2                               ; Release lock for next run
        . Do dbinit                              ; Initialize database for next run
        Quit
 
dbinit  ; Entryref dbinit clears database between lines
        Kill ^count,^highest,^reads,^result,^step,^updates
        Quit
 
digitsinit                                      ; Initialize arrays to convert between strings and integers
        New m,x
        Set x=$Text(digits)
        For m=0:1:9 Set di($Piece(x,";",m+2))=m,ds(m)=$Piece(x,";",m+2)
        Quit
 
inttostr(n)                                     ; Convert an integer to a string
        New m,s
        Set s=ds($Extract(n,1))
        For m=2:1:$Length(n) Set s=s_" "_ds($Extract(n,m))
        Quit s
        ;
strtoint(s)                                     ; Convert a string to an integer
        New m,n
        Set n=di($Piece(s," ",1))
        For m=2:1:$Length(s," ") Set n=10*n+di($Piece(s," ",m))
        Quit n
 
; This is where Jobbed processes start
doblk(allfirst)
        Set (reads,updates,highest)=0           ; Start with zero reads, writes and highest number
        Do digitsinit                           ; Initialize data for conversion between integers and strings
        Lock +l2($JOB)                          ; Get lock l2 that parent will wait on till this Jobbed processes is done
        If $Increment(^count,-1)                ; Decrement ^count to say this process is alive
        Lock +l1($JOB)                          ; This process will get lock l1($JOB) only parent has released lock on l1
        ;
        ; Process the next block in ^limits that needs processing; quit when done
        For  Quit:'$Data(^limits($increment(tmp)))  Do:1=$increment(^limits(tmp,1)) dostep($select($data(^limits(tmp-1)):^limits(tmp-1)+1,1:allfirst),^limits(tmp))
        ;
        TStart ()                               ; Update global statistics inside a transaction
        ; The following line unconditionally adds the number of reads & write performed by this process to the
        ; number of reads & writes performed by all processes, and sets the highest for all processes if the
        ; highest calculated by this process is greater than that calculated so far for all processes
        Set:$Increment(^reads,reads)&$Increment(^updates,updates)&(highest>$Get(^highest)) ^highest=highest
        TCommit
        Lock -l1($JOB),-l2($JOB)                ; Release locks to tell parent this parent is done
        Quit                                    ; Jobbed processes terminate here
 
dostep(first,last)                              ; Calculate the maximum number of steps from first through last
        New current,currpath,i,n
        For current=first:1:last Do
        . Set n=current                          ; Start n at current
        . Kill currpath                          ; Currpath holds path to 1 for current
        . ; Go till we reach 1 or a number with a known number of steps
        . For i=0:1 Quit:$Increment(reads)&($Data(^step($$inttostr(n)))!(1=n))  Do
        ..  Set currpath(i)=n                     ; log n as current number in sequence
        ..  Set n=$Select('(n#2):n/2,1:3*n+1)     ; compute the next number
        ..  Set:n>highest highest=n             ; see if we have a new highest number reached
        . Do:0<i                                 ; if 0=i we already have an answer for n, nothing to do here
        ..  If 1<n Set i=i+$$strtoint(^step($$inttostr(n)))
        ..  TStart ()                             ; Atomically set maximum
        ..  Set:i>$Get(^result) ^result=i
        ..  TCommit
        ..  Set n="" For  Set n=$Order(currpath(n)) Quit:""=n  Set:$Increment(updates) ^step($$inttostr(currpath(n)))=$$inttostr(i-n)
        Quit

Next, an example of "traditional" M coding style, a VistA source code routine from the late 1970s (last edited in 1992), written originally by George Timson at a time when the language standard had not evolved features such as variable scoping. It is generally recognized that this code is not very readable to many programmers today. Though it should be noted that the code is expert friendly, (i.e. to acculturated VistA programmers it is readable and appropriately structured) - this code is an example of code that has been used effectively for many years. The culture and style of long lived application code cannot and does not change overnight, and when code continues to work, such cultures focus on adding new functionality rather than updating code simply for stylistic reasons.

Due to the terse nature of fully abbreviated MUMPS code, the size of similar code in other programming languages providing the same functionality would be considerably larger.

%DTC
%DTC ; SF/XAK - DATE/TIME OPERATIONS ;1/16/92  11:36 AM
     ;;19.0;VA FileMan;;Jul 14, 1992
     D    I 'X1!'X2 S X="" Q
     S X=X1 D H S X1=%H,X=X2,X2=%Y+1 D H S X=X1-%H,%Y=%Y+1&X2
     K %H,X1,X2 Q
     ;
C    S X=X1 Q:'X  D H S %H=%H+X2 D YMD S:$P(X1,".",2) X=X_"."_$P(X1,".",2) K X1,X2 Q
S    S %=%#60/100+(%#3600\60)/100+(%\3600)/100 Q
     ;
H    I X<1410000 S %H=0,%Y=-1 Q
     S %Y=$E(X,1,3),%M=$E(X,4,5),%D=$E(X,6,7)
     S %T=$E(X_0,9,10)*60+$E(X_"000",11,12)*60+$E(X_"00000",13,14)
TOH  S %H=%M>2&'(%Y#4)+$P("^31^59^90^120^151^181^212^243^273^304^334","^",%M)+%D
     S %='%M!'%D,%Y=%Y-141,%H=%H+(%Y*365)+(%Y\4)-(%Y>59)+%,%Y=$S(%:-1,1:%H+4#7)
     K %M,%D,% Q
     ;
DOW  D H S Y=%Y K %H,%Y Q
DW   D H S Y=%Y,X=$P("SUN^MON^TUES^WEDNES^THURS^FRI^SATUR","^",Y+1)_"DAY"
     S:Y<0 X="" Q
7    S %=%H>21608+%H-.1,%Y=%\365.25+141,%=%#365.25\1
     S %D=%+306#(%Y#4=0+365)#153#61#31+1,%M=%-%D\29+1
     S X=%Y_"00"+%M_"00"+%D Q
     ;
YX   D YMD S Y=X_% G DD^%DT
YMD  D 7 S %=$P(%H,",",2) D S K %D,%M,%Y Q
T    F %=1:1 S Y=$E(X,%) Q:"+-"[Y  G 1^%DT:$E("TODAY",%)'=Y
     S X=$E(X,%+1,99) G PM:Y="" I +X'=X D DMW S X=%
     G:'X 1^%DT
PM   S @("%H=$H"_Y_X) D TT G 1^%DT:%I(3)'?3N,D^%DT
N    F %=2:1 S Y=$E(X,%) Q:"+-"[Y  G 1^%DT:$E("NOW",%)'=Y
     I Y="" S %H=$H G RT
     S X=$E(X,%+1,99)
     I X?1.N1"H" S X=X*3600,%H=$H,@("X=$P(%H,"","",2)"_Y_X),%=$S(X<0:-1,1:0)+(X\86400),X=X#86400,%H=$P(%H,",")+%_","_X G RT
     D DMW G 1^%DT:'% S @("%H=$H"_Y_%),%H=%H_","_$P($H,",",2)
RT   D TT S %=$P(%H,",",2) D S S %=X_% I %DT'["S" S %=+$E(%,1,12)
     Q:'$D(%(0))  S Y=% G E^%DT
PF   S %H=$H D YMD S %(9)=X,X=%DT["F"*2-1 I @("%I(1)*100+%I(2)"_$E("> <",X+2)_"$E(%(9),4,7)") S %I(3)=%I(3)+X
     Q
TT   D 7 S %I(1)=%M,%I(2)=%D,%I(3)=%Y K %M,%D,%Y Q
NOW  S %H=$H,%H=$S($P(%H,",",2):%H,1:%H-1)
     D TT S %=$P(%H,",",2) D S S %=X_$S(%:%,1:.24) Q
DMW  S %=$S(X?1.N1"D":+X,X?1.N1"W":X*7,X?1.N1"M":X*30,+X=X:X,1:0)
     Q
COMMA     ;
     S %D=X<0 S:%D X=-X S %=$S($D(X2):+X2,1:2),X=$J(X,1,%),%=$L(X)-3-$E(23456789,%),%L=$S($D(X3):X3,1:12)
     F %=%:-3 Q:$E(X,%)=""  S X=$E(X,1,%)_","_$E(X,%+1,99)
     S:$D(X2) X=$E("$",X2["$")_X S X=$J($E("(",%D)_X_$E(" )",%D+1),%L) K %,%D,%L
     Q
HELP S DDH=$S($D(DDH):DDH,1:0),A1="Examples of Valid Dates:" D %
     S A1="  JAN 20 1957 or 20 JAN 57 or 1/20/57"_$S(%DT'["N":" or 012057",1:"") D %
     S A1="  T   (for TODAY),  T+1 (for TOMORROW),  T+2,  T+7,  etc." D %
     S A1="  T-1 (for YESTERDAY),  T-3W (for 3 WEEKS AGO), etc." D %
     S A1="If the year is omitted, the computer "_$S(%DT["P":"assumes a date in the PAST.",1:"uses the CURRENT YEAR.") D %
     I %DT'["X" S A1="You may omit the precise day, as:  JAN, 1957" D %
     I %DT'["T",%DT'["R" G 0
     S A1="If the date is omitted, the current date is assumed." D %
     S A1="Follow the date with a time, such as JAN 20@10, T@10AM, 10:30, etc." D %
     S A1="You may enter a time, such as NOON, MIDNIGHT or NOW." D %
     I %DT["S" S A1="Seconds may be entered as 10:30:30 or 103030AM." D %
     I %DT["R" S A1="Time is REQUIRED in this response." D %
0    Q:'$D(%DT(0))
     S A1=" " D % S A1="Enter a date which is "_$S(%DT(0)["-":"less",1:"greater")_" than or equal to " D %
     S Y=$S(%DT(0)["-":$P(%DT(0),"-",2),1:%DT(0)) D DD^%DT:Y'["NOW"
     I '$D(DDS) W Y,"." K A1 Q
     S DDH(DDH,"T")=DDH(DDH,"T")_Y_"." K A1 Q
     ;
%    I '$D(DDS) W !,"     ",A1 Q
     S DDH=DDH+1,DDH(DDH,"T")="     "_A1 Q

The following code is a complete implementation of ROT13, a trivially breakable cipher used for various purposes on the Net, not high security. It illustrates the compact nature of MUMPS code and is rather less cryptic than the sample above.

ST      ; ROT13 ; Gunter Rensch ; 2000-01-03 ; Encrypt/Decrypt ROT13
        Q ; no direct execution
        ;
        ; call from your program with
        ;       S A="String"
        ;       S A=$$ROT^ROT13(.A)
        ;
ROT(R)  ;
        S S1="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        S S2="NOPQRSTUVWXYZABCDEFGHIJKLM"
        S s1="abcdefghijklmnopqrstuvwxyz"
        S s2="nopqrstuvwxyzabcdefghijklm"
        S R=$TR(R,S1_s1,S2_s2)
        Q R

A second implementation is below, which illustrates the possibilities of concision in MUMPS.

s A="String" F i=1:1:$L(A) W $c($S($A($E(A,i))<91:$A($E(A,i))-52#26+65,1:$A($E(A,i))-84#26+97))

Finally, one of the shortest programs ever written in a high-level language, demonstrating the extreme concision of which MUMPS is capable.

s x="x x" x x

The same algorithm using expanded variable and command names

Set X="Xecute X" Xecute X

This program sets a value of "x x" to a variable named x, and then launches an infinite recursive execution of x, probably resulting in a stack overflow. At just 13 characters, including spaces and an end-of-line mark (since the MUMPS standard specifies storage in characters, not in bytes), using just two characters, the first variant demonstrates that it is also possible to write obscure and obfuscated code in M.

See also

  • PSL an extension to MUMPS
  • Caché ObjectScript an object oriented extension to MUMPS from a prominent MUMPS vendor
  • EsiObjects an Open Source Object Oriented extension to MUMPS

References

Further reading

  • Walters, Richard (1989). "ABCs of MUMPS. 1989: Butterworth-Heinemann, ISBN 1-55558-017-3.
  • Walters, Richard (1997). M Programming: A Comprehensive Guide. Digital Press. ISBN 1-55558-167-6.
  • Lewkowicz, John. The Complete MUMPS : An Introduction and Reference Manual for the MUMPS Programming Language. ISBN 0-13-162125-4
  • Kirsten, Wolfgang, et al. (2003) Object-Oriented Application Development Using the Caché Postrelational Database ISBN 3-540-00960-4
  • Martínez de Carvajal Hedrich, Ernesto (1993). "El Lenguaje MUMPS". Completa obra en castellano sobre el lenguaje Mumps. ISBN 84-477-0125-5. Distribuido exclusivamente por su author (ecarvajal@hedrich.es)
  • O'Kane, K.C.; A language for implementing information retrieval software, Online Review, Vol 16, No 3, pp 127–137 (1992).
  • O'Kane, K.C.; and McColligan, E. E., A case study of a Mumps intranet patient record, Journal of the Healthcare Information and Management Systems Society, Vol 11, No 3, pp 81–95 (1997).
  • O'Kane, K.C.; and McColligan, E.E., A Web Based Mumps Virtual Machine, Proceedings of the American Medical Informatics Association 1997
  • O'Kane, K.C., The Mumps Programming Language, Createspace, ISBN 1438243383, 120 pages (2010).

External links


Wikimedia Foundation. 2010.

Игры ⚽ Поможем сделать НИР
Synonyms:

Look at other dictionaries:

  • MUMPS — (Massachusetts General Hospital Utility Multi Programming System) ist eine ursprünglich auf DEC PDP und VAX Computern entwickelte Programmiersprache (genauer eine Interpretersprache, die selbst Multitasking ermöglicht) mit integrierter Datenbank …   Deutsch Wikipedia

  • MUMPS — Apparu en 1966 Auteur Neil Pappalardo Paradigme procédural Typage …   Wikipédia en Français

  • mumps — /mumps/, n. (used with a sing. v.) Pathol. an infectious disease characterized by inflammatory swelling of the parotid and usually other salivary glands, and sometimes by inflammation of the testes or ovaries, caused by a paramyxovirus. [1590… …   Universalium

  • mumps — mȕmps (mȕms) m DEFINICIJA pat. zarazna bolest priušnih žlijezda koje oteknu na jednoj ili na obje strane lica; parotitis, zaušnjaci ETIMOLOGIJA njem. Mumps ← engl. mumps …   Hrvatski jezični portal

  • mumps — the illness, is usually treated as a singular noun (Mumps is common in young children), and is sometimes used informally or locally with the • (This morning I had a chat with one of the girls who has been off sick with the mumps Evening Gazette,… …   Modern English usage

  • Mumps — Mumps, n. [Prov. E. mump to be sulky. Cf. {Mump}, {Mumble}, and {Mum}.] 1. pl. Sullenness; silent displeasure; the sulks. Skinner. [1913 Webster] 2. [Prob. so called from the patient s appearance.] (Med.) A specific infectious febrile disorder… …   The Collaborative International Dictionary of English

  • mumps — [mʌmps] n [U] also the mumps [Date: 1500 1600; Origin: mump expression made by twisting the mouth (16 17 centuries)] an infectious illness which makes your neck swell and become painful …   Dictionary of contemporary English

  • Mumps — der, landsch. auch die; <aus gleichbed. engl. mumps> Ziegenpeter; durch ein Virus hervorgerufene Entzündung der Ohrspeicheldrüse mit schmerzhaften Schwellungen (Med.) …   Das große Fremdwörterbuch

  • Mumps — Smf (auch Mumpf südd. m.) Parotitis per. Wortschatz reg. (19. Jh.) Entlehnung. Als Krankheitsbezeichnung ist das Wort aus ne. mumps entlehnt, einer wie ein Singular behandelten Pluralform zu ne. mump ein verdrossenes Gesicht machen (bezogen auf… …   Etymologisches Wörterbuch der deutschen sprache

  • mumps — (n.) type of contagious disease, c.1600, from plural of mump a grimace (1590s), originally a verb, to whine like a beggar (1580s), from Du. mompen to cheat, deceive, originally probably to mumble, whine, of imitative origin. The infectious… …   Etymology dictionary

  • Mumps — Mumps: Der seit dem Anfang des 19. Jh.s bezeugte Name für die meist harmlos verlaufende ansteckende Entzündung der Ohrspeicheldrüse – dafür im Volksmund Bezeichnungen wie »Bauernwetzel« und »Ziegenpeter« (s. unter ↑ Ziege) – ist aus dem Engl.… …   Das Herkunftswörterbuch

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”