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?
source to share
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
.
source to share