Jojika
Jojika

Reputation: 7

How can I simplify this in Haskell?

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

Answers (2)

luqui
luqui

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

shinobi
shinobi

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

Related Questions