I've written a data type to represent a wordin the memory of a MIX computer.A MIX word has a sign bit and five six-bit bytes.So I created a data type and provided instancesfor all the common numeric typeclasses(that I could think of).Some questions that came to mind:A lot of my instantiationsuse dependencies in the opposite order of the typeclass inheritance.For example, the instance for Enum uses toInteger(which is defined in class (Real a, Enum a) => Integral a).I was worried about operations loopingdue to circular dependencies among the default implementations,but that didn't seem to happen.Is this practice safe? Is it good?I tried to isolate the actual bit-manipulationto toInteger and fromInteger.Is this good?Is there a better way I could do this?What about the logic for the weird signed-zero behavior(e.g., -1 + 1 = -0 but 1 - 1 = +0)?Do my signedOp function and its use seem appropriate?I didn't export the data constructorbecause I didn't want users to be able to writethings like MixWord True (0, 0, 0, 0, 65).Is it reasonable to force the use of fromIntegral and friends?Finally, the Ord implementation:I'd prefer this to have fewer than five clauses!Usually, I'd try to implement this with <>-chained comparings,but I didn't immediately see a way to do this.Thoughts?I've also included a very incomplete spec file.Mix.hsmodule Mix (MixWord, signBit) whereimport Data.Word (Word8)import Data.Ratio ((%))import Data.List (intercalate)import Data.Ix (Ix, range, index, inRange)type Byte = Word8  -- really, we want Word6, but it doesn't exist-- | A word in a MIX computer,-- which contains a sign bit and four bytes,-- where the first byte listed is the MSB.data MixWord = MixWord Bool (Byte, Byte, Byte, Byte, Byte)-- | The range of values that can be stored in a single byte-- (i.e., one greater than the maximum value in a single byte).byteSize :: Integral a => abyteSize = 64-- | Extract the raw sign bit from a MIX word,-- returning True for semipositive numbers-- and False for seminegative numbers.signBit :: MixWord -> BoolsignBit (MixWord sign _) = signinstance Show MixWord where    -- e.g., show (68 :: MixWord) = + 00 00 00 01 04    show (MixWord sign (b1, b2, b3, b4, b5)) =        intercalate   (signString : map format [b1, b2, b3, b4, b5])      where        signString | sign       = +                   | otherwise  = -        format x =            let shown = show x            in  if length shown < 2                    then '0' : shown                    else showninstance Eq MixWord where    MixWord _ (0, 0, 0, 0, 0) == MixWord _ (0, 0, 0, 0, 0) = True    MixWord sign1 bytes1 == MixWord sign2 bytes2 =        (sign1, bytes1) == (sign2, bytes2)instance Ord MixWord where    compare (MixWord _ (0, 0, 0, 0, 0)) (MixWord _ (0, 0, 0, 0, 0)) = EQ    compare (MixWord False _) (MixWord True _) = LT    compare (MixWord True _) (MixWord False _) = GT    compare (MixWord True bs1) (MixWord True bs2) = compare bs1 bs2    compare (MixWord False bs1) (MixWord False bs2) = compare bs2 bs1instance Real MixWord where    toRational mw = toInteger mw % 1instance Bounded MixWord where    maxBound = MixWord True (byteSize, byteSize, byteSize, byteSize, byteSize)    minBound = MixWord False (byteSize, byteSize, byteSize, byteSize, byteSize)instance Enum MixWord where    succ m      | m == maxBound   = error          Enum.succ{MixWord}: tried to take `succ' of maxBound      | otherwise = m + 1    pred m      | m == minBound   = error          Enum.pred{MixWord}: tried to take `pred' of minBound      | otherwise = m - 1    toEnum = fromInteger . toInteger    fromEnum = fromInteger . toIntegerinstance Integral MixWord where    toInteger (MixWord sign (b1, b2, b3, b4, b5)) =        signum *            (fromIntegral b5 + byteSize *            (fromIntegral b4 + byteSize *            (fromIntegral b3 + byteSize *            (fromIntegral b2 + byteSize * fromIntegral b1))))      where        signum | sign       = 1               | otherwise  = -1    quotRem m1 m2 =        let (q, r) = quotRem (toInteger m1) (toInteger m2)        in  (fromIntegral q, fromIntegral r)instance Num MixWord where    m1 + m2 = signedOp (+ (toInteger m2)) m1    m1 * m2 = signedOp (* (toInteger m2)) m1    abs = fromInteger . abs . toInteger    signum = fromInteger . signum . toInteger    negate (MixWord sign bytes) = MixWord (not sign) bytes    fromInteger n =        let magnitude       = abs n            (rest5, byte5)  = quotRem magnitude byteSize            (rest4, byte4)  = quotRem rest5 byteSize            (rest3, byte3)  = quotRem rest4 byteSize            (rest2, byte2)  = quotRem rest3 byteSize            (_    , byte1)  = quotRem rest2 byteSize        in  MixWord (n >= 0) ( fromInteger byte1                             , fromInteger byte2                             , fromInteger byte3                             , fromInteger byte4                             , fromInteger byte5                             )-- | Create a MixNum version of an Integer binary operator,-- but when the output is zero-- use the sign of the first operand-- for the sign of the resulting zero.signedOp :: (Integer -> Integer) -> MixWord -> MixWordsignedOp op m@(MixWord s0 _) = case op (toInteger m) of    0       -> MixWord s0 (0, 0, 0, 0, 0)    other   -> fromInteger otherinstance Ix MixWord where    range (a, b) = [a..b]    index (a, _) x = (fromInteger . toInteger) (x - a)    inRange (a, b) x = a <= x && x <= bMixSpec.hsmodule MixSpec (main, spec) whereimport Test.Hspecimport Mix (MixWord, signBit)spec :: Specspec = describe MixWord $ do    describe toInteger and fromInteger $ do        it converts 0     $ 0 `shouldBe` tofrom 0        it converts 7     $ 7 `shouldBe` tofrom 7        it converts (-3)  $ (-3) `shouldBe` tofrom (-3)    describe show $ do        it shows zero as semipositive $            show (0 :: MixWord) `shouldBe` + 00 00 00 00 00        it shows negated zero as seminegative $            show (negate 0 :: MixWord) `shouldBe` - 00 00 00 00 00    describe ordering $ do        it lets 0 == -0 $            compare (0 :: MixWord) (negate 0 :: MixWord) `shouldBe` EQ        it lets -0 == 0 $            compare (negate 0 :: MixWord) (0 :: MixWord) `shouldBe` EQ        it lets -1 < -0 $            compare (negate 1 :: MixWord) (negate 0 :: MixWord) `shouldBe` LT        it lets -1 < 0 $            compare (negate 1 :: MixWord) (0 :: MixWord) `shouldBe` LT        it lets -3 < -2 $            compare (negate 3 :: MixWord) (negate 2 :: MixWord) `shouldBe` LT        it lets 3 > 2 $            compare (negate 3 :: MixWord) (negate 2 :: MixWord) `shouldBe` GT    context sign shenanigans $ do        it lets  1 - 1  = +0 $ signBit (1 - 1)  `shouldBe` True        it lets -1 + 1  = -0 $ signBit (-1 + 1) `shouldBe` False        it lets pred 1  = +0 $ signBit (pred 1) `shouldBe` True        it lets succ -1 = -0 $ signBit (succ (-1)) `shouldBe` False  where    tofrom = toInteger . (fromInteger :: Integer -> MixWord)main :: IO ()main = hspec spec
Manual instantiations of common typeclasses for a machine word data type
haskell