
Peter Simons wrote:
Ben Rudiak-Gould writes:
Must contexts be used in a single-threaded manner? If so, I would expect this interface:
start :: IO ctx feed :: ctx -> Buffer -> IO () commit :: ctx -> IO a
'feed' cannot have this signature because it needs to update the context.
Sure it can -- it's just like writeIORef :: IORef a -> a -> IO (). If the return of writeIORef were IO (IORef a) instead, it would be confusing: does it return the same IORef or a different one? If a different one, does the original one remain unchanged? If the same one, why bother returning it when the caller already had it? That's what confused me about your proposed interface.
If not, I would expect this interface:
start :: ctx feed :: ctx -> Buffer -> IO ctx commit :: ctx -> a
Both 'start' and 'commit' need to be in the IO monad, because creating and finalizing the context may involve IO calls. (Just think of a computation that does internal buffering in memory which is accessed through another Ptr.)
In this interface contexts are supposed to be immutable Haskell values, so there's no meaning in creating new ones or finalizing old ones. The initial empty context is just a value, and the final MD5 hash (or whatever) is a pure function of the final context. Yes, this would likely involve internal use of unsafePerformIO in the implementation, but that's what it's there for (a required part of the FFI). Dealing with reuse of state might also make the library too inefficient in practice, which would be a bigger problem.
feedBuffer :: ctx -> Buffer -> IO ctx feedSTUArray :: ctx -> STUArray s Int Word8 -> ST s ctx feedUArray :: ctx -> UArray Int Word8 -> ctx
I would implement feedSTUArray and friends as wrappers around the Ptr interface, not as primitive computations of the stream processor.
I think it's impossible to do this safely, but it would be great if I were wrong. -- Ben