Delete non-empty directory in Racket programming language - racket

How to delete a non-empty directory in Racket?
When I invoked this api (delete-directory dir), in which dir is "/Users/apple/Library/Application Support/aoi", I was told that:
exn found:#(struct:exn:fail:filesystem:errno delete-directory: cannot delete directory
path: /Users/apple/Library/Application Support/aoi
system error: Directory not empty; errno=66 #<continuation-mark-set> (66 . posix))
For context, this is the rest of my code:
(define lib-folder '("/Users/apple/Library/Application Support/aoi"))
(define (del-lib-folder)
(for-each (λ (p)
(printf "del lib folder:~a~n" p)
(if (directory-exists? p)
(delete-directory p)
(printf "folder does not exist:~a~n" p)))
lib-folder))
Did I miss something?

As in many operating systems the command to delete a directory only works if the directory is empty. Eg. in GNU
# rmdir dir
rmdir: failed to remove 'dir/': Directory not empty
Usually a recursive delete is needed:
# rm -r dir
#
In #lang racket you have the function delete-directory/files that deletes recursively. Just replacing delete-directory with delete-directory/files would do what you want:
(define lib-folder '("/Users/apple/Library/Application Support/aoi"))
(define (del-lib-folder)
(for-each (λ (p)
(printf "del lib folder:~a~n" p)
(if (directory-exists? p)
(delete-directory/files p)
(printf "folder does not exist:~a~n" p)))
lib-folder))
Be very careful though as a bug in the file list will do exactly as you asked and you may never get those files back.

use delete-directory/files:
(define lib-folder '("/Users/apple/Library/Application Support/aoi"))
(define (del-lib-folder)
(for-each (λ (p)
(printf "del lib folder:~a~n" p)
(delete-directory/files #:must-exist? #f p))
lib-folder))

Related

Racket: How do you use the value of an argument as a symbol?

I'm new to Racket, and am struggling to find the right words to explain what I'm trying to do. The best I can come up with is this: How do I use the value of an argument as a symbol, without calling the function which has the same name as the value of the argument, and without just quoting the name of the argument?
Here is a minimal example to show this.
There is a function called ul which does something.
There is another function called make-list which has a single parameter, list-type.
There is a third function, make-ul, which calls make-list with an argument ul.
In the true branch, the function ul is applied. Good so far.
In the false branch, I want to be able to use the value of the argument, which in this case is 'ul, as a symbol. In effect, this line of code would run (λ args (list* 'ul args)). How do I achieve this?
#lang racket
(define (ul . xs) `(div ,#xs))
(define (make-list list-type)
(if (equal? 'something 'something-else)
(λ args (apply list-type args)) ; true branch: call the function "ul"
(λ args (list* list-type args)))) ; false branch: how to use `ul without calling the "ul" function?
(define make-ul (make-list ul))
If your goal is to write good Racket code, then the best answer is don't.
Add a separate argument to take the symbol, like this:
;; make-list : Procedure Symbol -> Any ... -> Any
(define (make-list proc tag-sym)
(if ....
proc ;; (λ args (apply proc args)) simplifies to just proc
(λ args (list* tag-sym args))))
(define make-ul (make-list ul 'ul))
Or if the branch is independent of args, you could just have a single argument of type (U Procedure Symbol) instead:
;; make-list : (U Procedure Symbol) -> Any ... -> Any
(define (make-list list-type)
(cond [(procedure? list-type)
;; (λ args (apply list-type args))
list-type]
[(symbol? list-type)
(λ args (list* list-type args))]))
(define make-ul (make-list ul))
(define make-li (make-list 'li))
If you want to explore Racket features, there are two main ways to do it.
You can make a macro that takes a procedure name and uses it both as a reference and quotes it as a symbol. For example, using the first version of make-list above:
(define-syntax-rule (make-list* proc-name) (make-list proc-name (quote proc-name)))
(define make-ul (make-list* ul)) ;; => (define make-ul (make-list ul 'ul))
You can call object-name on a procedure to ask what Racket thinks its name is. For example, (object-name list) returns 'list, and (object-name ul) should return 'ul. But (object-name make-ul) will not return 'make-ul, because Racket tracks names at compile time, not at run time. (I would advise against writing code that depends on object-name; it makes your code very fragile.)

Macro calling macro gives "undefined variable" in Gambit Scheme

In Gambit Scheme, I can't seem to invoke a macro in the definition of another macro if I compile the file. Here is a contrived example:
;;;; example.scm
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body))
(define-macro (compose-macro f g)
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))
(define my-cadr
(lambda (x)
((compose-macro car cdr) x)))
;; $ gsc example.scm
;; *** ERROR IN #<procedure #2> -- Unbound variable: w/gensyms
However, if I load the file with the (include ...) special form in the interpreter, it works
$ gsi
> (include "example.scm")
> (pp my-cadr)
(lambda (x) ((lambda (#:x0) (car (cdr #:x0))) x))
Does anyone know what is going on here? Can I convince Gambit to let me use w/gensyms in the definition of another macro in a compiled file?
This is most likely related to phases.
Try this:
Put w/gensyms in a file a.scm and put compose-macro in a file b.scm that imports a.scm.
This is a phasing problem. You want the definition of w/gensyms to be available in the body of subsequent macros. This can be achieved with a for-syntax macro that forces the evaluation of the macro definition at syntax expansion time:
(define-macro (for-syntax . body)
(eval `(begin ,#body))
`(begin))
(for-syntax
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body)))
If you want the macro to be available both from within other macro definitions and within non-macro definition code you can use this instead:
(define-macro (for-syntax . body)
(eval `(begin ,#body))
`(begin ,#body))
For this specific example, since you are using the macro at a single place, you could have done this:
(define-macro (compose-macro f g)
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body))
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))
A related approach to address phasing issues is to put the definition of w/gensyms and other macros in the file "macros.scm" and do:
(define-macro (compose-macro f g)
(include "macros.scm")
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))

Racket: Logging to a file

I was writing a Racket program that needed to log information but I wanted to store the logs in a file. My first attempt was to use "with-logging-to-port" and use "open-output-file" to create an output-port.
#lang racket
(require racket/logging)
(define (identity/log x)
(log-info "returning ~a" x) x)
(with-logging-to-port (open-output-file "testing.txt")
(λ () (identity/log 4)) 'info)
However when I open the file afterwards it is blank! In addition, I can't run this more than once because "open-output-file" gives me an error that the file already exists.
I'm pretty sure the reason is that you don't close the file properly. This should work:
(let ((out (open-output-file "testing.txt"
; just to not get an error on consecutive runs
#:exists 'append)))
(with-logging-to-port out
(λ () (identity/log 4)) 'info)
(close-output-port out))
Instead of doing housekeeping you can use call-with-output-file
(call-with-output-file "testing.txt"
(λ (out)
(with-logging-to-port out
(λ () (identity/log 4)) 'info))
#:exists 'append)
If log information is in a list of strings, say lst, one can also use following function:
(display-lines-to-file lst "mylog.txt"
#:exists 'append)
See: https://docs.racket-lang.org/reference/Filesystem.html?q=lines-file#%28def._%28%28lib._racket%2Ffile..rkt%29._display-lines-to-file%29%29
(require racket/file)
(display-lines-to-file lst path
[ #:separator separator
#:mode mode-flag
#:exists exists-flag]) → void?
I give you my log func's source :
(define my_logger (make-logger 'my-log))
(define logger_thread #f)
(define (log fmt . content)
(log-message my_logger 'info "" (string-append (format-time (now)) " " (apply format (cons fmt content)))))
(define (start-logger log_path)
(let ([r (make-log-receiver my_logger 'info)]
[riqi (format-riqi (now))])
(set! logger_thread
(thread
(lambda ()
(let ([log_dir (build-path log_path (substring riqi 0 4))])
(when (not (directory-exists? log_dir))
(make-directory log_dir))
(with-output-to-file
(build-path log_path (substring riqi 0 4) riqi) #:exists 'append
(lambda ()
(let loop ()
(match (sync r)
[(vector l m v v1)
(printf "~a\n" v)
(flush-output)])
(loop))))))))))
(define (restart-logger)
(kill-thread logger_thread)
(start-logger))
(define (launch-log-daemon log_path)
(start-logger log_path)
(thread
(lambda ()
(let loop ()
(sync
(alarm-evt (+ (current-inexact-milliseconds) (* 1000 60 60))))
(when (= 0 (date-hour (seconds->date (current-seconds))))
(restart-logger))
(loop)))))
At the beginning of the app, you should run:
(launch-log-daemon log_path)
then you can use it like this:
(log "~a:~a" "some1" "some2")
I use the date as the log file directory and name,
it will automatically start a new log file when date changed.
foramt-riqi and format-time is here:
(define (format-riqi the_date)
(format "~a~a~a"
(date-year the_date)
(~a (date-month the_date) #:min-width 2 #:pad-string "0" #:align 'right)
(~a (number->string (date-day the_date)) #:min-width 2 #:pad-string "0" #:align 'right)))
(define (format-time the_date)
(format "~a:~a:~a"
(~a (date-hour the_date) #:min-width 2 #:pad-string "0" #:align 'right)
(~a (date-minute the_date) #:min-width 2 #:pad-string "0" #:align 'right)
(~a (date-second the_date) #:min-width 2 #:pad-string "0" #:align 'right)))
Open your file with flag 'append. For example:
(open-output-file "testing.txt" #:exists 'append )

Emacs metaprogramming, dynamically define methods

I am trying to define some helper functions to quickly jump to different projects from within emacs. I started by defining a macro as follows
(defmacro project-alias (name path)
`(defun ,name ()
(interactive)
(cd ,path)))
And this works great I can (project-alias foo "~/bar") no problem. The problem comes when I try and apply this macro over a list of tuples.
(setq projects '((foo . "~/foo")
(bar . "~/bar")))
(dolist (p projects)
(project-alias (car p) (cdr p)))
The above code errors with
Debugger entered--Lisp error: (wrong-type-argument symbolp (car p))
defalias((car p) (lambda nil (interactive) (cd (cdr p))))
I have tried passing the first argument in as a string and calling intern to get the symbol representation out with no joy, and I've also tried defining my macro to accept the string form and that doesn't work either
What am I doing wrong?
If your use of the macro involves evaluating sexps to produce the name and path, then it needs to evaluate the sexps:
(defmacro project-alias (name path)
`(defun ,(eval name) () (interactive) (cd ,(eval path))))
Alternatively, use a function:
(defun project-alias (name path)
(eval `(defun ,name () (interactive) (cd ,path))))
You could do either
(defun project-alias-f (name path)
(eval `(defun ,name ()
(interactive)
(cd ,path))))
(dolist (p projects)
(project-alias-f (car p) (cdr p)))
or
(dolist (p projects)
(eval `(project-alias ,(car p) ,(cdr p))))
Macro arguments are passed un-evaluated. (Macros could not otherwise do what they can do.)
So your arguments are literally the forms (car p) and (cdr p) (as opposed to, for instance, foo and "~/foo").
Here's another take on it, with no macros nor eval:
;; -*- lexical-binding:t -*-
(defun project-alias-f (name filename)
(defalias name (lambda () (interactive) (cd filename)))
(dolist (p projects)
(project-alias-f (car p) (cdr p)))
This is not an answer to your macro problem, but an alternative solution to your desire to jump between projects.
In my init.el file, I have (amongst other things)
(set-register ?A '(file . "~/.aliases"))
(set-register ?E '(file . "~/.emacs.d/init.el"))
(set-register ?H '(file . "~/.hgrc"))
(set-register ?T '(file . "~/.TODO.org"))
Then I can use jump-to-register (C-x r j) to jump to one of these files when I wish to edit the file (or do something with one of the unlisted projects). Because a file/folder is stored in the register (rather than a window config say), emacs will open the file or folder it finds in the register.

using the rplaca function

I am trying to replace a symbol in a list with another symbol example:
(replace 'the 'a '(the cat sat on the mat))
==> (a cat sat on a mat) So the "the" should be replaced by "a"
Here is my code,
(defun replace (item new-item list)
(cond ((null list)
list
)
((eq (first list) (item))
((rplaca list new-item)
(replace (rest list))))
))
;rplace replace the first of the cons with obj
;(defparameter *some-list* (list* 'one 'two 'three 'four)) => *some-list*
;*some-list* => (ONE TWO THREE . FOUR)
;(rplaca *some-list* 'uno) => (UNO TWO THREE . FOUR)
When I compile it in aligra is giving me the following error
Error: Function position must contain a symbol or lambda expression: (RPLACA LIST NEW-ITEM)
[condition type: PARSE-ERROR]
I don't understand why is giving this error since the rplace function takes two arguments.
There are several different errors in your code:
item is not a function, so that you should not surround it with parentheses
your recursive call should repeat the same two first arguments as the original call
the recursive call should be made in all cases (and not only when the car was replaced)
you have extra parentheses around the rplaca call, which are the actual cause of the reported error
(defun replace (item new-item list)
(cond ((null list)
list)
((eq (first list) item)
(rplaca list new-item)
(replace item new-item (rest list)))
(t
(replace item new-item (rest list)))))
(setq l '(a cat sat on a mat))
(replace 'a 'the l)
l ;; -> (the cat sat on the mat)
Also, as noted in comments, it is not customary to mute literals ; you might want to construct a new list instead, for example like this:
(defun replace-1 (item new-item list)
(mapcar (lambda (car)
(if (eq car item)
new-item
car))
list))
(replace-1 'a 'the '(a cat sat on a mat))
;; -> (the cat sat on the mat)