Simple solution to mkdir a tree

The problem

Yesterday night I've written command line interface (CLI) for GNU Artanis, after took care of my daughter to sleep, I had only one hour to hack something.

My aim is to add `art create proj-name' command to create a bunch of files/directories for initializing a new Artanis web app. Just like Rails does. Well, since my Turing brain has limited time for this hack, I have to choose my favorite way to traverse the tree for this job, the recursive way. ;-)

Depth first traversal

One of the proper way to mkdir a directory tree is to use DFS. I chose the pre-order DFS.

If you have well structured Tree implementation like this, then the pseudo code below could be simple enough to try.

;; This is pseudo code
(define (preorder node visit)
  (cond
   ((leaf? node) #t)
   (else
    (visit node)
    (for-each preorder (children node)))))

Actually, the same algorithm is used in my unfinished data-taistructure lib Nashkel. Could be optimized to iterative one though...

Tree structure

Here's a problem: I don't known if I'll add/remove directories in the future, so I need a flexible design to let me do this job easily. A proper tree design is a good start.

For example:

|-- app
|   |-- controller
|   |-- model
|   `-- view
|-- lib
|-- log
|-- prv
|-- pub
|   |-- css
|   |-- img
|   |   `-- upload
|   `-- js
|-- sys
|   |-- i18n
|   `-- pages
|-- test
|   |-- benchmark
|   |-- functional
|   `-- unit
`-- tmp
    `-- cache

In Scheme, the most convenient data structure is the list:

(define *dir-arch*
  '((app (model controller view)) ; MVC stuff
    (sys (pages i18n)) ; system stuff
    (log) ; log files
    (lib) ; libs
    (pub ((img (upload)) css js)) ; public assets
    (prv) ; private stuff, say, something dedicated config or tokens
    (tmp (cache)) ; temporary files
    (test (unit functional benchmark)))) ; tests stuffs

Let me explain this tree structure built with list, since the algorithm implementation has something to do with the data structure design. I gave the rule below:

((node1 (child1_1 child1_2 ...))
 (node2 ((node3 (child3_1 child3_2 ...)) child2_1 child2_2 (node4 (child4_1 child4_2 ...)) ...))
 (node5) ; no any children
 ...)

The disadvantage of this tree is hard to modify dynamically (alright, you may use set-car! and set-cdr!). But it's easy to handle statically, because the hierarchy is clear.

Design the high-order-function

Now that I'm using Scheme, I'll choose FP way containly. High order function just like design pattern of FP. Design a good and generic one will save your lot time in the future development.

Here is mine:

(define (dfs tree visit level)
  (match tree
    (() #t)
    (((r (children ...)) rest ...)
     (visit r level)
     (for-each (lambda (x) (dfs (list x) visit (cons r level))) children)
     (dfs rest visit level))
    (((r) rest ...)
     (visit r level)
     (dfs rest visit level))
    ((children ...)
     (visit (car children) level)
     (dfs (cdr children) visit level))))

So it's easy to mkdir a directory tree now:

(define (create-framework)
  (define (->path x l)
    (format #f "~{~a~^/~}" (reverse (cons x l))))
  (dfs *dir-arch* (lambda (x l) (mkdir (->path x l))) '()))

This CLI feature won't appear in 0.0.3, and because it'll bring great change to GNU Artanis, maybe it should be in 0.1.0. Dunno...

Happy hacking!