content: import qualified Data.Map.Strict as M
  import Data.Maybe (maybe)
  
  ------------------ ROMAN NUMERALS DECODED ----------------
  
  mapRoman :: M.Map Char Int
  mapRoman =
    M.fromList $
      zip "IVXLCDM" $
        scanl (*) 1 (cycle [5, 2])
  
  fromRoman :: String -> Maybe Int
  fromRoman cs =
    let op l r
          | l >= r = (+)
          | otherwise = (-)
     in snd
          . foldr
            (\l (r, n) -> (l, op l r n l))
            (0, 0)
          <$> traverse (`M.lookup` mapRoman) cs
  
  --------------------------- TEST -------------------------
  main :: IO ()
  main =
    putStrLn $
      fTable
        "Roman numeral decoding as a right fold:\n"
        show
        (maybe "(Unrecognised character seen)" show)
        fromRoman
        [ "MDCLXVI",
          "MCMXC",
          "MMVIII",
          "MMXVI",
          "MMXVII",
          "QQXVII"
        ]
  
  ------------------------ FORMATTING ----------------------
  
  fTable ::
    String ->
    (a -> String) ->
    (b -> String) ->
    (a -> b) ->
    [a] ->
    String
  fTable s xShow fxShow f xs =
    unlines $
      s :
      fmap
        ( ((<>) . rjust w ' ' . xShow)
            <*> ((" -> " <>) . fxShow . f)
        )
        xs
    where
      rjust n c = drop . length <*> (replicate n c <>)
      w = maximum (length . xShow <$> xs)
  