This is kind of a soft question, but in the following code, there's a lot of duplication in the section marked "caesar ciphers." What's the "Haskell" way to deal with this? Should I make a higher order function? I thought about that, but I don't know what makes sense. Is there a "cipher" type that I could define for making ciphers?
Also, I know it may appear a little bit overengineered, in the sense that I'm doing the same error checking in two places, but I think it makes sense from the perspective of what each of the functions "means." Suggestions?
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))
--Caesar Ciphers
caesarEncipher::Integer->Integer->Integer->Maybe Integer
caesarEncipher r s p | relativelyPrime r 26 = Just $ mod (r * p + s) 26
| otherwise = Nothing
caesarDecipher::Integer->Integer->Integer->Maybe Integer
caesarDecipher r s c | relativelyPrime r 26 = mod <$> ((*) <$> q <*> pure (c - s)) <*> pure 26
| otherwise = Nothing
where
q = modularInverse r 26
caesarEncipherString::Integer->Integer->String->Maybe String
caesarEncipherString r s p | relativelyPrime r 26 = fmap digitsToText $ mapM (caesarEncipher r s) plaintext
| otherwise = Nothing
where
plaintext = textToDigits p
caesarDecipherString::Integer->Integer->String->Maybe String
caesarDecipherString r s c | relativelyPrime r 26 = fmap digitsToText $ mapM (caesarDecipher r s) ciphertext
| otherwise = Nothing
where
ciphertext = textToDigits c
bruteForceCaesarDecipher::String->[Maybe String]
bruteForceCaesarDecipher c = caesarDecipherString <$> [0..25] <*> [0..25] <*> pure c