1

I have come across an interesting problem that I thought might be a pertinent place for Template Haskell. I'm working on a web front-end to a database using yesod and yesod-persistant. I am generating my database types using mkPerist function and the persistLowerCase quasi-quoter. My problem is, I need a way to edit fields of the database but writing the hamlet code for six different pages for each of the columns seems incredibly repetitive. I figured I could use Template Haskell to automatically generating the text fields and checkboxes for editing that column of the database given the type. Ideally I would just pass the name of the type to the Template Haskell function and then TH would take care of generating all of the Hamlet for the page. My question is, can I use Template Haskell in this case? Is it the best solution? Particularly, can Template Haskell generate code for other quasi-quoters? Particularly Hamlet? Here is a link to my project as of now: https://github.com/ProspectRidgeTech/PRADatabase Thanks in advance! (PS. Let me know if there is a better way to approach this problem and if you have any suggested edits to my question.)

Brooks
  • 359
  • 3
  • 9

1 Answers1

1

To answer your question : Yes you can, however I wouldn't recommend it. A quasi quoter is just a function which take a string and generate some code, so you when you see

[hamlet|blah blah|]

You could replace it with (or equivalent)

$(hamlet "blah blah")

So nothing stops you in TH to generate a string an call hamlet to it. However, one of the point of TH is type safety. Generating a string to then parse kind of defeat the object of it. Also, this 2 steps code generation will probably be hard to debug.

Anyway, if your problem is to generate table for Persistent entities, I don't think you need TH at all and just use Persistent fields information. I had a similar problem and writen some code which generate an Html table for a list of entities. It shouldn't be hard to modify it do input.

entitiesToTable :: PersistEntity a => (FieldDef -> Text) -> [Entity a] -> Html
entitiesToTable getColumn entities = do
  let eDef = entityDef (map entityVal entities)
  [shamlet|
<table.table.table-bordered.table-striped class="#{unHaskellName $ entityHaskell eDef}">
  <tr>
    <th> Id
    $forall field <- entityFields eDef
      <th> #{getColumn field}
  $forall Entity eit entity  <- entities
    <tr>
      <td.id> #{renderPersistValue $ toPersistValue eit}
      $forall (pfield, fieldDef) <- zip (toPersistFields entity) (entityFields eDef)
        <td class="#{getHaskellName fieldDef}" > #{renderPersistValue $ toPersistValue pfield}
|]

Writing the code to process the form and update the database might be more tricky and need TH, however there is no Hamlet involved in this step.

duplode
  • 33,731
  • 7
  • 79
  • 150
mb14
  • 22,276
  • 7
  • 60
  • 102
  • Thank you! I think if I can solve at least part of this problem without TH that is most definitely the way to go about it! I was unaware that Persistent provided this sort of type information! As a quick follow up question, where does the parameter `getColumn` come from? I presume I can do something like `(runDB $ selectList [] [])` to obtain I list of all the `entities` from a certain column, but what does `(FieldDef -> Text)` do? Also, could you link me to the module where these Persistent functions are defined? Thank you for all of your help! – Brooks Nov 28 '16 at 07:28
  • `getColumn :: FieldDef -> Text` is just a function which extract the name from the column definition to use it for the column name. I personally use `unDBName . fieldDB` but you can change to change the case for example. – mb14 Nov 28 '16 at 08:57
  • [there](https://hackage.haskell.org/package/persistent-2.6/docs/Database-Persist-Types.html) – mb14 Nov 28 '16 at 08:58
  • Awesome! Thank you for the swift reply! I think that is all I needed! Thanks again! – Brooks Nov 29 '16 at 00:57