[Haskell-cafe] Sequencing Operations in a Monad

Ryan Ingram ryani.spam at gmail.com
Fri Sep 14 22:26:20 EDT 2007

As long as the FFI calls don't make destructive updates to existing
matrices, you can do what you want.

For example, assuming you have:

-- adds the second matrix to the first & overwrites the first
matrixAddIO :: MatrixIO -> MatrixIO -> IO ()

-- creates a new copy of a matrix
matrixCopyIO :: MatrixIO -> IO MatrixIO

Then you can define "safe" operators like this:

module Matrix (
) where
import System.IO.Unsafe (unsafePerformIO)

newtype Matrix = Matrix { liftMatrix :: MatrixIO }

matrixCreate :: MatrixIO -> IO Matrix
matrixCreate m = do
   mNew <- matrixCopyIO m
   return (Matrix mNew)

matrixAdd :: Matrix -> Matrix -> Matrix
matrixAdd (Matrix m1) (Matrix m2) = unsafePerformIO $ do
   mDest <- matrixCopyIO m1
   matrixAddIO mDest m2
   return (Matrix mDest)

What is important is that every use of unsafePerformIO comes with a
proof at some level that the computation really is "functional"; that
is, that the result depends only on the inputs and not on the order of
operations.  An informal sketch of this proof for this bit of code:

1) Matrices are only injected into the system via matrixCreate, which
is an ordered operation in the IO Monad; the "Matrix" constructor is
not exported.

2) matrixCreate copies its source data.  So changes to source MatrixIO
objects can't affect any Matrix.

3) matrixAddIO only modifies its first argument, not the second.  We
only call it with a brand-new matrix object, so it's safe to modify

You should be able to expand this to the point that you can implement
Num operations.  But be warned that efficiency may suffer; lots of
intermediate matrices get created, used once, and then discarded.
It's possible that you can use GHC rules to rewrite & fuse operations
which would help; I'd expect a serious matrix library to do so.  See

 -- ryan

More information about the Haskell-Cafe mailing list