开发者

How do I make a Record Type bit addressable in Haskell?

开发者 https://www.devze.com 2023-03-16 20:20 出处:网络
I have a record type that is 4 Word32. data MyType = MyType {a :: Word32, b :: Word32, c :: Word32, d :: Word32 }

I have a record type that is 4 Word32.

data MyType = MyType {a :: Word32, b :: Word32, c :: Word32, d :: Word32 }

Most of the time, I want to treat this type as 4 separate Word32. However开发者_运维百科, sometimes I wish to treat it as a single stream of binary data (128 bits long, the concatenation of the 4 Word32). I know that in Python, I would write different accessor functions for this "structure", so that I could read/modify it in both ways. But this is Haskell. I am wondering how an experienced Haskeller would go about this?


There's a class for that :-)

import Data.Bits

newtype MyWord128 = MyWord128 MyType

instance Num MyWord128 where
   -- implement this one

instance Bits MyWord128 where
   -- and then this one, which is what you really want

Check out the documentation for Data.Bits. A complete minimal definition is to provide an implementation of .&., .|., complement, shift, rotate, bitSize and isSigned (or a few other possible combinations: see the doc for details). Annoyingly you also have to implement Num, although it's not entirely clear to me why they defined it that way.


If you really want it to be like a struct of four word32's, you might want to use strict/unpacked fields:

data MyType = MyType { a :: {-# UNPACK #-} !Word32
                     , b :: {-# UNPACK #-} !Word32
                     , c :: {-# UNPACK #-} !Word32
                     , d :: {-# UNPACK #-} !Word32 }
  deriving (Show)

Then, let's define a couple of bit-fiddling functions:

mask :: Bits a => Int -> a
mask count = (1 `shiftL` count) - 1

bitRange :: Bits a => Int -> Int -> a -> a
bitRange low count val = (val `shiftR` low) .&. mask count

Now you can just write 128-bit accessors for this type:

from128 :: Integer -> MyType
from128 val = MyType (bitsFrom 0)
                     (bitsFrom 32)
                     (bitsFrom 64)
                     (bitsFrom 96)
  where
    bitsFrom i = fromIntegral (bitRange i 32 val)

to128 :: MyType -> Integer
to128 (MyType a b c d) =
  foldl' (.|.) 0 [
    bitsTo a 0,
    bitsTo b 32,
    bitsTo c 64,
    bitsTo d 96
  ]
  where
    bitsTo val i = fromIntegral val `shiftL` i

For the a b c d fields, you can just use fclabels. You can also make an fclabel bijective Functor (:<->:):

myType128 :: MyType :<->: Integer
myType128 = to128 :<->: from128
0

精彩评论

暂无评论...
验证码 换一张
取 消