
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 ( Matrix, matrixCreate, matrixAdd ) 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 there. 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 http://www.haskell.org/ghc/docs/latest/html/users_guide/rewrite-rules.html -- ryan