[racket] Use of map and eval to evaluate symbol in namespace

From: Henry Lenzi (henry.lenzi at gmail.com)
Date: Sun Aug 3 23:04:35 EDT 2014

Oh, OK. :-)

-- Henry

On Sun, Aug 3, 2014 at 9:38 PM, Alexander D. Knauth
<alexander at knauth.org> wrote:
>
> On Aug 3, 2014, at 7:58 PM, Henry Lenzi <henry.lenzi at gmail.com> wrote:
>
>> Alexander's idea is interesting, but it onlt works if the
>> prescription file is not numbered (which is actually more natural),
>> such as if it were:
>> hctz25 30 pl 1xd
>> simva20 30 pl 1xn
>>
>>> (define in2 (open-input-file "Recipe3.txt"))
>>> (port->list (compose1 eval read) in2)
>> '("Hydrochlorothiazide 25mg"
>>  30
>>  "cps"
>>  "Take 1 pill P.O. 1x/day"
>>  "Simvastatin 20mg"
>>  30
>>  "cps"
>>  "Take 1 pill P.O. 1x at night")
>>
>> The issue would then be about extracting and joining 4 or 5 (if it has
>> an INST instruction) items from that list.
>> string-join, however, will bork at numbers. So it's kind of the same
>> issue as previously than with |30|.
>
> Then just do this: (string-join (map ~a (port->list (compose1 eval read) in2)) instead.
>
>>
>> in what regards the presence of INSTs, maybe this could be approached
>> by first scanning the list for an INST instruction using REGEXPs, but
>> I don't know how to do that yet.
>>
>>
>> Thanks,
>>
>> Henry Lenzi
>>
>> On Sun, Aug 3, 2014 at 5:40 PM, Alexander D. Knauth
>> <alexander at knauth.org> wrote:
>>>
>>> On Aug 3, 2014, at 4:29 PM, Alexander D. Knauth <alexander at knauth.org> wrote:
>>>
>>>>
>>>> On Aug 3, 2014, at 3:29 PM, Henry Lenzi <henry.lenzi at gmail.com> wrote:
>>>>
>>>>> ; Hello all --
>>>>> ; So here's how I solve all those little problems regarding symbols
>>>>> and evaluation of medication definitions.
>>>>> ; Would you please bear with me? I apologize for the length.
>>>>> ; This is the approach I've taken. I've chosen no to use any macrology
>>>>> or parser/lexer technique because I don't grok them and they
>>>>> ; don't really seem necessary, for reasons explained in the code comments.
>>>>> ; I have not decided to hash tables, for the following reason: there's
>>>>> a part of the code (the drug definitions, the instructions), that
>>>>> ; should be easy enough for non-programmers to edit. If they are kept
>>>>> very simple, it's possible, because the users have to edit those
>>>>> ; files. So, even though it is source code, it's not as intimidating
>>>>> as editing source code if hash tables.
>>>>> ; Another aspect is that I hope modules provided some sort of safety
>>>>> in terms of syntax checking. That is to say, if you used make a
>>>>> ; typo in the medication part of the DSL, the system will (hopefully)
>>>>> bork because no such module exists. I believe this also creates
>>>>> ; an opportunity for "syntax validation" if a proper input phase is
>>>>> designed. But Lisp/Scheme being a dynamic language, the run-time
>>>>> ; will bork immediately once it sees funny things. This is a way to
>>>>> guarantee the DSL is correct, which we get for free by using Racket.
>>>>> ; A fourth aspect is that, if each drug is kept a different module
>>>>> (which I haven't done here, BTW), then we can make for easier
>>>>> ; internationalization, by keeping modules by languages, e.g.,
>>>>> hctz25-en, hctz25-pt_br. I believe Dan has an interest in this project
>>>>> too,
>>>>> ; so  it's best to design with that in mind.
>>>>> ; Final comment regards "database". We get "database" for free, by
>>>>> registering prescriptions with patient register numbers. The OS
>>>>> ; takes care of pretty musch anything else. And there's no need for
>>>>> atomicity and concurrency. Like I said, this is stupid code.
>>>>> ;
>>>>> ;
>>>>> #lang racket
>>>>>
>>>>> ; code-review-for-racketeers-2014-08-03-a.rkt
>>>>> ;
>>>>> ; For this exercise, suppose a Recipe.txt file. Let´s suppose the idea
>>>>> is that the physician
>>>>> ; has two options: 1) he or she opens Notepad and writes the
>>>>> prescription file (Recipe.text);
>>>>> ; 2) or, the software asks for inputs and writes the file (this will
>>>>> not be covered in this
>>>>> ; exercise). The written prescription in the shorthand DSL would look
>>>>> like below, with the
>>>>> ; exception of a first field with patient ID data not included (to be
>>>>> done later).
>>>>> ; The prescription has a rigid syntax would look like this (line
>>>>> breaks included):
>>>>> ; 1-
>>>>> ; hctz25 30 pl 1xd
>>>>> ;
>>>>> ; 2-
>>>>> ; simva20 30 pl 1xn
>>>>>
>>>>>
>>>>> ; Needed for EVAL, used later on
>>>>> (define-namespace-anchor a)
>>>>>
>>>>> ; These definitions should be in a different module.
>>>>> ; This way we get syntax checking for free.
>>>>> ; MED - medication. Includes dosage.
>>>>> (define hctz25 "Hydrochlorothiazide 25mg")
>>>>> (define simva20 "Simvastatin 20mg")
>>>>> ; FORM - whether the patient will take home pills, a tube, a flask, capsules
>>>>> (define pl "pills")
>>>>> ; POS - posology, whether the patient will take 1 pill 3x a day, or 2
>>>>> pills 2x a day, etc.
>>>>> (define 1xd "Take 1 pill P.O. 1x/day")
>>>>> (define 1xn "Take 1 pill P.O. 1x at night")
>>>>> ; INSTs - special instructions. INST is just a prefix INST+MED without
>>>>> the dosage.
>>>>> (define INSTOMZ "half an hour before breakfast, with a glass of water")
>>>>> ; Formatters - simple for now, but should be a function of the space available.
>>>>> (define  line "-----------")
>>>>>
>>>>>
>>>>>
>>>>> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>>>>> ; The main part of a prescription DSL is pretty rigid in syntax, being
>>>>> composed of blocks of theses parts:
>>>>> ; MEDICATION QUANTITY FORM POSOLOGY INSTRUCTION, or MED QUANT FORM POS INST.
>>>>> ; Please note that, in this DSL, the MED part includes the drug dosage
>>>>> (e.g., HCTZ25, where
>>>>> ; the HCTZ designates the drug, and the 25 the dosage).
>>>>> ; An example would be:
>>>>> ; HCTZ25 30 PL 1XD
>>>>> ; meaning: Hydrochlorothiazide 25mg -------------- 30 pills
>>>>> ;                Take 1 pill P.O. 1X day
>>>>> ; INST are special instructions. They basically are more detailed
>>>>> explanation to the patient about
>>>>> ; how to use the medication properly. Not always there's a INST in the
>>>>> prescription DSL.
>>>>> ; INSTs are, in fact, a PREFIX for the MED without the dose. For
>>>>> example, OMZ20 is Omeprazol 20mg.
>>>>> ; The instruction for OMZ would be INSTOMZ ("half an hour before
>>>>> breakfast, with a glass of water").
>>>>> ; In this case, the DSL line would be:
>>>>> ; OMZ20 30 PL 1XD INSTOMZ
>>>>> ; meaning: Omeprazol 20mg ------------------- 30 pills
>>>>> ;               Take 1 pill P.O. 1X day
>>>>> ;               half an hour before breakfast, with
>>>>> ;               a glass of water
>>>>> ; Questions regarding proper formatting of INST are not addressed at
>>>>> this moment.
>>>>> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>>>>> ; Now follows a description of some problems I encountered and the
>>>>> choices made in solving them:
>>>>> ; (define in (open-input-file "Recipe.txt"))
>>>>> ; If you just (string-split (read-line in)) you'll get:
>>>>> ; => '("hctz25" "30" "cp" "1xd")
>>>>> ; and that will not evaluate the symbols to their string descritptions.
>>>>> ; Because of that, you need to do a:
>>>>> ; > (map string->symbol (string-split (read-line in)))
>>>>> ; which will evaluate to
>>>>> ; => '(hctz25 |30| cp 1xd)
>>>>> ; This would be ideal to MAP EVAL to, but the problem is the |30|
>>>>
>>>> What you want here is something like this:
>>>> ;; Instead of (map string->symbol (string-split (read-line in)))
>>>> (for/list ([thing (in-port read in)]) thing)
>>>
>>> Actually come to think of it you can do this:
>>> (sequence->list in)
>>> Or this:
>>> (port->list read in)
>>>
>>>> ;; and then you can do (map eval …) to that if you want.
>>>> ;; Or you could do both at once like this:
>>>> (for/list ([thing (in-port read in)])
>>>> (eval thing namespace))
>>>
>>> Or for that:
>>> (port->list (compose1 eval read) in)
>>>
>>>>
>>>>> ; So, the idea is SET!ing that list to a name we can call easily, i.e.,
>>>>> ; med-line-holder, because then we can extract the pieces (since we
>>>>> can't do list
>>>>> ; surgery easily, such a "replace the the element at position 1 with
>>>>> so-and-so element”).
>>>
>>> look at list-set from unstable/list
>>>
>>>>> ; Since the prescription syntax is pretty rigid, we can get away with this
>>>>> ; simple approach.
>>>>>
>>>>> (define med-line-holder '()) ; initial value of med-line-holder is an empty list
>>>>> (define med-name-holder '())
>>>>> (define med-quant-holder '())
>>>>> (define med-form-holder '())
>>>>> (define med-pos-holder '())
>>>>> (define med-inst-holder '()) ; remember, not always INSTructions
>>>>> happen in a DSL prescription .
>>>>>
>>>>> (define in (open-input-file "Recipe.txt"))
>>>>> (port-count-lines! in)
>>>>> (define (clpr) (close-input-port in))
>>>>>
>>>>> ; a med-line-holder is a list that has MED QUANT FORM POS (and sometimes INST)
>>>>> ; This is obtained from a plain text file. When it is read, it becomes something
>>>>> ; like this: '(hctz25 |30| cp 1xd)
>>>>> (define (set-med-line-holder)
>>>>> (set! med-line-holder (map string->symbol (string-split (read-line in)))))
>>>>>
>>>>> (define (set-med-name-holder)
>>>>> ; (set! med-name-holder (eval (car med-line-holder))) ;; in the REPL
>>>>> (set! med-name-holder (eval (car med-line-holder)
>>>>> (namespace-anchor->namespace a))))
>>>>>
>>>>> (define (set-med-quant-holder) ; the CADR of the med-line-holder
>>>>> ; (set! med-quant-holder (eval (symbol->string (cadr med-line-holder))))
>>>>> (set! med-quant-holder (eval (symbol->string (cadr med-line-holder))
>>>>> (namespace-anchor->namespace a))))
>>>>>
>>>>> (define (set-med-form-holder) ; the CADDR of the med-line-holder -
>>>>> gets the FORM, e.g., pills, etc.
>>>>> ; (set! med-form-holder (eval (symbol->string (caddr med-line-holder))))
>>>>> (set! med-form-holder (eval (caddr med-line-holder)
>>>>> (namespace-anchor->namespace a))))
>>>>>
>>>>> (define (set-med-pos-holder) ; the CADDDR of the med-line-holder -
>>>>> gets the POS, e.g., 1xd
>>>>>  ; (set! med-pos-holder (eval (symbol->string (cadddr med-line-holder))))
>>>>> (set! med-pos-holder (eval (cadddr med-line-holder)
>>>>> (namespace-anchor->namespace a))))
>>>>>
>>>>>
>>>>> (define (set-med-inst-holder) ; the LAST of the med-line-holder - gets the INST
>>>>>  ; (set! med-pos-holder (eval (symbol->string (last med-line-holder))))
>>>>> (set! med-pos-holder (eval (last med-line-holder)
>>>>> (namespace-anchor->namespace a))))
>>>>>
>>>>> ; One problem here regards the optional INST instructions.
>>>>> ; How to create a SETter function that will only SET! med-inst-holder
>>>>> ; if there's an INST instruction? Note that INST is a prefix. A real
>>>>> instruction is, e.g.,
>>>>> ; INSTOMZ (for OMZ20).
>>>>> (define (look-for-line)
>>>>> (if (regexp-match #px"\\d\\-" (read-line in))
>>>>>    (begin
>>>>>      (set-med-line-holder)
>>>>>      (set-med-name-holder)
>>>>>      (set-med-quant-holder)
>>>>>      (set-med-form-holder)
>>>>>      (set-med-pos-holder))
>>>>>    'NO-LINE))
>>>>>
>>>>> (define (display-stuff)
>>>>> (newline)
>>>>> (display med-line-holder) (newline)
>>>>> (display med-name-holder) (newline)
>>>>> (display med-quant-holder) (newline)
>>>>> (display med-form-holder) (newline)
>>>>> (display med-pos-holder) (newline))
>>>>> ; The problem remains of what to do with the eventual INST.
>>>>>
>>>>>
>>>>> ; Successive calls to (look-for-line) would read the next lines.
>>>>> ; Output would alternate between a DSL line, or a NO-LINE (from look-for-line,
>>>>> ; if it hits a line with no text in Recipe.txt
>>>>> (look-for-line)
>>>>> ;(display-stuff)
>>>>>
>>>>>
>>>>> (define (output-a-line)
>>>>> (string-join (list med-name-holder line med-quant-holder med-form-holder "\n"
>>>>>                           med-pos-holder "\n")))
>>>>>
>>>>> (define (format-a-line)
>>>>> (display (output-a-line)))
>>>>>
>>>>> ;(define (output-a-line)
>>>>> ; (display (string-join (list med-name-holder line med-quant-holder
>>>>> med-form-holder "\n"
>>>>> ;                             med-pos-holder "\n"))))
>>>>> (newline)
>>>>> ;(output-a-line)
>>>>>
>>>>> (format-a-line)
>>>>>
>>>>>
>>>>>
>>>>> ; PROBLEMS
>>>>> ; 1) How do we find out how many lines to (look-for-line)?
>>>>> ;    This is one of the resons I specified the "1-", "2-" in the Recipe.txt. Not
>>>>> ;    only it makes for easy visual understanding, but it may be used
>>>>> to provide a hint
>>>>> ;    for this problem.
>>>>> ;    Possible approaches:
>>>>> ;    - Maybe this can be solved with REGEXPS? This information could
>>>>> provide a sentinel
>>>>> ;      variable for an iterator function?
>>>>> ;    - Is there some sort if line counting function? (Note that I have set
>>>>> ;      (port-count-lines! in) somewhere above in the code.
>>>>> ; 2) How do we know we've reached the end of the file?
>>>>> ; 3) How to deal with the not-always-present INST?
>>>>> ;    - How do we check for INSTs? With a REGEXP?
>>>>> ;    - Choosing between INSTs with REGEXPS is not necessary, as they
>>>>> will be loaded in a module,
>>>>> ;      so the system will "know" which one to choose.
>>>>> ; 4) Another idea would be "slurp" the whole of the prescription, and
>>>>> then deal with evaluation. How?
>>>>> ; (define f1
>>>>> ;    (file->string
>>>>> ;   "C:\\Path\\to\\sources\\Recipe.txt"))
>>>>> ;> (string-normalize-spaces f1)
>>>>> ;"1- hctz25 30 pl 1xd 2- simva20 30 pl 1xn"
>>>>> ;
>>>>> ; That's all for now, folks!
>>>>> ; Many thanks for all the help so far, Racketeers!
>>>>> ; Cheers,
>>>>> ; Henry Lenzi
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Sat, Aug 2, 2014 at 12:44 AM, Henry Lenzi <henry.lenzi at gmail.com> wrote:
>>>>>> Hello everyone -
>>>>>>
>>>>>> First of all, a big Thank You to all of you and for taking the time for
>>>>>> responding.
>>>>>>
>>>>>> I'll have to set aside sometime during this weekend to see if I can
>>>>>> understand the ideas you've been so kind to offer.
>>>>>>
>>>>>> However, I should confess that I've made some progress with way simpler
>>>>>> stuff which I hope to post later on.  Like I've said, this is stupid
>>>>>> software. Anyways, none of this is final.
>>>>>>
>>>>>> It really just used a plain text solution, since the format if a recipe is
>>>>>> so rigid. The question of expanding the symbols from files to run-time was
>>>>>> easier than I thought.
>>>>>>
>>>>>> The idea of using modules might have the nice collateral effect if some sort
>>>>>> of primitive type (or syntax) checking for free. I like the idea someone
>>>>>> offered of using modules for medication definitions. Actually, one module
>>>>>> per definition makes it very easy for future users to add new medications.
>>>>>> The ease of syntax is important because it allows for the customization by
>>>>>> non-sophisticated users (physicians, nurses).
>>>>>>
>>>>>> Cheers,
>>>>>> Henry Lenzi.
>>>>>
>>>>> ____________________
>>>>> Racket Users list:
>>>>> http://lists.racket-lang.org/users
>>>>
>>>>
>>>> ____________________
>>>> Racket Users list:
>>>> http://lists.racket-lang.org/users
>>>
>>
>> ____________________
>>  Racket Users list:
>>  http://lists.racket-lang.org/users
>


Posted on the users mailing list.