stimesDefault, stimesIdempotent etc throw exception on Integral types without a zero.
For example
data BinP -- also called Pos
= BE
| B0 BinP
| B1 BinP
like in bin package or just
newtype Natural1 = N1 Natural
instance Integral N1 where
toInteger (N1 n) = 1 + toInteger n
With these
*Data.Bin Data.Semigroup> stimes (1 :: Integer) "foo"
"foo"
*Data.Bin Data.Semigroup> stimes (2 :: Integer) "foo"
"foofoo"
*Data.Bin Data.Semigroup> stimes (3 :: Integer) "foo"
"foofoofoo"
*Data.Bin Data.Semigroup> stimes (1 :: BinP) "foo"
"*** Exception: arithmetic underflow
*Data.Bin Data.Semigroup> stimes (2 :: BinP) "foo"
"*** Exception: arithmetic underflow
*Data.Bin Data.Semigroup> stimes (3 :: BinP) "foo"
"*** Exception: arithmetic underflow
The stimesDefault is defined as
stimesDefault :: (Integral b, Semigroup a) => b -> a -> a
stimesDefault y0 x0
| y0 <= 0 = errorWithoutStackTrace "stimes: positive multiplier expected"
| otherwise = f x0 y0 where f ...
but I think it can be as well defined as
stimesDefault :: (Integral b, Semigroup a) => b -> a -> a
stimesDefault y0 x0
| y0 >= 1 = f x0 y0 where ...
| otherwise = errorWithoutStackTrace "stimes: positive multiplier expected"
and similarly for stimesIdempotent
Fixing mtimesDefault would be trickier as it is defined as
stimesIdempotentMonoid :: (Integral b, Monoid a) => b -> a -> a
stimesIdempotentMonoid n x = case compare n 0 of
LT -> errorWithoutStackTrace "stimesIdempotentMonoid: negative multiplier"
EQ -> mempty
GT -> x
and using compare is probably very welcome, although for types people care about, like Int or Integer, or even BinP or Natural/1, having n >= 1 first case is probably a win.
Edited by Oleg Grenrus