[Takusen] Nested Iterations with Takusen
oleg at okmij.org
oleg at okmij.org
Wed Apr 20 04:27:24 BST 2011
Pepe Barbe wrote:
> > secondLevelQuery :: IO Integer
> > secondLevelQuery = do
> > let query = sql "SELECT count(*) from alarms"
> > iter :: Monad m => Integer -> IterAct m Integer
> > iter count _ = result' count
> > withSession connection (doQuery query iter 0)
> >
> > firstLevelQuery :: IO (Integer, Integer)
> > firstLevelQuery = do
> > let query = sql "SELECT count(*) from alarms"
> > iter :: Monad m => Integer -> IterAct m (Integer, Integer)
> > iter count _ = do
> > count' <- secondLevelQuery
> > result' (count', count)
> > withSession connection (doQuery query iter (0,0))
> > If you change it to:
> > iter :: Integer -> IterAct IO (Integer, Integer)
> >
> > Does your type error go away?
> It doesn't work, I get the following error:
> No instance for (Database.Enumerator.QueryIteratee
> (DBM mark Session)
> Database.PostgreSQL.Enumerator.Query
> (IO (IterResult (Integer, Integer)))
> (Integer, Integer)
> Database.PostgreSQL.Enumerator.ColumnBuffer)
Within withSession, the monad to work with is (DBM mark Session)
rather than IO. That was the error pointed out in the error
message. So, the original signature
> > iter :: Monad m => Integer -> IterAct m (Integer, Integer)
was correct, to a point. However, that signature had a flaw too, as
the type-checker pointed out: you were doing the IO within Iter, so
iter cannot be polymorphic over any monad. The solution is to change
the firstLevelQuery code to include the following lines:
> > iter :: MonadIO m => Integer -> IterAct m (Integer, Integer)
> > iter count _ = do
> > count' <- liftIO secondLevelQuery
> > result' (count', count)
The two changes are MonadIO constraint (telling the type-checker that
you will be doing IO), and liftIO to inject an IO a computation into
more general (MonadIO m => m a) computation.
That said, the code is unoptimal, as Jason has pointed out. It is not
a good idea to connect/disconnect several times. Establishing the
connection with the database server takes a lot of time and
resources. DBMS documentation always recommends to connect once, and do
all the work within the connection. You may try something like the
following:
> secondLevelQuery = do
> let query = sql "SELECT count(*) from alarms"
> iter :: Monad m => Int -> IterAct m Int
> iter count _ = result' count
> doQuery query iter 0
>
> firstLevelQuery :: IO (Int, Int)
> firstLevelQuery = do
> let query = sql "SELECT count(*) from alarms"
> iter (count:Int) _ = do
> count' <- secondLevelQuery
> result' (count'::Int, count::Int)
> withSession connection (doQuery query iter (0,0))
I omitted the signature. You may want to check what GHC has inferred (use
the :t command in GHCi) and then supply the inferred signature.
There was another potential problem: I'm not sure a particular backend
supports queries for the general Integer; it has to be either Int or Int64.
More information about the Takusen
mailing list