Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IO in where clause

Tags:

io

haskell

monads

I thought I was beginning to understand IO in Haskell until I ran into the following problem.

I have the following function, which returns type IO Float:

getFundPrice :: Int ->  Int -> IO Float
getFundPrice fund date = do 
                       priceList <- getFundPrice' fund date
                       let h = head priceList
                       return  h

The function getFundPrice' uses the takusen database library and returns a list of type IO [Float].

I'm able to successfully test the getFundPrice function with Hunit using the following:

  p <- getFundPrice 120 20100303
  assertEqual
    "get fund price"
    10.286 
    (p)

The problem that is stumping me is the following function definition:

lastPos :: Int -> (Shares,Float) -> Period -> Fund -> (Shares,Float)
lastPos endDate begPos [] fund  = (fst begPos, trnPrice) 
                               where trnPrice = do
                                             price <- getFundPrice fund endDate
                                             return price                                                                        

The error I'm getting when attempting to compile is " Couldn't match expected type Float' against inferred typeIO Float'"

I thought the *price <- getFundPrice * action would retrieve the price for me as it does with my HUnit code.

What's different about using this in the where clause?

like image 590
Neil Avatar asked Mar 29 '11 04:03

Neil


2 Answers

If you remove the explicit type signature you will see the correct type of:

lastPos :: Int -> (Shares,Float) -> Period -> Fund -> (Shares,IO Float)

Notice the IO Float at the end. You have defined trnPrice to be an IO action that retrieves a floating point value. You have not executed that action to retrieve the value! You can not execute that action from anywhere except for the IO monad, which lastPos is not in. What you can do is:

lastPos :: Int -> (Shares,Float) -> Period -> Fund -> IO (Shares, Float)
lastPos endDate begPos [] fund  = do
    price <- getFundPrice fund endDate
    return (fst begPos, price)

Which lifts all of lastPos into IO.

like image 54
Thomas M. DuBuisson Avatar answered Nov 15 '22 11:11

Thomas M. DuBuisson


The thing about the IO monad is that you can never get rid of the impure taint. Because your getFundPrice function is not pure, nothing calling it can be pure either.

Your function will have to have the type

lastPos :: Int -> (Shares,Float) -> Period -> Fund -> IO (Shares,Float)

If it helps clarify matters, in your where clause the return statement wraps the pure price value back up in IO. There's no way1 to avoid doing this - the IO monad is designed not to allow it.

In fact it's exactly the same as where trnPrice = getFundPrice fund endDate

1 actually there is a backdoor, but it's not a good idea to use it

like image 37
Rob Agar Avatar answered Nov 15 '22 10:11

Rob Agar