Reputation: 7
I have something this:
func1 :: IO [MyObjectData]
func1 = do
res1 <- quickQuery conn123 "select * from table1" []
return $ map parseMyObjectData res1
where
parseMyObjectData [a, b, c, d, e, f, g, h, i, j, k, l] =
MyObjectData (fromSql a) (fromSql b) (fromSql c) (fromSql d) (fromSql e)
(fromSql f) (fromSql g) (fromSql h) (fromSql i)
(MyObjectDataNested
(fromSql j) (fromSql k) (fromSql l))
It's a simplified version. Is there any way to simplify it? I'm thinking that I should somehow use "fromSql" in map or mapM, but how exactly? Or....
Upvotes: 0
Views: 124
Reputation: 60463
Not really. You could mess with data generics, which is a bit of a sledgehammer unless you are doing this for a number of data types. If it's just one or two instances, I'd just bite the bullet and write out the boilerplate. If you have a big schema like this, it's worth reading the Scrap Your Boilerplate paper and using generics. (N.B. there may be more modern approaches to generics, I'm not sure, this is the only one I've ever used)
There are "cleaner" ways, depending on how you decide your database abstraction works. If your columns are to be treated as a "serial stream", you could make a class for deserializable objects:
import Control.Monad.Supply
class FromSQLStream a where
fromSQLStream :: Supply SqlData a
-- All FromSQLs are FromSQLStreams, but a superclass isn't a good idea
-- for some reasons here...
fromSqlS :: (FromSQL a) => Supply SqlData a
fromSqlS = fromSql <$> supply
(I'm guessing about the names FromSQL
and SqlData
, but this is probably roughly how your SQL library works)
Then at least it composes a bit:
instance FromSQLStream MyObjectDataNested where
fromSQLStream = MyObjectDataNested <$> fromSqlS <*> fromSqlS <*> fromSqlS
instance FromSQLStream MyObjectData where
fromSqlStream =
MyObjectData <$> fromSqlS <*> fromSqlS <*> fromSqlS <*> fromSqlS
<*> fromSqlS <*> fromSqlS <*> fromSqlS <*> fromSqlS
<*> fromSqlS <*> fromSQLStream
Which is awful repetitive, but under the hood it's less repetitive than it looks because of the type information floating around directing things. But I don't think you can do better than that without generics.
Upvotes: 1
Reputation: 361
By all likelihood, you can use neither map
nor mapM
, because, for all you've given us to look at, there isn't necessarily any single type to which each of a
, b
, c
, etc. would be mapped. For that matter, I doubt there's any sensible way to simplify this in the general case.
Upvotes: 0