Question

Below is an implementation of modular arithmetic Num instance that is modeled after Data.Fixed.

I'd like to write an alternate implementation of fromRational which would look something like:

fromRational r = case invertMod (denominator r) theModulus of
                   Just inv -> normalize $ (numerator r) * inv
                   Nothing -> error "..."

but I can't figure out what I would use for theModulus. Unlike the other type-class functions, I don't have a value of type Modular a around on which I can call modulus.

{-# LANGUAGE NoMonomorphismRestriction #-}

import Math.NumberTheory.Moduli (invertMod)
import Data.Ratio (numerator, denominator)

class HasModulus a where
  modulus :: p a -> Integer

withType :: (p a -> f a) -> f a
withType foo = foo undefined

withModulus :: (HasModulus a) => (Integer -> f a) -> f a
withModulus foo = withType (foo . modulus)

newtype Modular a = M Integer

normalize :: HasModulus a => Integer -> Modular a
normalize x = withModulus $ \m -> M (x `mod` m)

instance (HasModulus a) => Num (Modular a) where
  (M a) + (M b) = normalize (a+b)
  (M a) - (M b) = normalize (a-b)
  (M a) * (M b) = normalize (a*b)
  negate (M a)  = normalize (-a)
  abs           = id
  signum _      = fromInteger 1
  fromInteger   = normalize

instance (HasModulus a) => Fractional (Modular a) where
  recip ma@(M a) = case invertMod a (modulus ma) of
                     Just inv -> normalize $ inv
                     Nothing  -> error "divide by zero error"
  ma / mb        = ma * (recip mb)
  fromRational r = (fromInteger $ numerator r) / (fromInteger $ denominator r)

instance (HasModulus a) => Show (Modular a) where
  show mx@(M x) = (show x) ++ " mod " ++ (show $ modulus mx)

data M5 = M5
data M7 = M7

instance HasModulus M5 where modulus _ = 5
instance HasModulus M7 where modulus _ = 7

bar = 1 / 3

main = do print $ (bar :: Modular M5)
          print $ (bar :: Modular M7)
Was it helpful?

Solution

A way to write fromRational closer to your initial Ansatz is

fromRational r = let x = case invertMod (denominator r) (modulus x) of
                           Just inv -> normalize $ (numerator r) * inv
                           Nothing -> error "..."
                 in x

Since the result is of type Modular a, we can obtain the modulus from it (without inspecting it). So all we need is to name it, so that we can refer to it where it's needed.

OTHER TIPS

I figured it out... the key is to use the withModulus function:

mdivide :: HasModulus a => Integer -> Integer -> Modular a
mdivide x y = withModulus $ M . mdiv' x y
                where mdiv' x y m =
                        case invertMod y m of
                          Just inv -> (x * inv) `mod` m
                          Nothing  -> error "..."

and then...

fromRational r = mdivide (numerator r) (denominator r)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top