I'm trying to use the cached function to prevent multiple db queries in different widgets and handlers:
newtype CachedBobId key
= CachedBobId { unCachedBobId :: key }
deriving Typeable
getBob' :: Handler BobId
getBob' = do
uncle <- runInputGet $ ireq textField "bobsuncle"
(Entity bob _) <- runDB $ getBy404 $ UniqueBob uncle
return bob
getBob :: Handler BobId
getBob = do
a <- getBob'
let b = return $ CachedBobId a
c <- cached b
return $ unCachedBobId c
And in a widget somewhere:
renderDerp :: Widget
renderDerp = do
--these are used in the shakespeare files
lolBob <- handlerToWidget $ getBob
nutherBob <- handlerToWidget $ getBob
$(widgetFile "test")
This compiles but the query to get the ID still runs multiple times.
What am I doing wrong? Or is there a better way to only get bob once and use him in every handler and widget?
I'm pretty new to Yesod, but I think you just need to tweak getBob
getBob :: Handler BobId
getBob = unCachedBobId <$> cached (CachedBobId <$> getBob')
The problem is that your current getBob function starts its do block with a <- getBob'. Remember that a do block sequences monadic actions, so you actually end up calling getBob' first thing every time getBob is called. In an ironic twist, after you've done this, you create a cached version of a handler which returns what you just got from getBob', but end up querying that cached version exactly once (right afterwards with c <- cached b), then it just falls out of scope and the garbage collector gets it.
In the solution I present above, you wrap whatever getBob' gives you in CachedBobId. Then, you pass that handler CachedBobId <$> getBob' :: Handler (CachedBobId BobId), to cached, which gives you back another handler cached (CachedBobId <$> getBob') of the same type, but with caching. Finally, you extract whatever the cached handler gives you to get back a Handler BobId.
Related
When using reflex-gi-gtk-0.2.0.0
I can access a dynamic from within an event:
submitButtonE4 <- eventOnSignal submitButton #clicked
(
do
let processDyn dynCompany = do
case dynCompany of
Just company -> do
path <- chartAnnualROA company fileOptions800x600 --generateChart company
Gtk.imageClear chartImage
Gtk.set chartImage [#file := T.pack defaultReportPath]
--return x -- path
case T.null $ T.pack path of
True -> return "" --dynCompany
Nothing -> return "" -- dynCompany
return $ ffor maybeCompanyDyn processDyn
>>= )
But in order to be evaluated, I need to bind it to a label:
sink submitClickStatusLabel [#label :== ffor submitButtonE4 (T.pack . show)]
which does not work as it is in Dynamic (SpiderTimeline x) (IO (Maybe Company)).
So instead I must go and get the info that the dynamic was bound to:
(
do
name <- Gtk.get companyCboxBoxEntryWidget #text
case Map.lookup name companyMap of
Just company -> do
path <- chartAnnualROA company fileOptions800x600 --generateChart company
Gtk.imageClear chartImage
Gtk.set chartImage [#file := T.pack defaultReportPath]
return path
Nothing -> return "../investingRIO/src/Data/Reports/initialChart.svg"
>>= )
and now I can sink it and cause evalution.
sink submitClickStatusLabel [#label :== ffor submitButtonE (T.pack . show)]
I am unable to find any way to force the evaluation when using the first method. How do I force the evalution without sinking to another widget?
Thanks
Here is the new version, based on Kritzefitz's answer.
An event for selecting a company from a combobox, which is same as before
companySelectionE <- eventOnAttribute companyCboxBoxEntryWidget #text
Replaced dynamic with a behavior.
companySelectionB <- hold Nothing $ ffor companySelectionE (`Map.lookup` companyMap)
generateChart (renamed from processDyn) returns a () instead of a FilePath, which was an attempt at forcing evaluation, now done by performEvent.
let
generateChart company = do
case company of
Just companyJ -> do
chartAnnualROA companyJ fileOptions800x600
Gtk.imageClear chartImage
Gtk.set chartImage [#file := T.pack defaultReportPath]
return ()
Nothing -> return ()
submitClickedE now uses eventOnSignal0 instead of eventOnSignal
submitClickedE <- eventOnSignal0 submitButton #clicked
Creating a chart from the selected company is now a behavior instead of a dynamic.
let generateChartB = generateChart <$> companySelectionB
Now I use <# to create a new event from the submit event and generate chart behavior.
let generateChartE = generateChartB <# submitClickedE
And the use of performEvent, which eliminated all the labels I was creating and sinking to in an attempt to get my IO to evaluate. It also eliminated the FilePath return from generateChart, aslo an attempt to force evaluation.
processedCompany <- performEvent $ runGtk <$> generateChartE
Thank cleared up a lot of things for me, thanks.
Here it is in a single quote for easier reading:
companySelectionE <- eventOnAttribute companyCbox #text
companySelectionB <- hold Nothing $ ffor companySelectionE (`Map.lookup` companyMap)
let
generateChart company = do
case company of
Just companyJ -> do
chartAnnualROA companyJ fileOptions800x600
Gtk.set chartImage [#file := T.pack defaultReportPath]
return ()
Nothing -> return ()
submitClickedE <- eventOnSignal0 submitButton #clicked
let generateChartB = generateChart <$> companySelectionB
let generateChartE = generateChartB <# submitClickedE
processedCompany <- performEvent $ runGtk <$> generateChartE
I think most of your trouble comes from the fact, that you want to do substantial amounts of work inside eventOnSignal . This place is not intended to do the actual heavy lifting of your business logic and it doesn't provide you with the proper context to effectively work with reactive values, such as Dynamics, as you are currently experiencing.
The actual use case for the eventOnSignal* family of functions is to obtain basic inputs for your reactive network. The input provided by a button doesn't carry any actual information. It just provides the information when the button has been clicked. For cases like this you usually don't want to use eventOnSignal directly, but rather eventOnSignal0, so let's do that:
submitClickedE <- eventOnSignal0 submitButton #clicked
The type returned by this is submitClickedE :: Event t (). As you can see, the Event has a () as its value, which is what we want, because merely clicking the button doesn't produce any value by itself. But you want to call an IO-producing function on the value inside processDyn, so let's first construct the IO action you want to execute:
let processDynD = processDyn <$> dynCompany
The assignment here has the type processDynD :: Dynamic t (IO (Maybe Company)). As you can see, the IO hasn't been executed yet. Luckily reflex provides an operation to execute IO actions inside reactive values, called performEvent :: Event t (Performable m a) -> m (Event t a). There are two things about this type, that don't quite fit what we need at the moment. First, it expects the monad to be performed to be a Performable m whereas we have IO, but we will get to that in a moment. The second and more pressing concern is that performEvent expects an Event, not a Dynamic. This makes sense, because you can't execute an IO action continuously. You have to decide when the IO action is executed.
AIUI you want the IO to be executed, when the submitButton is clicked. So we want an Event that fires whenever submitClickedE fires, but it should fire the current value inside processDynD. Doing something like this is called “sampling a Behavior with an Event” and can be done using the operator (<#). In your case you want to sample a Dynamic, but you can always turn a Dynamic into a Behavior using current. So to get the expected Event you can use this:
let processDynE = current processDynD <# submitClickedE
The assignment has the value processDynE :: Event t (IO (Maybe Company)). But as you can see, the IO still hasn't been executed. We can now do that using performEvent as discussed earlier:
processedCompany <- performEvent $ runGtk <$> processDynE
We use runGtk to lift the IO in processDynE to the required Performable m. The returned value has the type processedCompany :: Event t (Maybe Company). You can now sink this into your output label, as was your original intention:
sink submitClickStatusLabel [#label :== T.pack . show <$> processedCompany]
Note though, that unlike your original attempt, we now ended up with an Event instead of a Dynamic. If you actually need a Dynamic from all of this, you have to construct it from the Event using holdDyn initialValue processedCompany. But then you have to provide an initialValue because otherwise there is no value for the Dynamic before the submitButton has been clicked for the first time.
I have written this ManagerSettings to log all requests and responses for my http-conduit application. (By the way, I am importing ClassyPrelude).
tracingManagerSettings :: ManagerSettings
tracingManagerSettings =
tlsManagerSettings { managerModifyRequest = \req -> do
putStr "TRACE: "
print req
putStrLn ""
pure req
, managerModifyResponse = \r -> do
responseChunks <- brConsume $ responseBody r
let fullResponse = mconcat responseChunks
putStr "TRACE: RESPONSE: "
putStrLn $ decodeUtf8 fullResponse
pure $ r { responseBody = pure fullResponse }
}
However, it's not working - when I use it, the application is hanging and trying to consume all the RAM in the machine after printing the first request and first response, which suggests some kind of infinite loop.
Also, the request is printed twice.
I made a previous attempt that was similar, but didn't modify r. That failed because after I had already read the response completely, there was no more response data to read.
If I replace this with tlsManagerSettings, http-conduit works again.
My application is using libstackexchange, which I have modified to allow the ManagerSettings to be customised. I am using http-conduit version 2.2.4.
How can I diagnose the issue? How can I fix it?
managerModifyResponse doesn't work with a Response ByteString, it works with a Response BodyReader, where type BodyReader = IO ByteString along with the contract that if it produces a non-empty ByteString there is more input that can be read.
The problem you're running into is that pure fullResponse never returns an empty ByteString unless it always does. You need to provide a somewhat more complex IO action to capture the intended behavior. Maybe something along these lines (untested):
returnOnce :: Monoid a => a -> IO (IO a)
returnOnce x = do
ref <- newIORef x
pure $ readIORef ref <* writeIORef ref mempty
As for how to debug this? Not sure about generic methods. I was just suspicious that you probably needed a solution along these lines, and the docs for BodyReader confirmed it.
Since sodium has been deprecated by the author I'm trying to port my code to reactive-banana. However, there seem to be some incongruencies between the two that I'm having a hard time overcomming.
For example, in sodium it was easy to retrieve the current value of a behaviour:
retrieve :: Behaviour a -> IO a
retrieve b = sync $ sample b
I don't see how to do this in reactive-banana
(The reason I want this is because I'm trying to export the behaviour as a dbus property. Properties can be queried from other dbus clients)
Edit: Replaced the word "poll" as it was misleading
If you have a Behaviour modelling the value of your property, and you have an Event modelling the incoming requests for the property's value, then you can just use (<#) :: Behavior b -> Event a -> Event b1 to get a new event occurring at the times of your incoming requests with the value the property has at that time). Then you can transform that into the actual IO actions you need to take to reply to the request and use reactimate as usual.
1 https://hackage.haskell.org/package/reactive-banana-1.1.0.0/docs/Reactive-Banana-Combinators.html#v:-60--64-
For conceptual/architectural reasons, Reactive Banana has functions from Event to Behavior, but not vice versa, and it makes sense too, given th nature and meaning of FRP. I'm quite sure you can write a polling function, but instead you should consider changing the underlying code to expose events instead.
Is there a reason you can't change your Behavior into an Event? If not, that would be a good way to go about resolving your issue. (It might in theory even reveal a design shortcoming you have been overlooking so far.)
The answer seems to be "it's sort of possible".
sample corresponds to valueB, but there is no direct equivalent to sync.
However, it can be re-implemented with the help of execute:
module Sync where
import Control.Monad.Trans
import Data.IORef
import Reactive.Banana
import Reactive.Banana.Frameworks
data Network = Network { eventNetwork :: EventNetwork
, run :: MomentIO () -> IO ()
}
newNet :: IO Network
newNet = do
-- Create a new Event to handle MomentIO actions to be executed
(ah, call) <- newAddHandler
network <- compile $ do
globalExecuteEV <- fromAddHandler ah
-- Set it up so it executes MomentIO actions passed to it
_ <- execute globalExecuteEV
return ()
actuate network
return $ Network { eventNetwork = network
, run = call -- IO Action to fire the event
}
-- To run a MomentIO action within the context of the network, pass it to the
-- event.
sync :: Network -> MomentIO a -> IO a
sync Network{run = call} f = do
-- To retrieve the result of the action we set up an IORef
ref <- newIORef (error "Network hasn't written result to ref")
-- (`call' passes the do-block to the event)
call $ do
res <- f
-- Put the result into the IORef
liftIO $ writeIORef ref res
-- and read it back once the event has finished firing
readIORef ref
-- Example
main :: IO ()
main = do
net <- newNet -- Create an empty network
(bhv1, set1) <- sync net $ newBehavior (0 :: Integer)
(bhv2, set2) <- sync net $ newBehavior (0 :: Integer)
set1 3
set2 7
let sumB = (liftA2 (+) bhv1 bhv2)
print =<< sync net (valueB sumB)
set1 5
print =<< sync net (valueB sumB)
return ()
Starting out with Haskell and Yesod, probably getting a bit too far with Yesod relative to Haskell :)
I build entities using Persistent via
share [mkPersist sqlSettings, mkMigrate "migrateAll"][persistLowerCase|
Game
title String
company String
UniqueTitle title
deriving Show
Tag
label String
description String Maybe
UniqueLabel label
deriving Show
GameTag
gameId GameId
tagId TagId
UniqueGameTag gameId tagId
|]
-- Yesod related code ...
In main I have
main :: IO ()
main = do
let taggings = fromFile :: [(Game, Tag)] -- fromFile code not included
runStderrLoggingT $ withSqlitePool ":inmemory:" 10 $ λpool → liftIO $ do
runResourceT $ flip runSqlPool pool $ do
runMigration migrateAll
let (g, t) = head taggings
gid ← insert g
tid ← insert t
insert (GameTag gid tid)
warp 3000 $ App pool
Doing this I get the first relation into the database, and by selecting elements from the list I can add more 'by hand', but I can't figure out how to get all the relations into the database by somehow iterating over taggings. How do i define a function that I can map over taggings ::[(Game, Tag)] and inserts the game tags of the type GameTag constructed
by Persistent?
The main trick here isn't in pulling out the function, that is easy:
f (g, t) = do
gid <- insert g
tid <- insert t
insert (GameTag gid tid)
The trick is knowing how to use this.... Standard map won't work alone, because the function is defined in a monad (you can use it, it will just give you a list of actions back without running them).
map f taggings -- returns just a list, type [ResourceT IO a], doesn't run anything
Here are two ways to actually run the actions from within main.
sequence (map f taggings) --sequentially runs the actions in the list
or, the more readable
forM taggings f
or, in the slightly more verbose
forM taggings $ \tagging -> do
f tagging
You also might want to look at mapM. Also also should learn about forM_ and sequence_ to supress the (often useless) return values.
You have a sequence of actions that prefer to be executed in chunks due to some high-fixed overhead like packet headers or making connections. The limit is that sometimes the next action depends on the result of a previous one in which case, all pending actions are executed at once.
Example:
mySession :: Session IO ()
a <- readit -- nothing happens yet
b <- readit -- nothing happens yet
c <- readit -- nothing happens yet
if a -- all three readits execute because we need a
then write "a"
else write "..."
if b || c -- b and c already available
...
This reminds me of so many Haskell concepts but I can't put my finger on it.
Of course, you could do something obvious like:
[a,b,c] <- batch([readit, readit, readit])
But I'd like to hide the fact of chunking from the user for slickness purposes.
Not sure if Session is the right word. Maybe you can suggest a better one? (Packet, Batch, Chunk and Deferred come to mind.)
Update
I think there was a really good answer last night that I read on my phone but when I came back to look for it today it was gone. Was I dreaming?
I don't think you can do exactly what you want, since what you describe exploits haskell's lazy evaluation to have the evaluation of a force the actions that compute b and c, and there's no way to seq on unspecified values.
What I could do was hack together a monad transformer that delayed actions sequenced via >> so that they could be executed all together:
data Session m a = Session { pending :: [ m () ], final :: m a }
runSession :: Monad m => Session m a -> m a
runSession (Session ms ma) = foldr (flip (>>)) (return ()) ms >> ma
instance Monad m => Monad (Session m) where
return = Session [] . return
s >>= f = Session [] $ runSession s >>= (runSession . f)
(Session ms ma) >> (Session ms' ma') =
Session (ms' ++ (ma >> return ()) : ms) ma'
This violates some monad laws, but lets you do something like:
liftIO :: IO a -> Session IO a
liftIO = Session []
exampleSession :: Session IO Int
exampleSession = do
liftIO $ putStrLn "one"
liftIO $ putStrLn "two"
liftIO $ putStrLn "three"
liftIO $ putStrLn "four"
trace "five" $ return 5
and get
ghci> runSession exampleSession
five
one
two
three
four
5
ghci> length (pending exampleSession)
4
This is very similar to what Haxl does.
For more info:
Open sourcing haxl - Facebook Code Blog
ICFP 2014 talk
You could use the unsafeInterleaveIO function. It is a dangerous function that can introduce bugs to your program if not used carefully, but it does what you're asking for.
You can insert it into your example code like this:
lazyReadits :: IO [a]
lazyReadits = unsafeInterleaveIO $ do
a <- readit
r <- lazyReadits
return (a:r)
unsafeInterleaveIO makes the action as a whole lazy, but once it starts evaluating it will evaluate as if it had been strict. This means in my above example: readit will run as soon as something tests whether the returned list is empty or not. If I'd used mapM unsafeInterleaveIO (replicate 3 readit) instead, then readit would only be run when the actual elements of the list are evaluated, which would make the contents of the list depend on the order in which its elements are inspected, which is one example of how unsafeInterleaveIO can introduce bugs.