This is a development of laws for state variables in Haskell. These laws can be used in proofs, to "push" a read or write past another, using rewriting. The laws are:
See also: ConcurrencyLaws
The conditions on number 6 seems like not quite mathematically elegant enough to me, although I am unsure.
I was thinking of breaking it up into three rules:
Giving the same treatment to File I/O:
Let holdPosition m = hTell h >>= \x -> m >>= \y -> hSeek h AbsoluteSeek x >> return y
Let getAt h i = holdPosition $ hSeek h AbsoluteSeek i >> hGetChar h
Let hFileSize h = holdPosition $ hSeek h SeekFromEnd 0 >> hTell h
Let
protectedGetAt h i = hFileSize h >>= \sz -> if i >= sz then
return '\0'
else
getAt h i
Let putAt h i x = holdPosition $ hSeek h AbsoluteSeek i >> hPutChar h x
data Ref = Position Handle | Char Handle Int
Let
read (Position h) = liftM Left (hTell h)
read (Char handle i) = liftM Right (getAt handle i)
Let
write (Position h) (Left x) = hSeek h AbsoluteSeek x
write (Char handle i) (Right x) = putAt handle i x
Then the following laws hold:
The state laws (except 7 and 8) hold for read and write given above.
putAt h i x >> getAt h j = protectedGetAt h j >>= \y -> putAt h i x >> return y, when i /= j
hSeek h m i >> hSeek h x j = hSeek h x j, x /= RelativeSeek
hSeek h RelativeSeek i = hTell h >>= \j -> hSeek h AbsoluteSeek (i + j)
openFile path mode >>= \h -> hSeek h AbsoluteSeek 0 >> return h = openFile path mode