4
\$\begingroup\$

I started to teach myself Haskell recently. Here is some code I wrote to convert a roman numeral into an arabic numeral:

import Control.Arrow ((>>>))
import Data.Maybe (fromJust)

fromRoman :: String -> Int
fromRoman = translateFromRoman >>> applyNegationRule >>> sum

translateFromRoman :: String -> [Int]
translateFromRoman = map (\r -> fromJust $ lookup r [('I',1),('V',5),('X',10),('L',50),('C',100),('D',500),('M',1000)])

applyNegationRule :: [Int] -> [Int]
applyNegationRule [x]      = [x]
applyNegationRule (x:y:xs) = (if x < y then -x else x) : applyNegationRule (y:xs)

I like it quite a bit. But some questions remain. First of all I never (at least I can't remember) came across (>>>) in other code so far. Everyone seems to use:

fromRoman :: String -> Int
fromRoman = sum . applyNegationRule . translateFromRoman

It produces the same output, but for me as someone used to read from top left to bottom right the first variant is just more readable. You could also write it like this:

fromRoman :: String -> Int
fromRoman = translateFromRoman
        >>> applyNegationRule
        >>> sum

To make it even more text like readable. So where's the catch?

Another question arising from this code is: Should I define the functions translateFromRoman and applyNegationRule in fromRoman ... where? Again I see it quite a lot but IMHO it also lacks readability. At the moment readability is one of the most important metrics for me to determine code quality.

And last but note least: Am I doing something bad in translateFromRoman and applyNegationRule or are there some better (faster/shorter or ofcourse more readable) alternatives?

\$\endgroup\$
3
  • \$\begingroup\$ You could watch this video for inspiration. \$\endgroup\$
    – mkrieger1
    Sep 9 '15 at 11:12
  • \$\begingroup\$ @mkrieger1 Can you elaborate on what kind of inspiration I should get from watching the video? For me my code is easier to read than the code in the video. The guy even states: "One really nasty way to generate integers from roman numbers." at the end of the video. To be clear I have working code and I'm looking for non nasty ways. \$\endgroup\$
    – amkoroew
    Sep 10 '15 at 5:53
  • \$\begingroup\$ I just remembered having seen this video some time ago and didn't want it to go unnoticed. I didn't mean to say that what is shown in the video is superior, only that it may open up some alternative views. \$\endgroup\$
    – mkrieger1
    Sep 10 '15 at 9:22
3
\$\begingroup\$

Since the Roman numerals are fixed and known in advance, it would be more efficient to write:

translateRomanDigit :: Char -> Int
translateRomanDigit 'I' = 1
translateRomanDigit 'V' = 5
...

translateFromRoman = map translateRomanDigit

The compiler can implement translateRomanDigit as a jump table or as a binary search. Using lookup will always perform a linear search.

As for >>> vs . - there should be no difference except that >>> requires you to import Control.Arrow whereas . is provided for in the Prelude.

\$\endgroup\$
2
  • \$\begingroup\$ I think you're right about translateFromRomanDigit it even adds two benefits. First of all it makes translateFromRoman way more readable and it also gets rid of the Maybe type. \$\endgroup\$
    – amkoroew
    Sep 10 '15 at 6:12
  • 2
    \$\begingroup\$ According to this answer on SO there is a difference between >>> and . namely the number of reduction steps is not always the same. \$\endgroup\$
    – amkoroew
    Sep 10 '15 at 6:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.