Text File Databases: Part 2

October 22, 2010

The processing functions are considerably simpler than the reader functions of the previous exercise; they don’t have to deal with multiple end-of-line markers or optional arguments. Here is fold:

(define (fold-port reader folder base . port)
  (let ((p (if (null? port) (current-input-port) (car port))))
    (let loop ((item (reader p)) (result base))
      (if (eof-object? item)
          (loop (reader p) (folder result item))))))

Map-port could be written as (reverse (fold-port reader cons '() . port)), but we’ll let it stand on its own:

(define (map-port reader mapper . port)
  (let ((p (if (null? port) (current-input-port) (car port))))
    (let loop ((item (reader p)) (result '()))
      (if (eof-object? item)
          (reverse result)
          (loop (reader p) (cons (mapper item) result))))))

For-each does the same iteration as fold and map, but accumulates no output, since all the procedure calls are evaluated solely for their side-effects:

(define (for-each-port reader proc . port)
  (let ((p (if (null? port) (current-input-port) (car port))))
    (let loop ((item (reader p)))
      (if (not (eof-object? item))
          (begin (proc item) (loop (reader p)))))))

Filter is different than the others, taking a reader and returning a new reader:

(define (filter-port reader pred?)
  (lambda args
    (let loop ((x (apply reader args)))
      (cond ((eof-object? x) x)
            ((pred? x) x)
            (else (loop (apply reader args)))))))

The code given above is collected at http://programmingpraxis.codepad.org/c7utWH0v, and we’ll have a real-life example in our next exercise. You can read more about this style of input/output data processing in this paper, which includes documentation, sample uses, and complete code.

Pages: 1 2

2 Responses to “Text File Databases: Part 2”

  1. […] Praxis – Text File Databases: Part 2 By Remco Niemeijer In today’s Programming Praxis exercise, our task is to define functions to map, filter, fold and foreach over […]

  2. Remco Niemeijer said

    As I argue in http://bonsaicode.wordpress.com/2010/10/22/programming-praxis-text-file-databases-part-2/ the only one of those four worth implementing in Haskell would be foreach, as restricting the others to text databases would lose more convenience than it gains. Hence, the code sample below only shows the definition for foreach (a.k.a. mapM_) and how to apply the regular list processing functions to database records.

    dbMapM_ :: Monad m => (a -> m b) -> Either l [a] -> m ()
    dbMapM_ = either (const $ return ()) . mapM_
    main :: IO ()
    main = do db <- readDB (fixedLength [5,3,4]) "db_fl.txt"
              print $ map head <$> db
              print $ foldl (const . succ) 0 <$> db
              print $ filter (odd . length) <$> db
              dbMapM_ print db

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: