[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