[Haskell-cafe] How to write elegant Haskell programms? (long posting)

Michael Roth mroth at nessie.de
Mon Jan 29 14:14:55 EST 2007

Hello list,

I'm new to Haskell and I'm trying to learn how to write elegant code
using Haskell.

I decided to convert the following small tool, written in ruby:

#! /usr/bin/env ruby

require 'pathname'

BASENAMES       = %w{ mail.log thttpd.log }
ARCHIVEDIR      = Pathname.new '/var/log/archive'
LOGDIR          = Pathname.new '/var/log'

class Pathname
  def glob glob_pattern
    Pathname.glob self.join(glob_pattern)
  def timestamp
    stat.mtime.strftime '%Y%m%d'

for basename in BASENAMES
  for oldname in LOGDIR.glob "#{basename}.*.gz"
    newname = ARCHIVEDIR.join "#{basename}.#{oldname.timestamp}.gz"
    puts "mv #{oldname} #{newname}"
    File.rename oldname, newname

My solution in Haskell is:

import System.Directory   (getDirectoryContents, getModificationTime,
import System.Locale      (defaultTimeLocale)
import System.Time        (ClockTime, toUTCTime, formatCalendarTime)
import Text.Regex         (mkRegex, matchRegex)
import Maybe
import Control.Monad

logdir, archivedir :: String
logfiles :: [String]

logfiles    = [ "mail.log", "thttpd.log" ]
logdir      = "/var/log"
archivedir  = "/var/log/archive"

basename :: String -> String
basename filename = head . fromMaybe [""] $ matchRegex rx filename where
  rx = mkRegex "^(.+)(\\.[0-9]+\\.gz)$"

isLogfile :: String -> Bool
isLogfile filename = basename filename `elem` logfiles

timestamp :: ClockTime -> String
timestamp time =
  formatCalendarTime defaultTimeLocale "%Y%m%d" (toUTCTime time)

makeOldname :: String -> String
makeOldname fn = logdir ++ '/' : fn

makeNewname :: String -> String -> String
makeNewname bn ts = archivedir ++ '/' : bn ++ '.' : ts ++ ".gz"

move :: String -> String -> IO ()
move oldname newname = do
  putStrLn $ "mv " ++ oldname ++ ' ' : newname
  renameFile oldname newname

main :: IO ()
main = do
  files <- liftM (filter isLogfile) (getDirectoryContents logdir)
  let oldnames = map makeOldname files
  times <- mapM getModificationTime oldnames
  let newnames = zipWith makeNewname (map basename files) (map timestamp
  zipWithM_ move oldnames newnames

Ok, the tool written in Haskell works. But, to me, the source doesn't
look very nice and even it is larger than the ruby solution, and more
imporant, the programm flow feels (at least to me) not very clear.

Are there any libraries available to make writing such tools easier?
How can I made the haskell source looking more beautiful?

Michael Roth

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 155 bytes
Desc: OpenPGP digital signature
Url : http://www.haskell.org/pipermail/haskell-cafe/attachments/20070129/bb3f0a2a/signature.bin

More information about the Haskell-Cafe mailing list