Home > Blockchain >  Converting quarters, dimes, and pennies to dollars and cents in Haskell
Converting quarters, dimes, and pennies to dollars and cents in Haskell

Time:01-25

I'm trying to convert quarters, dimes, and pennies to dollars and cents, but I can't seem to get the code to run.

I tried this code:

-- Declaration
toCents :: Int -> Int -> Int -> Int
toDollar :: Int -> Int -> Int -> Int

-- Definition
toCents quarter dime penny  = quarter*25   dime*10   penny
toDollar quarter dime penny = (toCents quarter dime penny) / 100) - ((toCents quarter dime penny mod 100) / 100)

main :: IO()
main = do
 let quarter = 12
 let dime = 67
 let penny = 43
 let sumDollar = toDollar quarter dime penny
 let sumCents = toCents quarter dime penny - (toDollar quarter dime penny) * 100
 print ("Conversion of "    show quarter    " quarters, "    show dime    " dimes, and "    show penny    " pennies to dollars and cents:")
 print(sumDollar    " dollars and "    sumCents    " cents.")

And it resulted to this error:

[1 of 1] Compiling Main             ( main.hs, main.o )
main.hs:7:68: error:
    • Couldn't match expected type ‘(Integer -> Integer -> Integer)
                                    -> Integer -> Int’
                  with actual type ‘Int’
    • The function ‘toCents’ is applied to five arguments,
      but its type ‘Int -> Int -> Int -> Int’ has only three
      In the first argument of ‘(/)’, namely
        ‘(toCents quarter dime penny mod 100)’
      In the second argument of ‘(-)’, namely
        ‘((toCents quarter dime penny mod 100) / 100)’
  |
7 | toDollar quarter dime penny = (toCents quarter dime penny / 100) - ((toCents quarter dime penny mod 100) / 100)
  |                                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
main.hs:17:8: error:
    • Couldn't match expected type ‘[Char]’ with actual type ‘Int’
    • In the first argument of ‘(  )’, namely ‘sumDollar’
      In the first argument of ‘print’, namely
        ‘(sumDollar    " dollars and "    sumCents    " cents.")’
      In a stmt of a 'do' block:
        print (sumDollar    " dollars and "    sumCents    " cents.")
   |
17 |  print(sumDollar    " dollars and "    sumCents    " cents.")
   |        ^^^^^^^^^
main.hs:17:40: error:
    • Couldn't match expected type ‘[Char]’ with actual type ‘Int’
    • In the first argument of ‘(  )’, namely ‘sumCents’
      In the second argument of ‘(  )’, namely ‘sumCents    " cents."’
      In the second argument of ‘(  )’, namely
        ‘" dollars and "    sumCents    " cents."’
   |
17 |  print(sumDollar    " dollars and "    sumCents    " cents.")
   |                   

CodePudding user response:

The expression toCents quarter dime penny mod 100 is parsed as

     ((((toCents quarter) dime) penny) mod) 100

or

      ( ( ( (toCents quarter
            ) dime
          ) penny
        ) mod
      ) 100

That looks completely weird, though it's not that weird if you keep in mind how currying works: it corresponds to, in an uncurried language,

      toCents(quarter, dime, penny, mod, 100)

Which is of course still nonsense though: you're feeding mod as an argument to the function toCents, instead of using it right there as an operator. Infix operators with letter names must be written in backticks to apply them, like 37`mod`4, which is the same as mod 37 4. Infix operators always bind weaker than prefix function application, so if you had written

  toCents quarter dime penny `mod` 100

then this would have parsed as

  mod (toCents quarter dime penny) 100

which is probably what you intended. That does however not mean it's good for the task. What you're doing with

    foo/100 - (foo`mod`100)/100

is a very roundabout attempt of implementing simple round-to-negative integer division.

It kind of works in Python, but even there it doesn't really do what it should: Python automatically converts the integers foo and 100 to floats to perform the division, then subtracts to floats from each other and the result is still a float though it happens to have zero fractional part.

Haskell doesn't even allow this mess to happen: it will complain that the / operator is not available on integers. You you really wanted that, you would have to explicitly convert to floats.

You don't want that though: you should simply use the built-in integer division operator instead

toDollar quarter dime penny = toCents quarter dime penny `div` 100

...Assuming it should really have round-to-negative behaviour.

Even better would be to structure your code and data properly, which would avoid both redundant computations and cryptic Int -> Int -> ... signatures:

data DollarValue = DollarValue {
       dollarPartVal, centPartVal :: Int
     }
data DollarCoins = DollarCoins {
       quarterCoinCount, dimeCoinCount, pennyCoinCount :: Int
     }

coinsValue :: DollarCoins -> DollarValue
coinsValue (DollarCoins q d p) = DollarValue dollars cents
 where (dollars, cents) = (25*q   10*d   p)`divMod`100

import Text.Printf

main = do
   let quarter = 12
       dime = 67
       penny = 43
   let DollarValue sumDollars sumCents
        = coinsValue $ DollarCoins quarter dime penny
   printf
    "Conversion of %i quarters, %i dimes, and %i pennies: %i dollars and %i cents.\n"
                   quarter      dime          penny       sumDollars     sumCents
  • Related