
Hi Simon, On Tue, Aug 18, 2009 at 10:41:45PM +0100, Simon Parry wrote:
It seems to work ok (I haven't properly tested it yet) but I feel the pvs function is just ugly. However it seems like its a fairly common requirement for maths modelling ie using Maybe or Error or such to represent conditions on the input variables and then later having to combine those 'wrapped' values with other things.
I don't quite understand what is function `pvs` supposed to do ?? Anyway, I try to guess. It seems that it just applies `(df yield)` to `times` and then multiply the resulting values one by one with `cashflow`. So it seems that you need to lift multiplication `(*)` to the Maybe monad in the second argument only. You can write your own version of `liftM2` (from `Control.Monad`) like this: liftM2snd f a mb = do { b <- mb; return (f a b) } You can verify that liftM2snd == (fmap .) Thus you can rewrite `pvs` as: pvs2 df yield cashflow = multiply cashflow discounts where multiply = zipWithM (fmap . (*)) discounts = map (df yield) times You could alternatively use the library version of `liftM2` but then you need to “lift” the `cashflow` list using `return`. Like this: pvs3 df yield cashflow = multiply (map return cashflow) discounts where multiply = zipWithM (liftM2 (*)) discounts = map (df yield) times When you take the advantage of commutativity of `*` you can write: pvs4 df yield = multiply discounts . map return where multiply = zipWithM (liftM2 (*)) discounts = map (df yield) times or maybe even better: pvs5 df yield = multiply discounts where multiply = zipWithM (flip $ fmap . (*)) discounts = map (df yield) times Anyway, note that all the `pvs` functions (including the your one) return `Nothing` when `(df yield)` returns `Nothing` for at least one related member of `times`. Is that what you want?
Basically it seems inelegant and I feel like I'm confusing the monadic and non-monadic parts?
You are using this function: fce = \c -> (>>= \d -> return $ c*d) which is pretty ugly and not very intuitive. Note that this is simply `liftM2snd (*)` from above, that is, `fmap . (*)`.
help/criticism welcome,
You might want to look at the `liftM` functions from `Control.Monad`. Note that I have inlined the only use of `discount`. In my opinion it improves readability. But it's up to you to judge. I hope this helps a little. I don't know any financial stuff so maybe I didn't understand well what is going on. Sincerely, Jan.
thanks
Simon
module TimeValueMoney1 where
--taken from Financial Numerical Recipes in C++ by B A Odegaard (2006): --Chapter 3
import Control.Monad
--time periods - assumes now is time 0-- times :: [Int] times = [0..]
minusOne :: Double minusOne = -1.0
--can have eg discrete or continuous compounding type Compounding = Double -> Int -> Maybe Double
--discounting and present value-- discreteCompounding :: Compounding discreteCompounding yield elapsed | yield > minusOne = Just ( 1.0/ (1.0 + yield)^elapsed ) | otherwise = Nothing
continuousCompounding :: Compounding continuousCompounding yield elapsed | yield > minusOne = Just (exp( minusOne * yield * fromIntegral elapsed ) ) | otherwise = Nothing
pvs :: Compounding -> Double -> [Double] -> Maybe [Double] pvs df yield cashflow = zipWithM ( \c -> (>>= \d -> return $ c*d ) ) cashflow discounts where discounts = map discount times discount = df yield
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Heriot-Watt University is a Scottish charity registered under charity number SC000278.