How can I make this Racket DRYer code?

I am porting a Python script to Racket as a learning experience, and I have this function:

(define (check-status) (define git [find-executable-path "git"]) (define-values (ckot out in err) (subprocess #f #f #f git "checkout" "-q" "master")) (define-values (local lout lin lerr) (subprocess #f #f #f git "rev-parse" "@")) (define-values (remote rout rin rerr) (subprocess #f #f #f git "rev-parse" "@{u}")) (define-values (merge-base mbout mbin mberr) (subprocess #f #f #f git "merge-base" "@" "@{u}")) (display-lines (port->lines mbout)) (define ports '(ckot out in err local lout lin lerr remote rout rin rerr merge-base mbout mbin mberr)) (map (lambda (x) (cond ((input-port? x) (close-input-port x)) ((output-port? x) (close-output-port x)))) ports)) 

The problem is that it is not very DRY. Since I use Lisp, and Lisp is known for being capable of doing crazy things, I want to know if there is a way to take all the subprocess code and extract it so that I can do something like:

 (define (check-status) (define commands '( '("checkout" "-q" "master") '("rev-parse" "@") '("rev-parse" "@{u}") '("merge-base" "@" "@{u}")) (map currently-immaginary-git-command-fn commands)) 

and in the end you get a list of the results of each team in the list of teams. How can I do it? Since I'm new to all Lisp / Scheme work, I figure out the syntax when I go, and I'm not completely aware of the resources available to me.

+6
source share
1 answer

First of all, it’s good for you, wanting to come up with a cleaner solution! You are right that there is a more elegant way to do what you tried.

For starters, using subprocess almost certainly be redundant in your particular use case. racket/system module provides a simpler interface that should be sufficient for your needs. In particular, I would use the system* function, which executes one process with the arguments provided, and then prints its output to stdout.

Using system* , you can create a very general helper function that can execute a command for a specific executable file and returns its output as a string.

 (define (execute-command proc-name) (define proc (find-executable-path proc-name)) (Ξ» (args) (with-output-to-string (thunk (apply system* proc args))))) 

This function itself returns a new function when it is called. This means that using it to invoke the Git command will look like this:

 ((execute-command "git") '("checkout" "-q" "master")) 

The reason for this will soon become apparent.

Actually, looking at the implementation of execute-command , we use with-output-to-string to redirect all the output from the system* call to a string (instead of just printing it to standard output). This is actually an abbreviation for using parameterize to set the current-output-port parameter, but it is simpler.

With this function, we can easily implement check-status .

 (define (check-status) (define commands '(("checkout" "-q" "master") ("rev-parse" "@") ("rev-parse" "@{u}") ("merge-base" "@" "@{u}"))) (map (execute-command "git") commands)) 

Now the reason for the return (execute-command "git") new function becomes apparent: we can use it to create a function that will then be displayed on the commands list to create a new list of lines.

Also note that the definition of the commands list uses only one ' at the beginning. The definition you provided will not work, and in fact, the ports list that you defined in your initial implementation is not what you expect. This is because '(...) not exactly the same as (list ...) - they are different, so be careful when using them.

+6
source

Source: https://habr.com/ru/post/988464/


All Articles