Jan Jakubuv jakubuv at gmail.com
Wed Aug 19 07:53:42 EDT 2009

```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

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
>

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
>
>
> --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