Making my own monadic type based on list

haskell

Solution

You could use a record type to make your output clearer:

data Loan = Loan {final :: Double, 
                  rate  :: Double, 
                  loan  :: Integer, 
                  years :: Int}
   deriving Show

printloans :: [Loan] -> IO()
printloans = mapM_ print

Use `printloans loans` or `printloans loans'` at the ghci prompt.

Edit: I forgot to include the definition of `dp`. It's for rounding to a given number of decimal places:

dp :: Int -> Double -> Double
n `dp` a = (/ 10.0^n).fromInteger.round.(* 10.0^n) $ a

Here's a way using a list directly:

loans = [Loan {final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs, 
               rate  = ir,
               loan  = amt,
               years = yrs}  
        | ir <- [0.005*x | x <- [4..10]],
          amt <- [1000*x | x <- [1..3]],
          yrs <- [1..4]
        ]

But if you like the monadic style, you can use:

loans' = do
  ir <- [0.005*x | x <- [4..10]]
  amt <- [1000*x | x <- [1..3]]
  yrs <- [1..4]
  return Loan {final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs, 
               rate  = ir,
               loan  = amt,
               years = yrs}

which benefits from fewer commas, and it's easier to change the order of the `<-` lines to change the order of the answers. You can add extras to your `Loan` record and calculate with them. You get output like this:

*Main> printloans loans'
Loan {final = 1020.0, rate = 2.0e-2, loan = 1000, years = 1}
Loan {final = 1040.4, rate = 2.0e-2, loan = 1000, years = 2}
Loan {final = 1061.21, rate = 2.0e-2, loan = 1000, years = 3}
Loan {final = 1082.43, rate = 2.0e-2, loan = 1000, years = 4}
Loan {final = 2040.0, rate = 2.0e-2, loan = 2000, years = 1}
Loan {final = 2080.8, rate = 2.0e-2, loan = 2000, years = 2}
...
...

EDIT:

You told me elsewhere you'd like output like `ir_5% yrs_3 amt_4000 tot_4360.5`. It's uglier, but here's a way of doing that sort of thing:

loans'' = do
  ir <- [0.005*x | x <- [4..10]]
  amt <- [1000*x | x <- [1..3]]
  yrs <- [1..4]
  let final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs
  return $ "final_" ++ show final
        ++ ",  ir_" ++ show ((2 `dp`) $ ir*100.0)    -- rounded away a rounding error in 3.5% 
        ++ "%,  amt_" ++ show amt 
        ++ ",  yrs_" ++ show yrs

When you do `mapM_ putStrLn loans''` you get output like

final_1020.0,  ir_2.0%,  amt_1000,  yrs_1
final_1040.4,  ir_2.0%,  amt_1000,  yrs_2
final_1061.21,  ir_2.0%,  amt_1000,  yrs_3
final_1082.43,  ir_2.0%,  amt_1000,  yrs_4
final_2040.0,  ir_2.0%,  amt_2000,  yrs_1
....

but I think the record type is much nicer - its output is easier to read and there's less messing about with strings.

Problem

I want to use Haskell to solve a financial combinatorial problem, the list monad seems to be a good fit for this. Now, my problem with the list monad is its inability to give names to the values involved. I will try to exemplify: ``` loan = [1000*x | x <- [1..3]] interest_rate = [0.005*x | x <- [4..10]] calc = do l <- loan i <- interest_rate return (l*i) ``` Running calc above gives me a list of numbers (`[20.0,25.0,30.0,35.0,40.0, ... ]`), but I can't tell what the loan and interest rate is used for each calculation. I get lost here, my intuition tells me to create my own monadic type of, say `HelpfulNumber :: (String,[Double])` and somehow say that: `>>=` and `return` should be `>>= . snd` and `return . snd` Am I on the right course here, or is there a better way? I am feeling a bit lost to be honest.

Original source