[darcs-users] darcs patch: add exception to haskell_policy.sh for D... (and 61 more)

Eric Kow kowey at darcs.net
Wed Oct 29 13:00:08 UTC 2008


Hi David,

As I mentioned earlier, this is the promised Salvo 8 (the big one),
which I have verified to pass tests on both GHC 6.6 and 6.8.

If it helps, here is my contribution to the review.

I think once we get over this hump, the merging the rest of sprint
patches in (or not) is easy

Also, if there are any changes to be made, I think we should make them
*on top* of this bundle because amend-recording is simply not going to
be practical (I don't meant to belabour the point, it's just that
amending is a bit ingrained in our culture, so people get a bit nervous
when there is a large amount of work that wants to get in)

----------------------------------------------------------------
add exception to haskell_policy.sh for Data.ByteString.readFile
Remove dead code: reversePS
replace insanely low level version of ifHeadThenTail with uncons
----------------------------------------------------------------
These are from Salvo 7

just hash the last 20 characters in LCS
---------------------------------------
Unfortunately, the rest of the bundle seems to depend on this patch
somewhere.  So I request that we apply this patch and then only after
the sprint stuff has settled down a bit, think about rolling it back.
If it makes you feel more comfortable, we can just leave it in unstable
for a while...

I don't think there will be many of these by the way.

Remove OldFastPackedString entirely
-----------------------------------
This is the patch that makes GHC 6.6 compatibility tricky.  If I
understand correctly, GHC 6.6 has a version of bytestring in its base
(2.0), but it doesn't have some functions which are used by darcs.

But that wasn't the real reason for keeping OldFastPackedString around.
This module was created when we had first started the switchover to
bytestring back in Februrary and we were still nervous about whether we
did it right or not.  And sure enough there were some initial hiccups,
like openFile instead of openBinaryFile, things which the presence of
OldFastPackedString helped us to isolate.

However, that was over 8 months (and two releases) ago.  We are now at
the stage where enough people have been relying on the new bytestring
implementation for long enough that we pretty much have to assume that
the new bytestring is the correct implementation for darcs.  So now is
the right time to jettison this module.

Remove splitPS in favor of its definition
-----------------------------------------
> -{-# INLINE splitPS #-}
> -splitPS :: Char -> PackedString -> [PackedString]
> -splitPS = BC.split

Here is the important bit.  The rest of it is just rewriting the
code to import qualified Data.ByteString.Char8 as BC, and use
BC.split directly.

Remove references to writeFilePS
--------------------------------
> -writeFilePS :: FilePath -> PackedString -> IO ()
> -writeFilePS = B.writeFile

Same principle...

-------------------------------
remove references to readFilePS
remove references to hPutPS
remove references to hGetPS
remove references anyPS
remove references to lastPS
remove references to indexPS
-------------------------------
Same principle... I think we can trust assume that similarly named
patches in the series do this same conservative thing.

replace reimplementation of c2w with bytestring's c2w
-----------------------------------------------------
The reimplementation here is our use of (toWord8 . ord)

The ByteString implementaton is (fromIntegral . ord), according to
http://www.haskell.org/ghc/docs/latest/html/libraries/bytestring/src/Data-ByteString-Internal.html

> +import qualified Data.ByteString.Internal as B (c2w)

Maybe we should use the BI prefix instead?

remove all referenes to lengthPS
--------------------------------
> -lengthPS :: PackedString -> Int
> -lengthPS = BC.length

Hmm... ok, here there is a discrepency between the original
implementation and the imports (original is BC, new is B).  But then
again, this makes sense because this is the length function and we're
talking about 8 bit representations anyway.

explicit import lists
---------------------
> -import qualified Data.ByteString       as B (length)
> -import qualified Data.ByteString.Char8 as BC
> +import qualified Data.ByteString       as B  (length)
> +import qualified Data.ByteString.Char8 as BC (index)

More explict import lists
-------------------------
> ] hunk ./src/Darcs/Diff.lhs 50
> -import qualified Data.ByteString as B
> -import qualified Data.ByteString.Char8 as BC
> +import qualified Data.ByteString as B       (hGet, length)
> +import qualified Data.ByteString.Char8 as BC (last)

