Haskell: Can you make the verbosity of an integer conversion more concise?

I have the following piece of code in Haskell:

addm a b m = fromInteger $ mod (fromIntegral a + fromIntegral b) (fromIntegral m)

      

The idea is that if the type a

, b

and m

does not have enough bits for the addition to wrap around (for example, when used Word8

), then the computation would be wrong, so to prevent this, we temporarily convert to Integer

, do the computation and then convert back ...

Is there a more concise way to write this without three fromIntegral

and one fromInteger

?

+3


source to share


3 answers


Expanding on augustuss comment :

addm a b m = i $ mod (i a + i b) (i m)
  where i = fromIntegral

      



( fromInteger

and fromIntegral

work identically on Integer

s, so we can replace fromInteger

with fromIntegral

).

+3


source


On the one hand, you must use toInteger

and fromInteger

, since it fromIntegral

converts to Num

and not Integer

. Unfortunately, there is no specific way to do this, but with multiple combinators, you can make it shorter:

import Data.Function (on)

addm a b m = fromInteger $ flip mod (toInteger m) $ on (+) toInteger a b

      

But with the operator

infixr 9 .:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)

      

you can define it as (if you give the m

first argument)

addm m = fromInteger .: flip mod (toInteger m) .: on (+) toInteger

      



But at this point, it is somewhat difficult to make it more hassle-free without making it unreadable. Casting in Haskell is always explicit, you cannot get around it. Some of the names just make it a little verbose, but you get type safety out of it. Personally, I would probably instead define two versions of this function, one of which refers to Integer

and the other refers to Integral

:

addm :: Integral a => a -> a -> a -> a
addm a b m = fromInteger $ addmInteger (toInteger a) (toInteger b) (toInteger m)
    where
        addmInteger :: Integer -> Integer -> Integer -> Integer
        addmInteger a' b' m' = (a' + b') `mod` m'

      

While this prints more, it cleanly separates logic from type casting. You can shorten this with a combinator like on

:

addm :: Integral a => a -> a -> a -> a
addm a b m = fromInteger $ on3 addmInteger toInteger a b m
    where
        on3 :: (b -> b -> b -> c) -> (a -> b) -> a -> a -> a -> c
        on3 f g x y z = f (g x) (g y) (g z)
        addmInteger :: Integer -> Integer -> Integer -> Integer
        addmInteger a' b' m' = (a' + b') `mod` m'

      

Although this is more general code. I personally like all of this because it on3

can be easily used elsewhere and the business logic is still separate from the casting.

+2


source


You are trying to define

addm a b m = fromInteger $
   (fromIntegral a + fromIntegral b) `mod` (fromIntegral m)

      

But what do you really want?

Based on the description, I will assume that the inputs are the same type as the weekend. I also assume that the type involved is good enough, so the overflow flows nicely modulo k

for some k

. This applies to Word8

( k = 2^8

), Int8

( k = 2^8

), etc., and in GHC it also applies to Word

and Int

. So what am I doing here?

For some q

and some r

such that 0<=r<m

,

a + b - q * m = r

      

This is an essential notion of Euclidean fission (a divMod

bit weird and doesn't implement it exactly, but it's pretty close). But then

a' + b' - q' * m' ≡ r' (mod k)

      

for all a'

, b'

, q'

and m'

congruent to the a

, b

, q

and m

modulo k

. This strongly suggests that overflow will not be a problem once you clarify the actual purpose of your calculation.

0


source







All Articles