Please correct my use of Maybe Monad

I am implementing a small program that makes exponentiation ciphers. Some of the calculations can fail, for example, when calculating modular inversion. I've used Maybe to deal with similar setbacks. But now I am stuck as I need to "inject" the value inside, possibly into another partially applied function. I know that if I had a function that took one argument, I would use bind for that.

import Data.Char
import Math.NumberTheory.Powers

extendedGcd::Integer->Integer->(Integer, Integer)
extendedGcd a b | r == 0 = (0, 1)
                | otherwise = (y, x - (y * d))
                where
                    (d, r) = a `divMod` b
                    (x, y) = extendedGcd b r

modularInverse::Integer->Integer->Maybe Integer
modularInverse n b | relativelyPrime n b = Just . fst $ extGcd n b
                   | otherwise = Nothing
                   where
                        extGcd = extendedGcd

relativelyPrime::Integer->Integer->Bool
relativelyPrime m n | gcd m n == 1 = True
                    | otherwise = False

textToDigits::String->[Integer]
textToDigits p = map (\x->toInteger (ord x - 97)) p

digitsToText::[Integer]->String
digitsToText d = map (\x->chr ((fromIntegral x) + 97)) d

exptEncipher::Integer->Integer->Integer->Maybe Integer
exptEncipher m k p | relativelyPrime k (p - 1) = Just $ powerMod p k m 
                   | otherwise = Nothing

exptDecipher::Integer->Integer->Integer->Integer
exptDecipher m q c = powerMod c q m

exptEncipherString::Integer->Integer->String->[Maybe Integer]
exptEncipherString m k p = map (exptEncipher m k) plaintext
    where
        plaintext = textToDigits p

exptDecipherString::Integer->Integer->[Maybe Integer]->Maybe String
exptDecipherString m k c = (fmap digitsToText) plaintext
    where
        q = modularInverse k (m - 1)
        plaintext = map (fmap $ exptDecipher m q) c

      

Specifically, my problem is with the exptDecipherString function, where I needed to inject the monad-encapsulated value in q into the exptDecipher function, which I would then raise to work on c. What is the correct way to do this? Also, I'm worried that I end up with a [Maybe Char] list instead of the Maybe string I want. I am having problems explaining all of this. Can someone enlighten me?

+3


source to share


1 answer


You can use sequence

and ap

to get development types. First for their signatures:

ap :: Monad m => m (a -> b) -> m a -> m b
sequence :: Monad m => [m a] -> m [a]

      

Please note what sequence

directs your concern about stock [Maybe Char]

instead Maybe String

. Both are in Control.Monad

(note that you will need to import ap

). We can use them like this:

exptDecipherString :: Integer -> Integer -> [Maybe Integer] -> Maybe String
exptDecipherString m k c = fmap digitsToText plaintext
  where
    q = modularInverse k (m - 1)
    plaintext = sequence $ map (ap $ fmap (exptDecipher m) q) c

      



We can get to this by working through types. First we appy exptDecipher

up m

, which gives us a type function Integer -> Integer -> Integer

. We want to apply this to q

, but it's a Maybe Integer

, so we have to use fmap (exptDecipher m) q

that then has a type Maybe (Integer -> Integer)

. Then we can put ap

on the front and get something like Maybe Integer -> Maybe Integer

. Then we type this over c

, which gives us [Maybe Integer]

which we can turn inside out with sequence

.

It might not work - if there are bugs in the logic, etc., but at least it compiles.

A couple of notes: you can use infix <$>

and <*>

from operators Control.Applicative

instead of fmap

and ap

accordingly for a slightly stronger syntax, and yours relativelyPrime

can be written much easier than relativelyPrime m n = gcd m n == 1

.

+3


source







All Articles