Question

I have a function that might fail, so the value it returns needs to be wrapped in a Maybe. It uses another function that might also fail, and that is also wrapped in a Maybe. The problem is, to get the types to work out in an intermediate calculation, I have to "prematurely" lift a function to work in the Maybe context. That results in my getting a type Maybe [Maybe Integer], when what I want is Maybe [Integer]. The function in question is the exptDecipherString function, the function that's forcing "premature" lifting is the modularInverse function.

import Data.Char
import Control.Applicative
import Control.Monad
import Math.NumberTheory.Powers

--Helpers

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 

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

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

--Exponentiation Ciphers

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

exptDecipher::Integer->Integer->Integer->Maybe Integer
exptDecipher m q c | relativelyPrime q (m - 1) = Just $ powerMod c q m
                   | otherwise = Nothing

exptEncipherString::Integer->Integer->String->Maybe [Integer]
exptEncipherString m k p | relativelyPrime k (m - 1) = mapM (exptEncipher m k) plaintext
                         | otherwise = Nothing
    where
        plaintext = textToDigits p

exptDecipherString::Integer->Integer->[Integer]->Maybe String
exptDecipherString m k c | relativelyPrime k (m - 1) = fmap digitsToText plaintext
                         | otherwise = Nothing
    where
        q = modularInverse k (m - 1)
        plaintext = mapM (exptDecipher m <$> q <*>) (map pure c)
Was it helpful?

Solution

The first thing you should usually try for "How can X become Y" is hoogle. In this case, it recommends you use join, which will collapse two Maybes into one.

With some code restructuring, the Maybe monad could also be used to help you here.

When all else fails, roll your own solution that uses functions or a case statement with pattern matching.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top