Proposal: Add hasBitSize to Data.Bits.Bits

There is currently no way to know whether or not calling Data.Bits.bitSizewill crash your program. I propose extending the Bits class to include: hasBitSize :: Bits b => b -> Bool such that it returns False for Integer and True for the other instances since the vast majority of instances are finite, it may be reasonable to set the default definition to hasBitSize _ = False Discussion Period: 2 weeks

On Fri, Jul 27, 2012 at 03:06:04PM -0400, Edward Kmett wrote:
There is currently no way to know whether or not calling Data.Bits.bitSizewill crash your program.
I propose extending the Bits class to include:
hasBitSize :: Bits b => b -> Bool
such that it returns False for Integer and True for the other instances
Can you give an example of a situation in which you would use hasBitSize, and do something useful if it returned False? Would it be better to move bitSize into a separate class?
since the vast majority of instances are finite, it may be reasonable to set the default definition to
hasBitSize _ = False
Did you mean True? Either way, I think I would personally prefer not to have a default, so that people need to actually check existing instances. Thanks Ian

On Fri, Jul 27, 2012 at 3:42 PM, Ian Lynagh
On Fri, Jul 27, 2012 at 03:06:04PM -0400, Edward Kmett wrote:
There is currently no way to know whether or not calling Data.Bits.bitSizewill crash your program.
I propose extending the Bits class to include:
hasBitSize :: Bits b => b -> Bool
such that it returns False for Integer and True for the other instances
Can you give an example of a situation in which you would use hasBitSize, and do something useful if it returned False?
The following can only work if bitSize is well defined. traverseBits :: (Applicative f, Bits b) => (Bool -> f Bool) -> b -> f b traverseBits f b = snd . Prelude.foldr step (bitSize b - 1,0) <$> traverse (f . testBit b) [0 .. bitSize b - 1] where step True (n,r) = (n - 1, setBit r n) step _ (n,r) = (n - 1, r) to work around this I've had to use: traverseBits :: (Applicative f, Bits b) => (Bool -> f Bool) -> b -> f b traverseBits f b = Prelude.foldr step 0 <$> traverse g bits where g n = (,) n <$> f (testBit b n) bits = Prelude.takeWhile hasBit [0..] hasBit n = complementBit b n /= b -- test to make sure that complementing this bit actually changes the value step (n,True) r = setBit r n step _ r = r where I'm manually probing each bit to see that changing it changes the value. Used with the other combinators in lens the former just crashes when you use foldMapOf :: ((c -> Const m d) -> a -> Const m b) -> (c -> m) -> a -> m foldMapOf l f = getConst . l (Const . f) toListOf :: ((c -> Const [c] d) -> a -> Const [c] b) -> a -> [c] toListOf l = foldMapOf l return Now when we use it with an explicit signature both of these can do reasonable things: ghci> toListOf traverseBits (5 :: Int) [True,False,True,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False] But with defaulting choosing Integer ghci> toListOf traverseBits 5 the former instance will crash, whereas the latter properly returns an infinite lazy list [True,False,True,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,False,... It would be much nicer not to have to probe using complementBit creating ever larger temporary integers just to see if the list of valid bit positions hadn't been exhausted. Then I could test at the start to know whether I should use [0..] or [0..bitSize-1] and get away with much less code and much less unnecessary memory allocation. -Edward Would it be better to move bitSize into a separate class? I don't think so. Ultimately, you should be able to know for every numeric type if it has a fixed or variable number of bits. In a perfect world 'd rather just have bitSize return a Maybe that way we don't have to truck with partial functions in the API. However, the case for just adding hasBitSize is that it avoids breaking existing code.
since the vast majority of instances are finite, it may be reasonable to set the default definition to
hasBitSize _ = False
Did you mean True?
Yes
Either way, I think I would personally prefer not to have a default, so that people need to actually check existing instances.
Also perfectly reasonable. -Edward
participants (2)
-
Edward Kmett
-
Ian Lynagh