remove all references to splitAtPS
----------------------------------
> Don Stewart <dons at galois.com>**20081026012749
> ] hunk ./src/Darcs/Patch/Info.lhs 34
> +import qualified Data.ByteString       as B  (length, splitAt)

> hunk ./src/FastPackedString.hs 266
> -{-# INLINE splitAtPS #-}
> -splitAtPS :: Int -> PackedString -> (PackedString, PackedString)
> -splitAtPS  = BC.splitAt

Same concern here.  Obviously, Don knows what he's doing, but a little
explanation might make spread the bytestring-enlightenment a bit...

make BC.last depend on GADT_WITNESSES in Diff.lhs
-------------------------------------------------

remove all references to headPS
-------------------------------
> +import qualified Data.ByteString.Char8    as BC (index, head)
> +import qualified Data.ByteString.Char8 as BC (index, head)
> +import Data.ByteString.Char8 as BC (head)

> -{-# INLINE headPS #-}
> -headPS :: PackedString -> Char
> -headPS = BC.head

Nice and boring

Remove all references to nullPS
-------------------------------
> -{-# INLINE nullPS #-}
> -nullPS :: PackedString -> Bool
> -nullPS = BC.null

Again a discrepency between the original implementation, and the import
list (original is BC, new is B)... but I have no reason to believe that
there should be a difference between BC.null and B.null.

remove nullPS from Darcs.Patch.Test
-----------------------------------

Optimize ignore_junk to not unpack the bytestring
-------------------------------------------------
>  ignore_junk :: [PackedString] -> [PackedString]
>  ignore_junk = filter isnt_ignored
> -    where isnt_ignored x = doesnt_start_with (unpackPS x) ignored
> -          doesnt_start_with x ys = not $ any (`isPrefixOf` x) ys
> +    where isnt_ignored x = doesnt_start_with x ignored
> +          doesnt_start_with x ys = not $ any (`B.isPrefixOf` x) ys

:-)

Don't unpack the same bytestring twice in two lines
---------------------------------------------------
>  takeHash :: PackedString -> Maybe (String, PackedString)
>  takeHash ps = do h <- listToMaybe $ linesPS ps
> -                 guard $ okayHash $ unpackPS h
> -                 Just (unpackPS h, dropPS (B.length h) ps)
> +                 let v = BC.unpack h
> +                 guard $ okayHash v
> +                 Just (v, dropPS (B.length h) ps)

Makes sense to me...

Optimize hunk handling not to needlessly unpack bytestrings
-----------------------------------------------------------
> hunk ./src/Darcs/Patch/Prim.lhs 269
> -    showsPrec d (Hunk line old new) | all ((==1) . length . unpackPS) old && all ((==1) . length . unpackPS) new
> +    showsPrec d (Hunk line old new) | all ((==1) . B.length) old && all ((==1) . B.length) new

> hunk ./src/Darcs/Patch/Prim.lhs 275
> -             showsPrecC ss = showParen True $ showString "packStringLetters " . showsPrec (app_prec + 1) (map (head . unpackPS) ss)
> +             showsPrecC ss = showParen True $ showString "packStringLetters " . showsPrec (app_prec + 1) (map B.head ss)

Makes sense...

Optimize inefficiency when unpacking string for tok replace
-----------------------------------------------------------
> -  return $ FP (readFileName x f) $ TokReplace (drop_brackets $ unpackPS regstr)
> +  return $ FP (readFileName x f) $ TokReplace (unpackPS (drop_brackets regstr))

Again following the general theme of not unpacking unless we have to..

> -    where drop_brackets = init . tail
> +    where drop_brackets = B.init . B.tail

Used above

> replace ./src/Darcs/Patch/Read.lhs [A-Za-z_0-9\-\.] unpackPS BC.unpack

Same as FastPackedString implementation below.

optimise use of unpack in Format.lhs
------------------------------------
>  writeRepoFormat :: RepoFormat -> FilePath -> IO ()
>  writeRepoFormat (RF rf) loc = writeBinFile loc $ unlines $
> -                              map (concat . intersperse "|" . map unpackPS) rf
> +                              map (BC.unpack . B.intercalate (BC.singleton '|')) rf

> replace ./src/Darcs/Repository/Format.lhs [A-Za-z_0-9\-\.] unpackPS B.unpack

Remove all references to unpackPS
---------------------------------
> -unpackPS :: PackedString -> String
> -unpackPS = BC.unpack

With these patches, I have taken the habit of grepping the rest of the
lines for import and unpack, just to make sure we are always using the
BC version.

remove writeFilePS usage from HTTP.hs
-------------------------------------
> replace ./src/HTTP.hs [A-Za-z_0-9\-\.] writeFilePS B.writeFile

This is merging Salvatore's fixes with the bytestring work

--------------------------------
remove all references to nilPS
remove all references to tailPS
remove all references to initPS
--------------------------------
> -nilPS :: PackedString
> -nilPS = BC.empty

> -tailPS :: PackedString -> PackedString
> -tailPS = BC.tail

> -initPS :: PackedString -> PackedString
> -initPS = BC.init

More list functions where the original uses BC but the new code
uses plain old B.

Remove appendPS, dead code
--------------------------
> -appendPS :: PackedString -> PackedString -> PackedString
> -appendPS = BC.append

Hooray!

remove all references to takePS
-------------------------------
> -takePS :: Int -> PackedString -> PackedString
> -takePS = BC.take

Same as above.  BC original, but B in the new implementation

remove all references to packWords
----------------------------------
> -packWords :: [Word8] -> PackedString
> -packWords s = createPS (length s) $ \p -> pokeArray p s

Replaced with B.pack

----------------------------------
remove all references to findPS
remove all references to breakPS
remove all references to findLastPS
-----------------------------------
> -findPS :: Char -> PackedString -> Maybe Int
> -findPS = BC.elemIndex

> -breakPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
> -breakPS = BC.break

> -findLastPS :: Char -> PackedString -> Maybe Int
> -findLastPS = BC.elemIndexEnd

Nice and boring

---------------------------------
remove all references to concatPS
remove all references to dropPS
---------------------------------
> -concatPS :: [PackedString] -> PackedString
> -concatPS = BC.concat

> -dropPS  :: Int -> PackedString -> PackedString
> -dropPS = BC.drop

Back to the theme of BC original, but B in the new code for list
functions, again, using my "grep +import" method of reviewing
these patches, because we all need a little faceless bureaucracy
in our lives.

remove fpstring.c:first_nonwhite, in favor of pure haskell implementation
-------------------------------------------------------------------------
> -#define ISSPACE(c) \
> -    ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')

Not actually removed by this patch, but for comparison's sake

> +-- A locale-independent isspace(3) so patches are interpreted the same everywhere.
> +-- ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
> +isSpaceWord8 :: Word8 -> Bool
> +isSpaceWord8 w =
> +    w == 0x20 ||    -- ' '
> +    w == 0x09 ||    -- '\t'
> +    w == 0x0A ||    -- '\n'
> +    w == 0x0D       -- '\r'
> +{-# INLINE isSpaceWord8 #-}

Looks good...

> -int first_nonwhite(const char *s, int len)
> -{
> -    const char *start;
> -    const char *end;
> -
> -    for (start = s, end = s + len; s < end && ISSPACE(*s); s++);
> -
> -    return s - start;
> -}

Given s = \t \s \s \0  (len = 3)
           0  1  2  3

This function returns 3

Given s = \t \s \s  f  o  o \0  (len = 6)
           0  1  2  3  4  5  6

This function also returns 3

> +firstnonspace :: Ptr Word8 -> Int -> Int -> IO Int
> +firstnonspace !ptr !n !m
> +    | n >= m    = return n
> +    | otherwise = do w <- peekElemOff ptr n
> +                     if isSpaceWord8 w then firstnonspace ptr (n+1) m else return n

And yes indeed this appears to behave the same way.  The rest of this
patch is nice and boring,

> +dropSpace :: B.ByteString -> B.ByteString
> +dropSpace (BI.PS x s l) = BI.inlinePerformIO $ withForeignPtr x $ \p -> do
> +    i <- firstnonspace (p `plusPtr` s) 0 l
> +    return $! if i == l then B.empty else BI.PS x (s+i) (l-i)

Except maybe for this... anybody have comments?  I think it comes from
bytestring's internals, and if so we can trust it :-)

pure haskell implementation of breakSpace, from Data.ByteString
---------------------------------------------------------------
> +firstspace :: Ptr Word8 -> Int -> Int -> IO Int
> +firstspace !ptr !n !m
> +    | n >= m    = return n
> +    | otherwise = do w <- peekElemOff ptr n
> +                     if (not . isSpaceWord8) w then firstspace ptr (n+1) m else return n

Same principle as firstnonspace above (is refactoring silly here?)

> -breakWhitePS :: PackedString -> (PackedString,PackedString)
> -breakWhitePS ps =
> -   case BI.toForeignPtr ps of
> -   (x,s,l) ->
> -    unsafePerformIO $ withForeignPtr x $ \p->
> -    do i <- fromIntegral `liftM` first_white (p `plusPtr` s) (fromIntegral l)
> -       if i == 0 then return (BC.empty, BI.fromForeignPtr x s l)
> -                 else if i == l
> -                      then return (BI.fromForeignPtr x s l, BC.empty)
> -                      else return (BI.fromForeignPtr x s i, BI.fromForeignPtr x (s+i) (l-i))

> +breakSpace :: B.ByteString -> (B.ByteString,B.ByteString)
> +breakSpace (BI.PS x s l) = BI.inlinePerformIO $ withForeignPtr x $ \p -> do
> +    i <- firstspace (p `plusPtr` s) 0 l
> +    return $! case () of {_
> +        | i == 0    -> (B.empty, BI.PS x s l)
> +        | i == l    -> (BI.PS x s l, B.empty)
> +        | otherwise -> (BI.PS x s i, BI.PS x (s+i) (l-i))
> +    }

Is anybody else able to comment here?  These appear to do the same thing
to me :-)

Remove all references to dropWhilePS, clean up silly_lex while I'm here
-----------------------------------------------------------------------
> -silly_lex ps = (BC.unpack $ BC.takeWhile (/='\n') ps', dropWhilePS (/='\n') ps')
> +silly_lex ps = (BC.unpack a, b)
> +    where
> +        (a, b) = BC.break (== '\n') (dropSpace ps)
> +

For the interested,
break p x is equivalent to (takeWhile (not . p) x, dropWhile (not . p) x)

The rest of this patch is nice and boring.

clean up imports in other modules after ByteString refactorings
---------------------------------------------------------------
> replace ./src/unit.lhs [A-Za-z_0-9\-\.] concatPS B.concat
> replace ./src/unit.lhs [A-Za-z_0-9\-\.] nilPS B.empty
> replace ./src/unit.lhs [A-Za-z_0-9\-\.] unpackPS BC.unpack

Leftovers

remove all references to generatePS
-----------------------------------
> +import qualified Data.ByteString.Internal as B  (c2w, createAndTrim)

> -generatePS :: Int -> (Ptr Word8 -> IO Int) -> IO PackedString
> -generatePS = BI.createAndTrim

> +createPS :: Int -> (Ptr Word8 -> IO ()) -> PackedString
> +createPS = BI.unsafeCreate
> +
> +mallocForeignPtr :: Int -> IO (ForeignPtr Word8)
> +mallocForeignPtr = BI.mallocByteString

Not sure where these came from, but I think they must just be movement
patches, like the below.  Nothing to see.

> +indexPSW ps i = BI.c2w $ BC.index ps i
> -indexPSW ps i = BI.c2w $ BC.index ps i

> +spanEndPS  = BC.spanEnd
> -spanEndPS  = BC.spanEnd

> -readIntPS = BC.readInt . BC.dropWhile isSpace
> +readIntPS = BC.readInt . BC.dropWhile isSpace

remove all references to indexPSW (only ever used as 'head')
------------------------------------------------------------
> -encode ps n buf bufi = case indexPSW ps 0 of
> +encode ps n buf bufi = case B.head ps of

> -        if B.null ps' || indexPSW ps' 0 == newline
> +        if B.null ps' || B.head ps' == newline

> - | otherwise = do poke (buf `plusPtr` bufi) (indexPSW ps 0)
> + | otherwise = do poke (buf `plusPtr` bufi) (B.head ps)

remove all references to spanEndPS
----------------------------------
> -spanEndPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
> -spanEndPS  = BC.spanEnd

Nice and boring

remove all references to breakOnPS
----------------------------------
> +-- Note, Data.ByteString rewrites break ((==) x) into the memchr-based
> +-- breakByte. For this rule to fire, we keep it in prefix application form

> +-- ByteString rewrites break (=='x') to breakByte 'x'
> +--  break ((==) x) = breakChar x
> +--  break (==x) = breakChar x

Note these comments comment

> -    else case breakOnPS '\n' $ B.tail $ dropSpace s of
> +    else case BC.break ((==) '\n') $ B.tail $ dropSpace s of

> -             case breakOnPS '*' $ B.tail s' of
> +             case BC.break ((==) '*') $ B.tail s' of

> -         else case breakOnPS '\n' $ B.tail x of
> +         else case BC.break ((==) '\n') $ B.tail x of

> -      f <- work (Just . breakOnPS '\n' . B.tail . BC.dropWhile (== ' '))
> -      t <- work (Just . breakOnPS '\n' . B.tail)
> +      f <- work (Just . BC.break ((==)'\n') . B.tail . BC.dropWhile (== ' '))
> +      t <- work (Just . BC.break ((==)'\n') . B.tail)

> -    (rest,str) <- case breakOnPS '\n' i of
> +    (rest,str) <- case BC.break ((==) '\n') i of

> -    (rest,str) <- case breakOnPS '\n' i of
> +    (rest,str) <- case BC.break ((==)'\n') i of

> -    (rest,str) <- case breakOnPS '\n' i of
> +    (rest,str) <- case BC.break ((==)'\n') i of

> -                    case breakOnPS '\n' $ B.tail pistr of
> +                    case BC.break ((==)'\n') $ B.tail pistr of

> -                 (l,r) = breakOnPS '\n' s'
> +                 (l,r) = BC.break ((==)'\n') s'

> -                 (kw,h) = breakOnPS ' ' l
> +                 (kw,h) = BC.break ((==)' ') l

Just me stripping out context because it isn't actually helpful here.

> -                Just n -> (B.take n p, B.drop n p)
> +                Just n  -> (B.take n p, B.drop n p)

Just a whitespace tweak

-------------------------------
remove all traces of packString
and in tests
-------------------------------
> --- | Convert a 'String' into a 'PackedString'
> -packString :: String -> PackedString
> -packString = BC.pack

Nice and boring, as verified by grep.

> -space_p   = Both " "  (packString " ")
> +space_p   = Both " "  (BC.singleton ' ')

> -space     = unsafeBoth " "  (packString " ")
> +space     = unsafeBoth " "  (BC.singleton ' ')

> -minus     = unsafeBoth "-"  (packString "-")
> -plus      = unsafeBoth "+"  (packString "+")
> -backslash = unsafeBoth "\\" (packString "\\")
> +minus     = unsafeBoth "-"  (BC.singleton '-')
> +plus      = unsafeBoth "+"  (BC.singleton '+')
> +backslash = unsafeBoth "\\" (BC.singleton '\\')

> -lparen = unsafeBoth  "(" (packString "(")
> +lparen = unsafeBoth  "(" (BC.singleton '(')

> -rparen = unsafeBoth ")" (packString ")")
> +rparen = unsafeBoth ")" (BC.singleton ')')

Ok, so you can't catch everything with my grep technique, but I hope
this sort of cursory review is useful anyway.

remove all references to createPS
---------------------------------
> -createPS :: Int -> (Ptr Word8 -> IO ()) -> PackedString
> -createPS = BI.unsafeCreate

Just these...

> -           createPS (2*l) $ \p -> withForeignPtr x $ \f ->
> +           BI.unsafeCreate (2*l) $ \p -> withForeignPtr x $ \f ->

> -           createPS (l `div` 2) $ \p -> withForeignPtr x $ \f ->
> +           BI.unsafeCreate (l `div` 2) $ \p -> withForeignPtr x $ \f ->

remove all references to mallocForeignPtr
-----------------------------------------
> -mallocForeignPtr :: Int -> IO (ForeignPtr Word8)
> -mallocForeignPtr = BI.mallocByteString

> replace ./src/FastPackedString.hs [A-Za-z_0-9\-\.] mallocForeignPtr BI.mallocByteString

remove all references to unsafeWithCStringLenPS
-----------------------------------------------
> hunk ./src/Crypt/SHA256.hs 22
>  import Foreign.C.String ( withCString )
> +import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
> +import qualified Data.ByteString as B

> hunk ./src/Crypt/SHA256.hs 25
> -sha256sum :: PackedString -> String
> +sha256sum :: B.ByteString -> String

> hunk ./src/Crypt/SHA256.hs 28
> -              unsafeWithCStringLenPS p $ \(ptr,n) ->
> +              unsafeUseAsCStringLen p $ \(ptr,n) ->
 
> -unsafeWithCStringLenPS :: PackedString -> ((CString, Int) -> IO a) -> IO a
> -unsafeWithCStringLenPS = BU.unsafeUseAsCStringLen

Nice and boring

remove dead code
----------------
> -withCStringPS :: PackedString -> (CString -> IO a) -> IO a
> -withCStringPS = BC.useAsCString

clean up module imports after ByteString changes
------------------------------------------------

remove last references to the PackedString type
-----------------------------------------------
> replace ./src/Darcs/Commands/Annotate.lhs [A-Za-z_0-9\-\.] PackedString BC.ByteString
> replace ./src/Darcs/Commands/Apply.lhs [A-Za-z_0-9\-\.] B.ByteString BC.ByteString
> replace ./src/Darcs/Commands/Apply.lhs [A-Za-z_0-9\-\.] PackedString B.ByteString

Any reason to be concerned about the difference between BC.ByteString
and B.ByteString?  The bulk of these use B.ByteString...

Remove all references  to FastPackedString the module. Gone
-----------------------------------------------------------
Straightforward module rename.  Henceforth, FastPackedString is no
longer our wrapper, but a set of utility functions.

> -	FastPackedString.hs UglyFileName.lhs IsoDate.lhs \
> +	ByteStringUtils.hs UglyFileName.lhs IsoDate.lhs \

> replace ./src/Darcs/Commands/Apply.lhs [A-Za-z_0-9] FastPackedString ByteStringUtils
...
> replace ./src/SHA1.lhs [A-Za-z_0-9] FastPackedString ByteStringUtils

> conflictor [
> replace ./src/Darcs/Patch/Test.lhs [A-Za-z_0-9\-\.] packString BC.pack
> ]
> :
> replace ./src/Darcs/Patch/Test.lhs [A-Za-z_0-9] FastPackedString ByteStringUtils
> conflictor [
> replace ./src/Darcs/Patch/Unit.lhs [A-Za-z_0-9\-\.] packString BC.pack
> ]
> :
> replace ./src/Darcs/Patch/Unit.lhs [A-Za-z_0-9] FastPackedString ByteStringUtils

These conflictors must have been Jason and Don working in parallel to
tweak imports, tests, etc.  Notice how it's only the Test modules that
are affected


small merges
------------
Nothing to see here.

clean up module imports after ByteString changes
------------------------------------------------

fixup ByteString compatibility for sake of ghc6.6
-------------------------------------------------
Not much to see here, though this patch is crucial.  The advantage of
them /starting/ by cleaning up and only finishing by re-adding the
GHC 6.6 compatibility is that it meant a lot less work for them at the
end.

I'm assuming these are just straight copy and paste from bytestring

> +uncons :: B.ByteString -> Maybe (Word8, B.ByteString)
> +uncons (BI.PS x s l)
> +    | l <= 0    = Nothing
> +    | otherwise = Just (BI.inlinePerformIO $ withForeignPtr x
> +                                        $ \p -> peekByteOff p s,
> +                        BI.PS x (s+1) (l-1))

> +fromForeignPtr :: ForeignPtr Word8
> +               -> Int -- ^ Offset
> +               -> Int -- ^ Length
> +               -> B.ByteString

> +intercalate :: B.ByteString -> [B.ByteString] -> B.ByteString
> +intercalate s = B.concat . (intersperse s)

Unused import police
--------------------
> Eric Kow <eric.kow at gmail.com>**20081026080744] hunk ./src/Darcs/Patch/Info.lhs 30
> -import Data.List ( isPrefixOf )
> hunk ./src/Darcs/Repository/Format.lhs 15
> -import Data.List ( sort, intersperse )
> +import Data.List ( sort )

Motivated by bytestring changes which removed the use of these imports

-- 
Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow>
PGP Key ID: 08AC04F9
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://lists.osuosl.org/pipermail/darcs-users/attachments/20081029/a48298f7/attachment.pgp 


More information about the darcs-users mailing list