csv-write.ss
(module csv-write mzscheme
  (require (lib "string.ss" "srfi" "13")
           (prefix string: (lib "string.ss"))
           (lib "contract.ss")
           (lib "etc.ss"))

  (define (normalize-newlines s)
    (string-join (string:regexp-split #rx"(?:\r\n)|\r|\n" s) "\r\n"))

  (define (normalize-quotes s)
    (regexp-replace* #rx"\"" s "\"\""))

  (define (format-cell x)
    (let ([str (format "~a" x)])
      (if (regexp-match #rx"\"|,|\r|\n" str)
          (string-append "\"" (normalize-newlines (normalize-quotes str)) "\"")
          str)))

  (define (format-row ls)
    (string-join (map format-cell ls) "," 'infix))

  (define (format-table ls)
    (map format-row ls))

  (define write-cell
    (opt-lambda (x [out (current-output-port)])
      (display (format-cell x) out)))

  (define write-row
    (opt-lambda (ls [out (current-output-port)])
      (display (format-row ls) out)
      (newline out)))

  (define write-table
    (opt-lambda (ls [out (current-output-port)])
      (for-each (lambda (row)
                  (display row out)
                  (newline out))
                (format-table ls))))

  (provide/contract
    [format-cell (any/c . -> . string?)]
    [format-row ((listof any/c) . -> . string?)]
    [format-table ((listof (listof any/c)) . -> . (listof string?))]
    [write-cell ((any/c) (output-port?) . opt-> . any)]
    [write-row (((listof any/c)) (output-port?) . opt-> . any)]
    [write-table (((listof (listof any/c))) (output-port?) . opt-> . any)]))