我一直在玩一些简单的二进制编码,它似乎在大多数情况下都能正常工作,直到我添加了 state monad。计划是使用状态来保存到目前为止我已写入字节串的内容的查找表,然后将偏移量写入字符串的先前实例而不是复制它们。
我检查并运行了所有类型,但后来我注意到它只是写出链中的最后一条指令。我改为使用 Control.Monad.State.Strict 但这没有任何区别,所以我怀疑我在其他地方犯了一个基本错误,但我不确定在哪里 - 我已经将代码缩减为基本功能. 另外,有没有更惯用的方式来做到这一点?
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative
import qualified Control.Monad.State.Strict as S
import Data.Binary.Put
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
data SState = SState {
wsPosition :: Int
-- plus whatever else
}
initialState = SState 0
type StatePut = S.State SState Put
class StateBinary a where
sput :: a -> StatePut
incPos :: Int -> S.State SState ()
incPos amnt = do
(SState p) <- S.get
S.put $ SState (p + amnt)
writeSized :: Int -> (a -> Put) -> a -> StatePut
writeSized n f x = do
incPos n
return (f x)
writeInt32 :: Int -> StatePut
writeInt32 = writeSized 32 putWord32be . fromIntegral
writeBS :: BS.ByteString -> StatePut
writeBS b = writeSized (BS.length b) putByteString b
data SomeData = SomeData {
sdName :: BS.ByteString
, sdAge :: Int
, sdN :: Int
} deriving (Show, Eq)
instance StateBinary SomeData where
sput (SomeData nm a n) = do
writeBS nm
writeInt32 a
writeInt32 n
testData = SomeData "TestName" 30 100
runSPut :: StateBinary a => a -> BL.ByteString
runSPut a = runPut $ S.evalState (sput a) initialState
-- runSPut testData returns "\NUL\NUL\NULd"