On Sunday, 6 June 2010, mumpster John Leo Zimmer proposed on the Hardhats mailing list that we create new versions of routine %DTC to illustrate the difference between early MUMPS and modern MUMPS, and that we use the new code as an example on the MUMPS Wikipedia article.
In the replies that follow, I'm going to take John up on his offer by refactoring the routine step by step. My main goal here is to help teach refactoring to journeyman mumpsters who are often faced with similar situations, in which they have taken responsibility for a routine written by someone else and must now master it.
For those interested in the background behind this project, I'm appending the complete thread from Hardhats
=====
ORIGINAL MESSAGE
Date: Fri, 4 Jun 2010 00:36:41 -0700 (PDT)
Subject: [Hardhats] Anyone care to comment on this?
From: rtweed <rob.tweed@gmail.com>
http://ankhos.wordpress.com/2010/06/03/ ... -the-mumpsI've already posted a comment.
Could somebody remove that damned code example from Wikipedia? It
causes more damage to people's perception of the language than
anything else I know.
RESPONSE # 1
Date: Fri, 04 Jun 2010 05:09:46 -0400
From: Joseph Dal Molin <dalmolin@e-cology.ca>
Subject: Re: [Hardhats] Anyone care to comment on this?
How about someone suggesting something to replace the Wikipedia snippet
that shows both the power of M and an example of a well written snippet.
RESPONSE # 2
Date: Fri, 4 Jun 2010 13:43:25 -0400
Subject: Re: [Hardhats] Anyone care to comment on this?
From: Sam Habiel <sam.habiel@gmail.com>
You know, the only thing that the Mumps example is missing is
comments. Otherwise it's a very nice and succinct piece of code
written in the style of early Mumps which I cannot fault given
hardware limitations and language development at that time. I don't
know if anybody realizes this, but this is the fileman routine %DTC,
an integral part of VISTA (see documentation at
http://www.hardhats.org/fileman/pm/cl_dtc1.htm).
The author's comparison isn't fair. This code does some pretty damned
complex stuff (gives you the difference between 2 dates); the python
code is just a potboiler.
I personally cannot argue with the author's point: most of VISTA code
looks like that; and is difficult for outsiders to refactor. And the
history of VISTA development informs us that it pretty much froze any
development in the early to mid-90's; much of the innovations of the
90 and 95 M standards have not been applied (parameter passing; newing
variables; never mind transactions). Notice in this example that the
programmers gets the parameters from the symbol table an kills the
variables at the end of the snippet... that pretty much dates it to
before 1990.
Sam
RESPONSE # 3
Date: Fri, 4 Jun 2010 14:22:02 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: rtweed <rob.tweed@gmail.com>
Sam
Sorry but I strongly and passionately disagree.
At the time it was written it was.....perhaps....the way you would
write this kind of stuff, but I'm sorry, to perpetuate and/or sanction
that style of indecipherable code is inexcusable these days. As I
say, it's precisely this kind of code that gives Mumps the universal
raspberry that it consistently gets...and I have to say I'm not
surprised. That %DTC routine could easily be rewritten to be much
clearer in terms of what it's doing.
As I've noted, if I employed someone who wrote code that like these
days....well they wouldn't be employed too long. Succinct it may be,
but it's as clear as mud, even to me who has been using Mumps since
1980. Even in those days I'd have had words with any developer in our
team that wrote something like that. Nice it certainly ain't.
It has to be removed from the Wikipedia page if we want anyone to take
Mumps seriously as a language fit for purpose in the 21st century
Rob
RESPONSE # 4
Date: Fri, 4 Jun 2010 15:05:26 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: "kdtop3@gmail.com" <kdtop3@gmail.com>
Rob,
Have you tried to change the wikipedia page and been shot down?
Maybe you could add something to show the way we can use mumps in
2010.
Kevin
RESPONSE # 5
Subject: RE: [Hardhats] Re: Anyone care to comment on this?
Date: Fri, 4 Jun 2010 22:19:53 -0500
From: "Bhaskar, KS" <KS.Bhaskar@FISglobal.com>
Didn't Jim Self have a converter that would automatically convert between
the abbreviated and full forms of MUMPS?
Regards
-- Bhaskar
RESPONSE # 6
Date: Fri, 4 Jun 2010 22:56:34 -0400
Subject: Re: [Hardhats] Re: Anyone care to comment on this?
From: JohnLeo Zimmer <johnleozim@gmail.com>
It would be an interesting exercise to rewrite %DTC in a manner free
of the constraints imposed on XAK by the systems available in those
ancient times.
It would also make a more interesting Wikipedia article.
jl.z
RESPONSE # 7
Date: Sat, 5 Jun 2010 00:45:12 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: rtweed <rob.tweed@gmail.com>
Bhaskar
That won't help - expanding the abbreviation won't make the logic any
clearer - that's the problem: you simply wouldn't write logic like
this any more. I'm sure it's all very clever stuff and very
efficient, but even if you expand it out it will still be
incomprehensible. By that I mean that you can't look at the code and
immediately understand 2 things:
- what is it doing?
- why is it doing it?
Unless you can quickly understand these 2 things, the code is
basically unmaintainable, even if you threw in loads of comments
Simple example - take the line:
Code:
TOH S %H=%M>2&'(%Y#4)+$P("^31^59^90^120^151^181^212^243^273^304^334","^",%M)+%D
Now what the hell is this all about? It's actually quite simple but
you wouldn't guess it from those hieroglyphics. So let's figure it
out:
That nasty $p bit ought to be separated out into something more
comprehensible:
Code:
getCumulativeDays(month)
new days,counter
s counter="^31^59^90^120^151^181^212^243^273^304^334"
s days=$p(cumulativeDays,"^",month)
QUIT days
Now that detail is out of the way and inherently explained by the
function name, we can begin to tidy up that line and make it clearer
what's going on:
Code:
TOH S %H=%M>2&'(%Y#4)+$$getCumulativeDays(%M)+%D
Still clear as mud, so let's re-write the whole thing:
Code:
convertToHValue(days,month)
new hValue,totalDays
set totalDays=$$getCumulativeDays(month)
set hValue=totalDays+days
if month>2,year#4=0 set hValue=hValue+1
QUIT hValue
Now it's clearer what it's doing it, and it's hopefully a lot clearer
why it's doing it too, so now we have something that is beginning to
look like it might be maintainable. A lot more wordy, yes, but on
today's hardware nobody will even notice any difference in execution
time, but the really costly thing - programmer time to do maintenance
- will be a lot more efficient.
Unfortunately, this kind of transformation isn't something any code
converter is going to manage to achieve.
Rob
RESPONSE # 8
Date: Sat, 5 Jun 2010 11:28:55 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: George Timson <gtimson@post.harvard.edu>
OK, now, this guy orlowski admits he is being "a tad unfair".
He illustrates with MUMPS the following task:
"For any two dates in the past 200 years, find the number of days
between them, remembering that 1900 was not a leap year."
He illustrates with Python the following task:
"Write out the four names 'john', 'pat', 'gary', and 'michael'"
May I opine that these two tasks differ in complexity by at least a
couple of orders of magnitude, and that in fact the second task is not
one that anyone would write a real program for?
VA got what they paid for. In thirty years they never (except for
Y2K) paid for a re-factoring of FileMan, and so they did not get it.
They still run with code designed, like this, to process the minimal
number of stack levels, and to fit in the tiniest of partitions.
I might mention by the way that this code _is_ commented, in a
separate corpus of documentation that the VA holds.
And also by the way, even though XAK's initials are on this routine
(the late lamented Michael Distaso, that is), his memory should not be
sullied with the blame for this mess. Fact is, I wrote it. And I
wrote it _before_ 1980.
Yours,
--George Timson
RESPONSE # 9
Date: Sat, 5 Jun 2010 11:35:41 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: George Timson <gtimson@post.harvard.edu>
Actually, I understated the unfairness here. There are _two_ MUMPS
tasks performed in this snippet:
> "For any two dates in the past 200 years, find the number of days
> between them, remembering that 1900 was not a leap year."
> "For any date in the past 200 years, and a given positive or negative integer, add or subtract that interger number of days and return the resulting date"
--George Timson
RESPONSE # 10
Date: Sat, 5 Jun 2010 11:56:20 -0400
Subject: Re: [Hardhats] Re: Anyone care to comment on this?
From: Sam Habiel <sam.habiel@gmail.com>
Wow! I can now understand it.
Sam
RESPONSE # 11
Date: Sun, 6 Jun 2010 00:53:25 -0700 (PDT)
Subject: [Hardhats] Re: Anyone care to comment on this?
From: rtweed <rob.tweed@gmail.com>
George
A tad unfair, perhaps, but Mr Orlowski's reaction to his perecption of
what Mumps code must be like, based on the example he saw in
Wikipedia, was hardly surprising. And therein is the issue I'm
wanting to focus on, rather than an analysis of the code example
itself.
The fact is that the inclusion of that particular block of old,
historic code as an example of what Mumps code looks like does the
entire worldwide community of Mumps developers a massive disservice.
Every practitioner of every other language looking at that page will
believe that's the kind of indecipherable code that all of us Mumps
programmers must write, and, not unreasonably they run a mile and/or
fall about laughing.
If you really think about Mr Orlowski's trivial example and why he
juxtaposed it against the Mumps example, what he was really
demonstrating was that Python code easily meets those two critical
criteria I mentioned in my previous post:
- what does the code do?
- why does it do it?
His example, albeit trivial, meets those criteria, and his tacit
implication is that the same will be true if you write complex
applications using Python.
By comparison, the Mumps code meets neither of these criteria. Indeed
for all he knows that Mumps code might be doing something as trivial
as his example. As the author, *you* know that the code is actually
doing some complex date manipulation logic, but the code doesn't shout
that out loud and clear to Mr Orlowski or any other reader. His
assumption is, not unreasonably, that if this is an example of what
Mumps code must be like, then *all* Mumps applications must be
unmaintainable gibberish.
Now of course, *we* all know that modern Mumps code can be just as
expressive and comprehensible as any other mainstream language. The
purpose of my previous posting was two-fold:
- to demonstrate that Mumps code can be written in a way that meets
those two criteria. Indeed well-written modern Mumps looks
surprisingly similar to corresponding Python code
- to point out to Bhaskar that simply automatically expanding the
example code is not going to help - any non-Mumps reader will still
see indecipherable code that doesn't meet the two key criteria.
Manual refactoring is the only option if that example is to be used.
The bottom line is that the example in the Wikipedia page is one of
the key reasons why Mumps continues to get such a bad press out there
and is continually lambasted by practitioners of other languages. It
has to go. It does none of us any favours.
Rob
RESPONSE # 12
Date: Sun, 6 Jun 2010 07:27:17 -0400
Subject: [Hardhats] Was: Anyone care to comment on this? Refactoring exercise
for SCM
From: JohnLeo Zimmer <johnleozim@gmail.com>
I propose a small side-project for next week's VCM:
Let's run an exercise to evolve new versions of %DTC that are faithful
to the original's function but with no other restraint other than
clarity. SAC need not apply, and we are not necessarily seeking to
replace the legacy code which obviously does function.
By the end of the week dialog among conference participants may lead
to a consensus on a version that can be posted to Wikipedia alongside
the legacy version.
RESPONSE # 13
Date: Sun, 6 Jun 2010 05:05:16 -0700 (PDT)
Subject: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: rtweed <rob.tweed@gmail.com>
Sounds like a great idea!
Not sure about alongside the legacy version btw - I'd completely
replace it for all the reasons I've outlined before.
Rob
RESPONSE # 14
Date: Sun, 6 Jun 2010 08:30:44 -0400
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: JohnLeo Zimmer <johnleozim@gmail.com>
IMHO the legacy code is important for understanding the history of
MUMPS. And keeping the legacy %DTC would preempt the M debunkers who
can pull 20,000 other examples of dense code from FOIA. I would
envision an article that shows:
Modern code:
Legacy code (circa 1980, PDP hardware, etc.):
Reference to the history:
jl.z
RESPONSE # 15
Date: Sun, 6 Jun 2010 05:57:50 -0700 (PDT)
Subject: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: rtweed <rob.tweed@gmail.com>
Provided it's suitably explained, then I guess you may be right.
You know, I can't help feeling there's also another dimension to this,
with interesting timing given the recent VistA Modernization report
and the attendee list for the VCM..
Seems to me that the very same issue affects the VA itself -
management look inside VistA and they see all this legacy Mumps code.
Their advisors, consultants etc all look at it too and roll their eyes
in horror, as do eager detractors and competitors from the sidelines.
Are any one of them aware that the code doesn't need to be like this?
Have any of them seen what modern Mumps code could look like as a
replacement for the legacy code?
Rather than sink the kinds of funds they are talking about into
rewriting in some other Open Source language, why not do the rewrite
using modern Mumps techniques that would produce code that was just as
readable as anything like Python etc can produce....but with the
advantage that it would automatically run alongside the old legacy
code and migration from old to new would be seamless. The
modernization report notes the need to fully understand the existing
logic, so an exercise of poring over all the legacy code and making
sense of it is recognised as a requirement anyway.
Even more reason, then, for your proposed exercise, perhaps? A real,
working illustration of legacy Mumps versus modern Mumps.
One last though: time, also, perhaps, to ditch the old protocols that
merely perpetuate a style of coding within VistA that belongs to a
bygone age?
RESPONSE # 16
Date: Sun, 6 Jun 2010 12:15:03 -0400
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: JohnLeo Zimmer <johnleozim@gmail.com>
On Sun, Jun 6, 2010 at 8:57 AM, rtweed <rob.tweed@gmail.com> wrote:
...snip
One last though: time, also, perhaps, to ditch the old protocols that
merely perpetuate a style of coding within VistA that belongs to a
bygone age?
..snip
For this exercise, I would require only clarity as our standard.
The VA SAC could support another discussion for another day.
GrampaZ
RESPONSE # 17
Date: Sun, 6 Jun 2010 09:36:45 -0700 (PDT)
Subject: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: "kdtop3@gmail.com" <kdtop3@gmail.com>
Zimmer,
As much as you all have heard me rail against old coding styles, I
think that going back and trying to re-write working code is a waste
of time.
I consider those code blocks to be same as a compiled .dll on a
windows machine. Sure I can break out by debugger and decompiler and
pick through what they do. But what it the point? As long as the
input and output are consistent and reliable, then I just use the code
as a black box. The same applies for %DTC.
We all know that %DTC *could* be written to back it easier to ready by
humans, but unless we are wanting the change the functionality, then
when we have put the hours of redoing the code, what have we achieved?
Instead, I would argue to put the effort into training a new
generation of mumps programmers to write code that is easier to
understand and extend. And I suspect that we are preaching to the
choir, as many of us know ARE coding this way. I have even notice
that George T. is coding more this way in his newer stuff.
But a fact of the matter (IMHO) is that mumps *is* a limited language,
compared to all the bells and whistles available in newer computer
languages. I consider it more of a database scripting language. If
I had my wishes, we would instantly convert all VistA into something
more modern. But there is no point to that line of thought. The
reason I learned mumps was to support VistA. Rob is pursuing new
business with mumps, and I wish him well (he will probably take
exception to my mumps assesment), but otherwise, I don't see any
purpose in trying to evangelize mumps. And if people want to hate on
M, then let them.
Kevin
RESPONSE # 18
Date: Sun, 06 Jun 2010 13:09:48 -0400
From: Joseph Dal Molin <dalmolin@e-cology.ca>
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
Given that the two code examples (M and Python) are not even related in
terms of functionality... how about simply finding a good example of
clearly written code that accomplishes what Rob Tweed is suggesting
instead of re-writing the example. This is not evangelizing IMHO.... it
is removing ammo for FUD flingers which is very important to
evangelizing VistA which we definitely should be doing and are....
J.
RESPONSE # 19
Date: Mon, 7 Jun 2010 08:55:37 -0400
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: JohnLeo Zimmer <johnleozim@gmail.com>
Kevin,
As I said above, I do not propose an attempt to <actually> replace the
working legacy version of ^%DTC that currently sits in VistA. This
would be an <<exercise>> to demonstrate that MUMPS is able to support
a more modern coding style focused on clarity and maintainability.
I would suggest that, like Rob, you have yourself contributed a number
of examples that could be used to show such a coding style is
possible. I think using ^%DTC as the starting point just helps define
the exercise... and would make it logical to add the winning design to
Wikipedia alongside the legacy version.
regards,
Zimmer
RESPONSE # 20
Date: Mon, 07 Jun 2010 10:27:10 -0400
From: "K.S. Bhaskar" <ks.bhaskar@fisglobal.com>
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
There are some changes that I would like to make to the attached, but I=20
would like to offer it as a starting point for a more modern looking M=20
program.
The changes I would like to make are divide up the range into blocks of=20
size n integers and then to have each of m processes work on the next=20
range of integers.
Regards
-- Bhaskar
Code:
threeen1e
; 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_12fzjpqbc2
; Assumes input format is 3 integers separated by a space with the first integer smaller than the second.
; Third integer is number of threads. No input error checking is done.
; This program is modified to show that the GT.M key-value store can use arbitrary strings for
; both keys and values - instead of numeric subscripts and values to store the numver of steps, each
; subscript and value is spelled out using the strings in the program source line labelled "digits".
;
; K.S. Bhaskar 20100228
;
; 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 uses strings in different languages for each digit in the database. It 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
;
; Read and process each line in turn. Since process may be restarting after a crash, any
; data in database is assumed to be as recovered after a crash. After computing and writing
; results from this run, 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 - starting number is first piece of input
. Set j=$Piece(input," ",2) ; j - ending number is second piece of input
. Set k=$Piece(input," ",3) ; k - parallel execution streams requested is third piece
. ; Reproduce input on output, formatting numbers
. Write $FNumber(i,",",0)," ",$FNumber(j,",",0)," ",$FNumber(k,",",0)
. ;
. ; Get number of CPUs, calculate number of parallel execution streams, calculate block size
. Open "cpus":(COMMAND="grep -i ^processor /proc/cpuinfo|wc -l":READONLY)::"PIPE"
. Use "cpus" Read cpus Use $PRINCIPAL ; Number of CPUS on system is in /proc/cpuinfo
. Close "cpus"
. Set:4*cpus>k k=4*cpus ; At least four execution streams per CPU
. Write " ",k ; Report actual number of execution streams
. Set blk=(j-i+k)\k ; Calculate size of blocks (last block may be smaller)
. ;
. ; 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=i:blk:j Do
.. Set c=$Increment(^count) ; Atomic increment of counter in database for process synchronization
.. Set tmp=$Select(s+blk-1>j:j,1:s+blk-1) ; Compute upper limit for numbers for next child Job
.. 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(s,tmp,i,j):(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(myfirst,mylast,allfirst,alllast)
Set reads=0,updates=0,highest=alllast ; Start with zero reads and zero writes, highest number is at least alllast
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
Do dostep(myfirst,mylast) ; Do the block assigned to this process
Do:alllast>mylast dostep(mylast+1,alllast) ; Help with the numbers after this process' block, if there are any
Do:allfirst<myfirst dostep(allfirst,myfirst-1) ; Help with the numbers before this process' block, if there are any
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
RESPONSE # 21
Date: Mon, 7 Jun 2010 08:44:37 -0600
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: drew einhorn <drew.einhorn@gmail.com>
When changing the Wikipedia page:
http://en.wikipedia.org/wiki/MUMPSYou should also look at and consider its discussion page:
http://en.wikipedia.org/wiki/Talk:MUMPSAnd the archived copy of earlier discussions:
http://en.wikipedia.org/wiki/Talk:MUMPS/Archive01Given Wikipedia's strong preference for citations of published
material from reliable sources over "original material" (which is
strongly deprecated in the Wikipedia). You should publish your
"original" examples elsewhere, perhaps in a tutorial section on the
hardhats website, and cite them in your revised wikipedia article. In
the meantime you should cite and discuss examples of contemporary,
well written M code in VistA, WorldVistA, OpenVista, and/or other open
source distributions, etc.
Drew Einhorn
"You can see a lot by just looking."
-- Yogi Berra
RESPONSE # 22
Date: Mon, 07 Jun 2010 10:48:55 -0400
From: "K.S. Bhaskar" <ks.bhaskar@fisglobal.com>
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
On the web, it's easy to publish references to quote! See an earlier
version of the program at
http://docs.google.com/View?id=3Ddd5f3337_15hssw6=bdhRegards
-- Bhaskar
RESPONSE # 23
Date: Mon, 7 Jun 2010 09:04:48 -0600
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: drew einhorn <drew.einhorn@gmail.com>
On Mon, Jun 7, 2010 at 8:48 AM, K.S. Bhaskar <ks.bhaskar@fisglobal.com> wro=
te:
> On the web, it's easy to publish references to quote! See an earlier
> version of the program at
http://docs.google.com/View?id=3Ddd5f3337_15hss=w6bdh>
It would be even better to "publish" it in a place that the Wikipedia
would consider a reliable, authoritative source such as a hardhats
tutorial, a VA publication, an opensource project with significant
adoption, etc. Better yet a collection of citations from several of
the above.
This is only a bit more difficult, and will result in a revision to
the wikipedia that is more likely to survive the wikipedia edit wars.
Publishing the referenced article on a respected site would be far
more likely to get the respect of the wikipedia's editors, than it's
current status as an anonymous google doc with no signature or any
other identification of its author and the author's credentials.
RESPONSE # 24
Date: Mon, 7 Jun 2010 12:13:49 -0400
Subject: Re: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: Steven McPhelan <smcphelan@alumni.uci.edu>
Why? Hardly anyone use %DTC in their code today when they have the same
functionality in the XLFDT utilities. It seems to me that the intent of
this original message thread is available in the XLFDT utilities. I do not
know the number of years it has been since I last actually used ^%DTC.
--
Steve
"Is life so dear or peace so sweet as to be purchased at the price of chains
and slavery? Forbid it, Almighty God! I know not what course others may
take, but as for me, give me liberty, or give me death!" - Patrick Henry
RESPONSE # 25
Date: Mon, 7 Jun 2010 13:31:06 -0700 (PDT)
Subject: [Hardhats] Re: Was: Anyone care to comment on this? Refactoring
exercise for SCM
From: OldMster <msires@verizon.net>
I think several are missing the point. John is proposing an exercise
that will only be a tool to debunk the myth that 'mumps is too hard to
learn' or 'mumps is too hard to support'. What he proposes would be a
tool to show that old Mumps code was written that way for a reason -
not because the language required it, but because the environment
(hardware, O/S, etc) required it.
I also agree with Kevin that Mumps has always been a database
scripting language, and in that role it has no peer (in my less than
humble opinion). It had user IO capabilities because it had to (at
the time), and the fact that Open and Use command parameters were
never part of the standard (how to specify them was, but the actual
parameters were implementation specific) contributes to this
argument. In today's world, using other technologies for the 'user'
end, and continuing to use Mumps for the database manipulation is a
very powerful and supportable system.
As an aside, I have usually silenced those preaching 'mumps is too
hard to learn' with the comment that I wasn't sure I'd hire someone as
a programmer that found learning 26 command ands and 26 functions a
difficult task. Yes, I know there aren't exactly 26 of each, but the
number is close, and given that almost all can be abbreviated to a
single character it is an easy number to use. The 'mumps is too hard
to support' argument is more difficult, given that Mumps programmers
(me included) have perpetuated the old style long beyond the point
where it actually was useful. My personal shop rule revolves around
how fast I can figure out what is going on at 3:00am - If it takes me
too long to figure it out, you're fired!
Mark
RESPONSE # 26
Date: Fri, 11 Jun 2010 08:54:18 -0700
From: "Frederick D. S. Marshall" <rick.marshall@vistaexpertise.net>
Subject: [Hardhats] MUMPS Refactoring Exercise [Was: Anyone care to comment on
this? Refactoring exercise for SCM]
Dear Kevin,
Like you I see little point in trying to evangelize MUMPS. I generally
agree that if people want to hate on MUMPS we should let them. Our job
isn't public relations; it's VISTA.
There are, though, reasons why refactoring %DTC is more than an exercise
in PR. The most important reasons to refactor dense code like this into
a more readable style are so the person doing the refactoring can (a)
improve their mastery of MUMPS, which is not as simple a language as it
seems on the surface, and (b) above all improve their mastery of the
specific VISTA package containing the dense code.
A) All programming languages are limited, and yes, this includes MUMPS,
whose limitations grate most on those of us used to other languages. But
so too is haiku limited, yet who would deny the greatness of that form
of poetry and spend their time railing against its extraordinarily
strict limitations. MUMPS is freedom itself compared to haiku, and is
capable of expressing equally profound things within its chosen domain
of medical informatics, which is why MUMPS dominates medical software.
Becoming great with MUMPS requires more than noticing those limitations.
It also requires learning to use them, learning how the joints of the
language articulate between those points of limitation, learning how to
use those limitations to create the idiom of your own fluent personal
MUMPS programming style.
Developing from MUMPS journeyman to mastery very much comes from
exercises like the one Doctor Z has proposed here, from mastering
difficult pieces of code by transforming them into our personal MUMPS
styles. A MUMPS journeyman is someone who has studied enough and
practiced enough to be capable of doing exercises like this. A MUMPS
master is someone who has already done this so many times that they've
built up a library of MUMPS code that they've created or refactored into
their own style that together accomplishes something impressive and
worthy, a masterwork. That journey from journeyman to mastery changes
each of us from someone who gags at the thought of having to edit a
routine like %DTC to someone who instead grins and rolls up his sleeves.
B) Part of any true transfer of VISTA authority from one generation to
the next is accomplished by having the new generation re-express the
complete algorithm of the package in place, in MUMPS, in their own
style, to make it all maximally readable to them personally (not just
generally). This compels them to sift through the code - all of the code
- until they understand every line, every variable, and every loop,
decision, and branch in the flow of control. That degree of
understanding of a package cannot be acquired any other way and is the
only secure foundation for the emerging VISTA renaissance, for the grand
projects to come.
When a hundred new programmers immerse themselves in VISTA's hundred
packages, we'll be well on our way back to having VISTA under effective
development, to being capable of making VISTA bloom spectacularly again.
Where the VA has gone wrong for years now is above all in trying to rush
to the exciting projects without first investing in the hard work of
remastering the code, and the results speak for themselves. Trying to
remove or replace things we don't understand is where a surprising
percentage of history's truly terrible results come from - and those of
us who have mastered our chosen areas of VISTA learn enough to
understand that they should not be removed or replaced when they can so
much more easily, cheaply, and safely be refactored and refined. Our
focus needs to be not so much on the code and how refactoring changes
it, but on the programmers involved and what refactoring the code
teaches them. That is more than a worthy investment - helping them
become tomorrow's VISTA masters needs to be our top priority as a community.
I agree with Steve McPhelan (as usual) that most of us don't use %DTC to
do our date conversions any more, because Wally Fort's XLFDT function
library does the same things using true parameter passing, functions,
and variable scoping. Nevertheless, %DTC is in active use throughout the
existing VISTA code, so it needs to be understood, and its very density
makes it a more useful exercise in refactoring than the XLFDT library.
I agree to the Doctor Z challenge, and I would encourage as many other
Mumpsters as possible out there to take it on, too. For my Paideia
students, let's make this our next joint homework assignment together,
to get things started back up again in anticipation of this summer's new
classes. Let's exercise our refactoring muscles and create a library of
%DTC variations that demonstrates many different styles in which we can
program in MUMPS.
To help protect that diversity, rather than posting the results publicly
just yet, please e-mail them to me and Doctor Z privately. Reply to this
Hardhats e-mail if you agree to the challenge and again when you're done
without posting the actual result publicly, so people can follow the
progress of the challenge. I'll collect and catalog them with Doctor Z,
and then on July 11th we'll post them all on Vistapedia for review and
reply here to relaunch the discussion.
Who else is up to the challenge?
Yours truly,
Rick
RESPONSE # 27
Date: Fri, 11 Jun 2010 09:02:26 -0700
From: "Frederick D. S. Marshall" <rick.marshall@vistaexpertise.net>
Subject: Re: [Hardhats] MUMPS Refactoring Exercise [Was: Anyone care to
comment on this? Refactoring exercise for SCM]
I'll just add that anyone wanting to tackle this project should have
handy the VA Fileman Programmer Manual to refer to the definitions of
the public supported entry points this routine contains.
RESPONSE # 28
Date: Fri, 11 Jun 2010 09:13:25 -0700
From: "Frederick D. S. Marshall" <rick.marshall@vistaexpertise.net>
Subject: Re: [Hardhats] MUMPS Refactoring Exercise [Was: Anyone care to
comment on this? Refactoring exercise for SCM]
To help anyone who wants to get started, here is DIDTC as a text file
with tabs for the line starts to improve legibility. Good luck.
Code:
DIDTC ;SFISC/XAK-DATE/TIME OPERATIONS ;20AUG2009
;;22.0;VA FileMan;**14,36,71,117,1035**;Mar 30, 1999
;Per VHA Directive 10-93-142, this routine should not be modified.
D N %T
I 'X1!'X2 S X="",%Y=0 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 N %,%T,%Y
S X=X1,X2=+$G(X2) I 'X S (X,%H)="" Q
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 ;called from DIG, DIP4
I X<1410000 S (%H,%T)=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 N DILEAP D
. N Y S Y=%Y+1700 S:%M<3 Y=Y-1
. S DILEAP=(Y\4)-(Y\100)+(Y\400)-446 Q
S %H=$P("^31^59^90^120^151^181^212^243^273^304^334","^",%M)+%D
S %=('%M!'%D),%Y=%Y-141
S %H=(%H+(%Y*365)+DILEAP+%),%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 I '%H S (%,X)="" Q
S %=(%H>21608)+(%H>94657)+%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 ;called from DIV, etc
D YMD S Y=X_% Q:Y="" G DD^%DT
;
YMD ;called from DIP5. Documented entry point for converting a date/time %H in $H format into a date (in X) and time (in %) in FileMan internal format.
I %H[",0" S %=%H N %H S %H=%-1_",86400"
N %D,%M,%Y D 7 S %=$P(%H,",",2) D S
Q
;
;
T ;from %DT
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?1.N1"M" S %H=$H D MONTH G D^%DT
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 ;from %DT
F %=2:1 S Y=$E(X,%) Q:"+-"[Y G 1^%DT:$E("NOW",%)'=Y
I Y="" S %H=$H D %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
I X?1.N1"'" S X=X*60,%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
I X?1.N1"M" S %H=$H D %H,MONTH G RT1
D DMW G 1^%DT:'% S @("%H=$H"_Y_%),%H=%H_","_$P($H,",",2) D %H
RT D TT
RT1 S %=$P(%H,",",2) D S S %=X_$S(%:%,1:.24) I %DT'["S" S %=+$E(%,1,12)
Q:'$D(%(0)) S Y=% G E^%DT
;
;
PF ;from %DT
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
;
;
MONTH ;Add months to current date
S Y=Y_+X
D TT
S %=%I(1)+Y,%I(1)=%-1#12+1,%I(3)=%I(3)+(%-$S(%>0:1,1:12)\12)
S %="31^"_($$LEAP(%I(3))+28)_"^31^30^31^30^31^31^30^31^30^31"
I %I(2)>$P(%,U,%I(1)) S %I(2)=$P(%,U,%I(1))
S X=%I(3)_"00"+%I(1)_"00"+%I(2)
Q
;
LEAP(X) ;Return 1 if leap year
S:X<1700 X=X+1700
Q '(X#4)&(X#100)!'(X#400)
;
TT N %M,%D,%Y D 7 S %I(1)=%M,%I(2)=%D,%I(3)=%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
;
%H I '$P(%H,",",2) S %H=%H-1 Q
I $P(%H,",",2)<60&(%DT'["S") S $P(%H,",",2)=60
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 %
I %DT["M" D G 0
. S A1=" "_$S(%DT["I":1.1957,1:"JAN 1957 or JAN 57")_$S(%DT'["N":" or 0157",1:"") D %
. S A1=" T (for this month)" D %
. S A1=" T+3M (for 3 months in the future)" D %
. S A1=" T-3M (for 3 months ago)" D %
. S A1="Only month and year are accepted. You must omit the precise day." D %
S A1=" "_$S(%DT["I":"20.1.1957",1:"JAN 20 1957 or 20 JAN 57")_" or "_$S(%DT["I":"20/1",1:"1/20")_"/57"_$S(%DT'["N":" or "_$S(%DT["I":200157,1:"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 " D D %
. I %DT["P" S A1=A1_"assumes a date in the PAST." Q
. I %DT["F" S A1=A1_"assumes a date in the FUTURE." Q
. S A1=A1_"uses CURRENT YEAR. Two digit year" D %
. S A1=" assumes no more than 20 years in the future, or 80 years in the past."
. Q
I %DT'["X" S A1="You may omit the precise day, as: "_$S(%DT["I":1,1:"JAN,")_" 1957" D %
I %DT'["T",%DT'["R" G 0
S A1="If only the time is entered, the current date is assumed." D %
S A1="Follow the date with a time, such as "_$S(%DT["I":"20.1",1:"JAN 20")_"@10, T@10AM, 10:30, etc." D %
S A1="You may enter a time, such as NOON, MIDNIGHT or NOW." D %
S A1="You may enter NOW+3' (for current date and time Plus 3 minutes" D %
S A1=" *Note--the Apostrophe following the number of minutes)" 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
Q
RESPONSE # 29
Date: Fri, 11 Jun 2010 10:58:13 -0700 (PDT)
Subject: [Hardhats] Re: MUMPS Refactoring Exercise [Was: Anyone care to
comment on this? Refactoring exercise for SCM]
From: Joel <joelivey@gmail.com>
Rick, I have accepted your challenge, used XTMREDO on it, and e-mailed
you the result.
RESPONSE # 30
Date: Thu, 17 Jun 2010 05:57:54 -0700
Subject: Re: [Hardhats] Re: MUMPS Refactoring Exercise [Was: Anyone care to
comment on this? Refactoring exercise for SCM]
From: Sam Habiel <sam.habiel@gmail.com>
What's "XTMREDO"? I don't have it in the latest worldvista (I am testing
the 3-09 version).
Sam
RESPONSE # 31
Date: Thu, 17 Jun 2010 12:19:09 -0500
Subject: Re: [Hardhats] Re: MUMPS Refactoring Exercise [Was: Anyone care to
comment on this? Refactoring exercise for SCM]
From: David Whitten <whitten@worldvista.org>
Joel is amazing as you already know. This is some of his handiwork...
Code:
XTMREDO ;JLI/FO-OAK-ROUTINE TO MAKE ROUTINES EASIER TO READ ;09/18/09 13:16
;;7.3;TOOLKIT;**to be determined**;
;
; The code in this routine is based on that in routine XINDX8,
; it has simply been modified to make the Structured Format listing
; into an actual routine. Conversion should not alter the
; functioning of the routine.
;