Bitwise-oriented semigroup and monoid newtype wrappers for Data.Bits.Bits instances
I've found myself needing these often, and given the existence of similar newtypes for directing (<>)
and mempty
for a range of other types, these are conspicuous by their absence. Additionally, while oneBits
isn't technically necessary, it's a lot more concise than complement zeroBits
, and I found myself needing it often.
To that end, I've sketched up this implementation. Technically speaking, GND isn't needed, but it means I don't have to repeat myself a lot. These could be made even more concise with coerce
, but I decided not to do that, since that would require TypeApplications
. I've limited derivation to methods that are specifically about bitwise operations, rather than stuff like Num
.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.Bits.Extra where
import Data.Bits ( Bits(..)
, FiniteBits(..)
)
-- | Monoid under bitwise AND.
newtype Conj a = Conj { getConj :: a }
deriving (Eq, Bounded, Enum, Bits, FiniteBits)
instance (Bits a) => Semigroup (Conj a) where
(Conj x) <> (Conj y) = Conj (x .&. y)
instance (Bits a) => Monoid (Conj a) where
mempty = Conj . complement $ zeroBits
-- | Monoid under bitwise OR.
newtype Disj a = Disj { getDisj :: a }
deriving (Eq, Bounded, Enum, Bits, FiniteBits)
instance (Bits a) => Semigroup (Disj a) where
(Disj x) <> (Disj y) = Disj (x .|. y)
instance (Bits a) => Monoid (Disj a) where
mempty = Disj zeroBits
-- | Semigroup under bitwise XOR.
newtype Xor a = Xor { getXor :: a }
deriving (Eq, Bounded, Enum, Bits, FiniteBits)
instance (Bits a) => Semigroup (Xor a) where
(Xor x) <> (Xor y) = Xor (x `xor` y)
-- | Semigroup under bitwise \'equality\'; defined as '1' if the corresponding
-- bits match, '0' otherwise.
newtype Iff a = Iff { getIff :: a }
deriving (Eq, Bounded, Enum, Bits, FiniteBits)
instance (Bits a) => Semigroup (Iff a) where
(Iff x) <> (Iff y) = Iff . complement $ (x `xor` y)
-- not strictly necessary, but would be a big help
-- probably should be INLINE
oneBits :: (Bits a) => a
oneBits = complement zeroBits
Potentially this could include more, such as instances of Ord
based on lexicographic ordering on the bits, rather than defaulting to the underlying one (such as the one for Int
, which I believe is numeric). However, that's a separate issue. I also don't know if these belong in Data.Bits
or Data.Semigroup
(or Data.Monoid
I guess).
Trac metadata
Trac field | Value |
---|---|
Version | 8.6.2 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | libraries/base |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Update (17/01/2021)
Based on my IRC conversation with @carter and @bgamari, as well as the comments here and on the MR, I've decided that the design will be as follows:
- The names for the newtypes will be
And
,Ior
,Xor
andIff
(the last subject to change if SIMD instruction names prove to be helpful, but that's unlikely). - They will live in
Data.Bits
. - To break the cyclic dep problem, the current
Data.Bits
will becomeGHC.Bits
, re-exported fromData.Bits
. -
oneBits
will also live inData.Bits
. -
Show, Read
instances for all of the above newtypes will be stock-derived.
I think this is the right choice in light of everything I've seen and heard.