jameswilddev
jameswilddev

Reputation: 612

Pulling out a column in all records in a Sqlite table into a concatenated string in Haskell with Persist

I'm trying to learn Haskell, specifically Snap, Blaze HTML5 and Persist. I would like to take every row in a table, select a single column from it, and then concatenate the values into a single string.

I've previously worked with C#'s LINQ quite extensively and under Entity Framework I could do it like this:

String.Join(", ", dbContext.People.Select(p => p.Name));

This would compile down to SELECT Name FROM People, with C# then concatenating those rows into a string with ", " in between.

To try and get the concatenation part right, I put this together, which seems to work:

intercalate ", " $ map show [1..10]

(it counts 1-9, concatenates with ", " in between the items)

However, I can't get this to work with Database.Persist.Sqlite. I'm not sure I quite understand the syntax here in Haskell. To contact the DB and retrieve the rows, I have to call: (as far as I understand)

runSqlite "TestDB" $ selectList ([] :: [Filter Person]) [] 0 0

The problem is that I'm not sure how to get the list out of runSqlite. runSqlite doesn't return the type I'm after, so I can't use the return value of runSqlite. How would I do this?

Thank you for reading.


To clarify:

Snap requires that I define a function to return the HTML I wish to send back to the client making the HTTP request. This means that:

page = runSqlite "TestDB" $ do
    {pull data from the DB)

Is no-go as I can't return the data via the runSqlite call, and as far as I know I can't have a variable in the page function which is set within the runSqlite do block. All examples I can find just write to IO in the runSqlite do block, which is not what needs to be done here.

Upvotes: 1

Views: 267

Answers (2)

jameswilddev
jameswilddev

Reputation: 612

It hadn't clicked that if I didn't use a do block with runSqlite, the result of the last call in the statement was the return value of the statement - this makes total sense.

https://gist.github.com/egonSchiele/5400694

In this example (not mine) the readPosts function does exactly what I'm after and cleared up some Haskell syntax confusion.

Thank you for your help @Sibi.

Upvotes: 1

Sibi
Sibi

Reputation: 48664

The type of runSqlite is:

runSqlite :: (MonadBaseControl IO m, MonadIO m)  => Text -> SqlPersistT (NoLoggingT (ResourceT m)) a -> m a

And the type of selectList is:

[Filter val] -> [SelectOpt val] -> m [Entity val]

So, you can actually, use the nice do notation of Monad, to extract it:

runSqlite "TestDB" $ do
  myData <- selectList ([] :: [Filter Person]) [] 0 0
  -- Now do stuff with myData

The <- thing gets the list out of the monad. I would suggest you to go through this chapter to get an idea of how Persistent is used. Note that the chapters in the book assume a basic Haskell understanding.


The issue is that I want to use the selectList outside of runSqlite as I need to pass the concatenated string to a Blaze HTML5 tag builder: body $ do p (concatenated list...)

For this case, just define a function that does your intended task:

myLogic :: [SqlColumnData] -> String -- Note that SqlColumnData is hypothetical
myLogic xs = undefined

And then just call them appropriately in your main function:

main = runSqlite "TestDB" $ do
  myData <- selectList ([] :: [Filter Person]) [] 0 0
  let string = myLogic myData
  -- do any other remaining stuff

Upvotes: 1

Related Questions