I have a few functions (some of them are nested) in the do block, which have a shared object (for example, a user session):
main = do
session <- establishConnection
user <- getUser
firstFunction user session
secondFunction session
firstFunction user session = do
options <- getOptions
nestedFunction options session
I want to avoid a many duplicated arguments in the functions, so how can I pass a session object into the firstFunction/secondFunction/nestedFunction without a function argument usage? E.g.:
main = do
session <- establishConnection
user <- getsUser
firstFunction user
firstFunction user = do
session <- someMagic
....
Depending on if your functions will change or not the "shared object" you can use a Reader monad or a State monad. The first one in case the "shared object" is read-only, the second one if you want to read and modify the "shared object". I'm going to do an example with the Reader because it is easier:
import Control.Monad.Reader
data Config = Config {
user :: String
, session :: Session
}
type ConnectionState a = ReaderT Config IO a
firstFunction :: ConnectionState ()
firstFunction = do
conf <- ask
-- do something with conf
return ()
secondFunction :: ConnectionState ()
secondFunction = do
conf <- ask
-- do something with conf
return ()
realMain :: ConnectionState ()
realMain = do
firstFunction
secondFunction
main :: IO ()
main = do
conn <- establishConnection
user <- getUser
runReaderT realMain (Config user conn)
The Config data type is the information that you want to pass to the functions, feel free to add or delete fields there. The ConnectionState is the Reader monad that you will use to "share" the Config. It basically encapsulate Config in a "read-only container". Every function has type ConnectionState and can use the function ask to read the Config from the monad. Note that in my example every function has type ConnectionState () but you
The main function is used to create a Config and then to call realMain with it. realMain calls all the functions involved in your program that need the Config.
EDIT: note that if you don' want user in your shared object but as argument of firstFunction you can remove it from Config and change the type of firstFunction to firstFunction :: String -> ConnectionState ()
There are two ways.
The first is probably the more accepted of the two but it is also more complicated. You use a state monad (or if you don't need to modify session a Reader monad). Since you are working in IO you need monad transformation as well to stack the two of them together.
http://en.wikibooks.org/wiki/Haskell/Monad_transformers
The library to use is called (mtl).
The type of your functions will become:
firstFunction :: User -> ReaderT Session IO returntype
If you use this a lot in your project you can make a type synonym
type MyMonad a = ReaderT Session IO a
your someMagic function will be ask
firstFunction user = do
session <- ask
the backdraw of this is that since the outermost monad is ReaderT if you want to perform IO, you have to lift it. This is best achieved using the liftIO function. However you don't need to do anything to run a third function of the same monad.
firstFunction user = do
session <- ask
liftIO (do some io)
thirdFunction
of course you need to specify the session when you first run your monad
main = do
session <- establishConnection
flip runReader session $ do
firstFunction
secondFunction
The second ways is sort of a hack
http://www.haskell.org/haskellwiki/Top_level_mutable_state
It lets you have global IORef's, which you can access from everywhere. The advantage of this is that you can avoid all the annoying lift functions. The disadvantage is that it's a hack and that it is global :).
A third solution is using closures: make local functions, which use session, with let inside the do-block:
main = do
session <- establishConnection
user <- getUser
let firstFunction = do
options <- getOptions
nestedFunction options session
let secondFunction = do
<...>
firstFunction
secondFunction
Related
I just start coding in Haskell recently and I start getting use of do block. I'm coming from Scala world, and after read a book and some blogs I see that do block was the inspiration of our from comprehension. But still I´m struggling with the arguments that pass in every function as input -> output
Here in my code I'm using scotty http server library to get a request and I'm trying to persist that request in mySQL.
But in this line where I try top get the value from the Maybe to send to the another function to be persisted
user <- (fmap (\user -> insertUser user) maybeUser)
It never compile since the function does not return the expected type
ActionT Text IO (Maybe User)
but
IO User
Here my hole program
createUser :: ActionM ()
createUser = do maybeUser <- getUserParam
-- Persist the user
_ <- persistUser
json maybeUser
getUserParam :: ActionT Text IO (Maybe User)
getUserParam = do requestBody <- body
return (decode requestBody)
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
persistUser _maybeUser = let maybeUser = _maybeUser in do
user <- maybeUser
user <- (fmap (\user -> insertUser user) maybeUser)
return maybeUser
insertUser :: User -> IO User
insertUser _user = let user = _user in do
conn <- createConnection
status <- execute conn insertUserQuery [MySQLInt32 (intToInt32 $ getUserId user), MySQLText "hello_haskell_world"]
return user
Let's consider the following function:
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
A value of type Maybe User is passed as an argument, and we need this user to be inserted into database. In order to do that, we can use (<$>) (or fmap) function as:
insertUser <$> maybeUser
The resulting type is: Maybe (IO User). Now we need to lift this type to ActionT Text IO (Maybe User) somehow.
Web.Scotty.Trans has a liftAndCatchIO function (also available in Web.Scotty module), which mostly does what we need, but it accepts IO a as an argument, so we need to "swap" Maybe and IO. Let's find a function for this. So sequence does what we need.
As a result, we have the following implementation of persistUser function:
persistUser maybeUser =
liftAndCatchIO $ sequence $ insertUser <$> maybeUser
In Haskell, the type constructor IO is a monad equipped with a return statement that lifts any expression to its IO version.
Nothing prevents us from lifting what is already an IO action to its IO version - giving us a type of the form IO (IO a).
So I can for example write the following program:
main = return . print $ "Hello world"
Which upon execution does exactly nothing.
My question is, what happens under the hood when this main is executed?
Are there any situations where it makes sense to return an IO action?
Under the hood, the runtime effectively discards the result of the IO action main, which is why it is usually defined as IO (). This means that if main actually has a type like IO (IO Int), there is no real issue. The IO action is executed, and the result (another IO action) is discarded, unexecuted.
Deeper in your program, you are more likely to just trigger a type error. For example, fmap doSomething (return . getLine) won't type-check if you meant fmap doSomething getLine.
IO is often approximated to State RealWorld, that is a State monad that operates on the "real world". return for State produces an action that does not alter contained state. So, by analogy, returning anything to IO doesn't do anything as well. Not only when you return some IO a, but any a.
Returning an IO action may be used to build up a computation in one place (capturing some values into a closure along the way) and execute it somewhere else. Much like passing a callback in C.
Actually, to build up an IO computation and pass it somewhere else you can use a let inside of a do-block:
main :: IO ()
main = do
let act = readFile "somefile" -- action is not performed yet
foo act -- pass the action as a parameter
foo :: IO () -> IO ()
foo act = do
.. do something
result <- act -- actually perform the action
..
In this case you don't need to return an IO a value.
But if the process of building that computation itself requires you to perform IO actions, that's where you'd need such a thing. In our example, let's ask user of filename to be opened:
main :: IO ()
main = do
act <- do
filename <- getLine
return (readFile filename)
foo act
Here, we need a filename to create our action, but getLine is in IO as well. That's where additional level of IO comes out. Of course, this example is synthetic, as you can just do filename <- getLine right in main.
I wrote a function
app :: Request -> H.Session H.Postgres IO Response
which accepts web requests and builds responses (consulting the database as needed). To actually send out the responses I made a wrapper
runApp :: H.Postgres -> H.SessionSettings -> Application
runApp pg sess req respond =
respond =<< H.session pg sess (app req)
I pass this function to Warp’s runSettings to loop forever and handle requests:
runSettings appSettings $ runApp pgSettings sessSettings
However this is really bad because it creates a new session for every request which defeats the purpose of the connection pool and prepared statements.
I would like to call runSettings inside H.session rather than the other way around. However runSettings has a signature Settings -> Application -> IO () and once inside IO I have lost access to the session. Is there a way to get back inside Session b m r?
This is a repost of a question from a private email.
Yes, in your example you create a new session for every request, which is unacceptable.
First of all, Session is just and alias to the reader monad transformer, which gives you a direct access to the pool. So you can always do:
session postgresSettings sessionSettings $ do
-- session' :: H.Session b m r -> m r
session' <- flip runReaderT <$> ask
let runApp request respond =
respond =<< session' (app request)
liftIO $ do
-- run warp here
Secondly, ReaderT has a MonadBaseControl instance, which is intended for similar patterns.
I am using the LevelDB library and Snap framework together. I have:
main :: IO ()
main = runResourceT $ do
db <- open "thedb" defaultOptions { createIfMissing = True }
liftIO $ serveSnaplet defaultConfig $ initWeb db
Now in my handler, I'm unsure how to get back to MonadResource IO in order to query the database:
handleWords :: Handler App App ()
handleWords = do
words <- uses thedb $ \db -> $ get db def "words"
writeBS $ pack $ show words
Which gives me a: No instance for (MonadResource IO) arising from a use of 'get'
Any ideas? I feel like I'm missing something about how to properly create a monad "stack". Thanks
MonadResource/ResourceT is one way of acquiring scarce resources in a way that guarantees resources will be freed in the case of an exception. Another approach is the bracket pattern, which is supported by Snap via the bracketSnap function. You can use this to create the ResourceT context needed by LevelDB:
import qualified Control.Monad.Trans.Resource as Res
bracketSnap Res.createInternalState Res.closeInternalState $ \resState -> do
let openAction = open "thedb" defaultOptions { createIfMissing = True }
db <- Res.runInternalState openAction resState
This could be made simpler with some changes in Snap and leveldb:
Instead of only providing the open function, which presumes a MonadResource context, there could be a function which returns a Resource value. I'm making this tweak in persistent for the 2.0 release.
Snap could provide support for either MonadResource or the Resource monad (two separate concepts with unfortunately similar names).
Snap doesn't need to support MonadResource or Resource for you to do this. You're doing the monad transformer composition in the wrong direction. A look at the types will help.
serveSnaplet :: Config Snap AppConfig -> SnapletInit b b -> IO ()
runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a
So you're trying to put an IO in the place that a ResourceT is expected. You should approach this the other way around. Put your open "thedb" ... call inside your application's Initializer with a liftIO. But open is a MonadResource, so you need to use the ResourceT instance to get it into an IO. It will look something like this:
app = makeSnaplet "app" "An snaplet example application." Nothing $ do
...
db <- liftIO $ runResourceT $ open "thedb" defaultOptions
Then store the db handle in your App state and you can retrieve it later using Handler's MonadReader or MonadState instances.
Please forgive my ignorance but, is there is way to invoke a method that returns Handler () from a method that returns only IO ()
For example consider these two methods
onReceiveMessage :: IO ()
onReceiveMessage = do
_ <- putStrLn "Received Message"
_ <- doSomething
return ()
doSomething :: Handler ()
doSomething = return ()
This does not seem to compile. I tried to compile it in a few different ways but all in vein. I am sure it must be possible but just don't know how.
Any Idea?
UPDATE
Expanding on the previous example, lets say I have another function that takes in a value of the previous function and returns IO (). That also does not work.
onReceiveMessage :: IO ()
onReceiveMessage =
doSomething >>= doSomethingElse
doSomething :: Handler MessageStatus
doSomething = return MessageStatus.Success
doSomethingElse :: MessageStatus -> IO ()
doSomethingElse _ = return ()
This also does not seem to work. To invoke an IO action from Handler is possible using the liftIO function for e.g. the below function compiles and works fine. It invokes a IO () action from a function that returns Handler MessageStatus. This is achieved using the liftIO function.
doSomething' :: Handler MessageStatus
doSomething' = (liftIO $ putStrLn "hello world") >> return MessageStatus.Success
Do we have something similar to invoke Handler action from IO?
UPDATE 2
Giving more context and explaining how I solved the problem.
I was trying to listen to RabbitMQ using the amqp package in an Yesod application.
When I receive a message a callback is invoked.
The callback needs to have the signature (Message, Envelope) -> IO ().
From the callback I needed to execute some SQL's.
Now I didn't want to write code to parse the config file and manage my own Connection Pool.
Hence, I wanted to hookup my code with the method provided by Yesod called runDB.
But since its return values are wrapped in Handler I was not able to invoke it as is from the message callback.
What I ended up doing was
Get hold of the App (Foundation) object while its being built in the Application.hs and passed it to my code.
Then create a curried function for the message callback
In the curried function I hold on to the config object that was already build by Yesod, while building the foundation
It was breeze after I figured this out, Using the config object I could read all my settings from the settings.yml and even keep a parallel connection pool.
This connection pool will be used to fire all my queries from the message callback.
Not only this, but because I used this approach, I get logging for free and I can see all my queries on the console without writing a single line of code.
Overall I feel I might have over complicated things but currently I do not know of any better way of doing this. If anyone has a better idea, I would love to hear it.
First of all, your indentions look off. White space in Haskell is important. But more importantly, every statement in a single monadic action has to be within the same monad:
onReceiveMessage :: IO ()
onReceiveMessage = do
putStrLn "Received Message" :: IO ()
doSomething :: Handler () -- Illegal! Must be type IO ()
return () :: IO ()
doSomething :: Handler ()
doSomething = return ()
So no, you can not return a handler from an IO action.
UPDATE
The Handler type is actually an alias for a more complex type which you can think of as "wrapping" the IO monad. What it allows you to do is "lift" an IO action into the Handler monad, but this only goes one way. In order to convert a Handler action into an IO action, there is usually a function provided by the library that is something like runHandler :: Handler [-> other args] -> IO (). I'm not particularly familiar with Yesod, but the pattern is similar across many libraries. This function is used to convert an entire Handler action into an IO action, and is usually reserved for running the server itself.
The complicated answer is that the Handler type is what is known as a monad transformer. They can be pretty tricky to learn at first, but you can just think of it as a monad that contains another monad. Monads can not perform operations outside themselves, so IO can't perform Handler actions, but since Handler contains IO inside it, an IO action can be "lifted" up a level. The really useful thing about monad transformers is that they can be layered indefinitely, and it essentially lets you compose the behavior of different monads together. For instance, imagine if you had a Maybe action, but you also wanted State functionality, Writer functionality, and IO functionality. With monad transformers, this becomes possible, if a bit more complex. However, the order in which these are composed can often matter, since operations can be lifted, but not lowered.
In recent versions you can use Yesod.Core.Handler.handlerToIO to get back into your cozy Handler stack.
But i recommend decoupling messaging and processing:
getOneMsg :: Channel -> Text -> Handler (Message, Envelope)
getOneMsg chan queue = liftIO $ do
mbox <- newEmptyMVar
tag <- consumeMsgs chan queue NoAck (putMVar mbox)
reply <- takeMVar mbox
cancelConsumer chan tag
return reply
myHandler = do
... regular handler code ...
(msg, env) <- getOneMsg chan queue
... regular handler code ...