roman-numerals 0.1 → 0.1.0.1
raw patch · 2 files changed
+75/−43 lines, 2 filesdep +base-unicode-symbolsdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: base-unicode-symbols
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- Text/RomanNumerals.hs +66/−39
- roman-numerals.cabal +9/−4
Text/RomanNumerals.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE UnicodeSyntax #-} {-| Parsing and pretty printing of Roman numerals. @@ -27,24 +29,48 @@ , unsafeFromRoman ) where -import Control.Monad.Error-import qualified Data.List as L -------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------++-- base+import Control.Monad ( (>>=), (>>), fail, liftM2, return )+import Data.Bool ( Bool(False, True), otherwise )+import Data.Char ( String )+import Data.Either ( either )+import Data.Eq ( (==) {- needed for desugaring -} )+import Data.Function ( ($), const, id )+import Data.List ( stripPrefix, take )+import Data.Maybe ( Maybe(Just), maybe )+import Data.Ord ( Ord, (<), (>) )+import Prelude ( Num, (-), (+), error, fromInteger )+import Text.Show ( show )++-- base-unicode-symbols+import Data.Eq.Unicode ( (≡) )+import Data.Function.Unicode ( (∘) )+import Data.Monoid.Unicode ( (⊕) )+import Data.Ord.Unicode ( (≥) )++-- mtl+import Control.Monad.Error ( MonadError, throwError )++------------------------------------------------------------------------------- -- Types ------------------------------------------------------------------------------- -- |A configuration with which the 'convertTo' and 'convertFrom' -- functions can be parameterized.-data (Ord n, Num n) =>+data (Ord n, Num n) ⇒ NumeralConfig n = NC { -- |The largest value that can be -- represented using this configuration.- ncMax :: n+ ncMax ∷ n -- |Symbol to represent the value 0. The -- Romans did not have a symbol for -- zero. If set to Nothing a -- 'convertFrom' 0 will throw an error.- , ncZero :: Maybe String+ , ncZero ∷ Maybe String -- |A table of symbols and their numerical -- values. The table must be ordered with -- the largest symbols appearing@@ -54,7 +80,7 @@ -- is associated with the value 0 both -- the convertTo and 'convertFrom' -- function will be undefined.- , ncTable :: [(String, n)]+ , ncTable ∷ [(String, n)] } -------------------------------------------------------------------------------@@ -67,7 +93,7 @@ -- limited to the range [1..3999]. Larger numbers can be represented -- using Roman numerals but you will need notations that are hard or -- impossible to express using strings.-modernRoman :: (Ord n, Num n) => NumeralConfig n+modernRoman ∷ (Ord n, Num n) ⇒ NumeralConfig n modernRoman = NC { ncMax = 3999 , ncZero = Just "" , ncTable = [ ("M", 1000)@@ -90,7 +116,7 @@ -- lower rank symbol can be placed before a higher rank symbol to -- denote the difference between them. Thus a numeral like "IV" will -- not be accepted or generated by this configuration.-simpleRoman :: (Ord n, Num n) => NumeralConfig n+simpleRoman ∷ (Ord n, Num n) ⇒ NumeralConfig n simpleRoman = NC { ncMax = 3999 , ncZero = Just "" , ncTable = [ ("M", 1000)@@ -115,80 +141,81 @@ -- representation is possible with the given configuration. If the -- value of any symbol in the configuration is equal to 0 or a symbol -- is the empty string this function is undefined.-convertTo :: (Ord n, Num n, MonadError String m) => NumeralConfig n -> n -> m String-convertTo nc n | n < 0 = throwError "Roman.convertTo: can't represent negative numbers"- | n > maxN = throwError $ "Roman.convertTo: too large (max = " ++ (show maxN) ++ ")"- | n == 0 = maybe (throwError "Roman.convertTo: no symbol for zero")- return- $ ncZero nc+convertTo ∷ (Ord n, Num n, MonadError String m) + ⇒ NumeralConfig n → n → m String+convertTo nc n | n < 0 = throwError "Roman.convertTo: can't represent negative numbers"+ | n > maxN = throwError $ "Roman.convertTo: too large (max = " ⊕ (show maxN) ⊕ ")"+ | n ≡ 0 = maybe (throwError "Roman.convertTo: no symbol for zero")+ return+ $ ncZero nc | otherwise = go n $ ncTable nc where maxN = ncMax nc go 0 _ = return "" go _ [] = throwError "Roman.convertTo: out of symbols"- go n tab@(~(sym, val) : ts) | n >= val = liftM2 (++) (return sym) $ go (n - val) tab+ go n tab@(~(sym, val) : ts) | n ≥ val = liftM2 (⊕) (return sym) $ go (n - val) tab | otherwise = go n ts -- |Like 'convertTo', but exceptions are promoted to errors.-unsafeConvertTo :: (Ord n, Num n) => NumeralConfig n -> n -> String-unsafeConvertTo nc = either error id . convertTo nc+unsafeConvertTo ∷ (Ord n, Num n) ⇒ NumeralConfig n → n → String+unsafeConvertTo nc = either error id ∘ convertTo nc -- |Converts a number to a modern Roman numeral. See 'convertTo' for -- possible exceptions.-toRoman :: (Ord n, Num n, MonadError String m) => n -> m String+toRoman ∷ (Ord n, Num n, MonadError String m) ⇒ n → m String toRoman = convertTo modernRoman -- |Like 'toRoman', but exceptions are promoted to errors.-unsafeToRoman :: (Ord n, Num n) => n -> String+unsafeToRoman ∷ (Ord n, Num n) ⇒ n → String unsafeToRoman = unsafeConvertTo modernRoman ------------------------------------------------------------------------------- -- Parsing ------------------------------------------------------------------------------- --- convertFrom nc xs == undefined when any symbol in the NumeralConfig+-- convertFrom nc xs ≡ undefined when any symbol in the NumeralConfig -- nc is associated with the value 0. -- |Parses a string as a Roman numeral according to the given -- configuration. An exception will be raised if the input is not a -- valid numeral.-convertFrom :: (Ord n, Num n, MonadError String m) => NumeralConfig n -> String -> m n-convertFrom nc xs | maybe False (== xs) (ncZero nc) = return 0- | otherwise = do n <- (go 0 (ncTable nc) xs)- xs' <- convertTo nc n- if xs == xs'+convertFrom ∷ (Ord n, Num n, MonadError String m) ⇒ NumeralConfig n → String → m n+convertFrom nc xs | maybe False (≡ xs) (ncZero nc) = return 0+ | otherwise = do n ← (go 0 (ncTable nc) xs)+ xs' ← convertTo nc n+ if xs ≡ xs' then return n else throwError "Roman.convertFrom: invalid Roman numeral" where go n _ [] = return n- go _ [] xs = throwError $ "Roman.convertFrom: can't parse: '" ++ (take 5 xs) ++ "'"+ go _ [] xs = throwError $ "Roman.convertFrom: can't parse: '" ⊕ (take 5 xs) ⊕ "'" go n tab@((sym, val) : ts) xs = maybe (go n ts xs) (go (n + val) tab)- $ L.stripPrefix sym xs+ $ stripPrefix sym xs -- |Like 'convertFrom', but exceptions are promoted to errors.-unsafeConvertFrom :: (Ord n, Num n) => NumeralConfig n -> String -> n-unsafeConvertFrom nc = either error id . convertFrom nc+unsafeConvertFrom ∷ (Ord n, Num n) ⇒ NumeralConfig n → String → n+unsafeConvertFrom nc = either error id ∘ convertFrom nc -- |Parses a string as a modern Roman numeral. See 'convertFrom' for -- possible exceptions.-fromRoman :: (Ord n, Num n, MonadError String m) => String -> m n+fromRoman ∷ (Ord n, Num n, MonadError String m) ⇒ String → m n fromRoman = convertFrom modernRoman -- |Like 'fromRoman', but exceptions are promoted to errors.-unsafeFromRoman :: (Ord n, Num n) => String -> n+unsafeFromRoman ∷ (Ord n, Num n) ⇒ String → n unsafeFromRoman = unsafeConvertFrom modernRoman ------------------------------------------------------------------------------- -- Properties ------------------------------------------------------------------------------- -printParseIsId :: (Ord n, Num n) => NumeralConfig n -> String -> Bool+printParseIsId ∷ (Ord n, Num n) ⇒ NumeralConfig n → String → Bool printParseIsId nc xs = either (const True) id- $ do n <- convertFrom nc xs- xs' <- convertTo nc n- return $ xs' == xs+ $ do n ← convertFrom nc xs+ xs' ← convertTo nc n+ return $ xs' ≡ xs -parsePrintIsId :: (Ord n, Num n) => NumeralConfig n -> n -> Bool+parsePrintIsId ∷ (Ord n, Num n) ⇒ NumeralConfig n → n → Bool parsePrintIsId nc n = either (const True) id- $ do xs <- convertTo nc n- n' <- convertFrom nc xs- return $ n' == n+ $ do xs ← convertTo nc n+ n' ← convertFrom nc xs+ return $ n' ≡ n
roman-numerals.cabal view
@@ -1,8 +1,8 @@ name: roman-numerals-version: 0.1+version: 0.1.0.1 cabal-version: >= 1.6 build-type: Simple-stability: provisional+stability: stable author: Roel van Dijk maintainer: vandijk.roel@gmail.com copyright: (c) 2009 Roel van Dijk@@ -18,10 +18,15 @@ functions. Exceptions are dealt with by wrapping the results of conversions in the error monad. +source-repository head+ type: darcs+ location: http://code.haskell.org/~roelvandijk/code/roman-numerals+ library- build-depends: base, mtl ==1.1.*+ build-depends: base >= 3.0.3.1 && < 4.3+ , base-unicode-symbols >= 0.1.1 && < 0.2+ , mtl == 1.1.* exposed-modules: Text.RomanNumerals- extensions: FlexibleContexts