Stephen Cagle
Stephen Cagle

Reputation: 14524

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

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?

Upvotes: 2

Views: 283

Answers (2)

Peaker
Peaker

Reputation: 2354

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

Upvotes: 5

Lambdageek
Lambdageek

Reputation: 12475

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.

Upvotes: 9

Related Questions