[darcs-devel] lazy parsing and a generic IO-like monad (was darcs patch)

David Roundy droundy at abridgegame.org
Wed Jan 5 06:43:52 PST 2005


On Mon, Jan 03, 2005 at 11:50:23AM -0500, Ian Lynagh wrote:
> > The catch is that we'd then have to maintain two separate parsers,
> > unless someone (Ian?) could come up with a tricky way to implement a
> > lazy and a strict parser with the same code.
> 
> This should do it.

Cool, thanks! I like how often your changes end up cleaning up the existing
code (and in the parsing code, it did seriously need cleanup!), besides
adding features.  Now I'll just need to see if there's a place we can
benefit from lazy patch parsing... I think it'll have to wait on an
apply_to_IO sort of implementation, which ideally would involve a common
monadic implementation shared with apply_to_slurpy.

I've been toying with this idea, and have a sort of sketch of an
implementation, which I'll attach here.  It's a bit ugly and very sketchy.
In particular, I'm not sure how to unify the directory bit with file IO
without a multi-parameter class, but there are issues with multi-parameter
classes that I don't yet understand, so this code doesn't really work yet
as written.

The problem is that I'd like to have different "directory-level" monads
(for IO, Slurpy, PatchCheck, maybe a database IO type), but then each of
these monads would have to have a different "Handle" type. One way around
this would be to support only "monolithic" file IO operations, such as
writeFile and readFile, but I'm afraid that may not be as efficient as
multiple hPutPSs, for example--it means we'd always have to construct the
entire file in memory before writing.  Maybe this wouldn't hurt, and it
would definitely simplify matters.  Perhaps a monolithic putDoc could
efficiently encapsulate multiple IO operations?

In any case, the basic idea is that if we could create a class FooIO (which
certainly could use a better name), then we could make

apply_patch :: FooIO m => Patch -> m ()

which would work either on slurpies or on IO.  In the latter case, we could
combine this code with lazy parsing to apply a patch to a working directory
as we parse it, so the entire patch doesn't need to be held in memory on a
darcs get --partial.  This would be a risky operation, in that we wouldn't
know whether the patch parses properly until we've already messed up the
directory, but in the context of the get command, this doesn't hurt.
-- 
David Roundy
http://www.darcs.net
-------------- next part --------------
module FooIO ( FooReadOnlyIO(..), FooIO(..) ) where

import Control.Monad ( liftM )

import System.IO ( Handle, hGetContents, openFile, IOMode(..),
                   hClose, hPutStr,
                 )
import System.Directory ( getCurrentDirectory, setCurrentDirectory,
                          renameFile, doesFileExist, doesDirectoryExist,
                          getDirectoryContents,
                        )

class Monad m => FooReadOnlyIO m where
    mInSubdirectory :: FilePath -> m a -> m a
    --mReadFilePS :: FilePath -> m PackedString
    mReadFile :: FilePath -> m String
    mDirectoryContents :: m [FilePath]
    mIsFile :: FilePath -> m Bool
    mIsDirectory :: FilePath -> m Bool

class FooReadOnlyIO m => FooIO m h where
    mWriteFile :: FilePath -> (h -> m ()) -> m ()
    mRenameFile :: FilePath -> FilePath -> m ()
    mPutStr :: h -> String -> m ()

instance FooReadOnlyIO IO where
    mInSubdirectory d x = do setCurrentDirectory d -- buggy
                             x
    mDirectoryContents = getDirectoryContents "."
    mReadFile = readFile
    mIsFile = doesFileExist
    mIsDirectory = doesDirectoryExist
    --mReadFilePS = readFilePS

instance FooIO IO Handle where
    mWriteFile x j = openFile x WriteMode >>= j
    mRenameFile = renameFile
    mPutStr = hPutStr

{-
newtype SlurpMonad a = SM (Slurpy -> (a, Slurpy))

withSlurpy :: Slurpy -> SlurpMonad a -> a
toSlurpy :: Slurpy -> SlurpMonad a -> Slurpy

-}


More information about the darcs-devel mailing list