haskellmonadsyesod

Problem to show value out of Handler monad in Haskell and Yesod


I am a beginner in Haskell and I am still studying and I stopped at the problem that has to do with monads.
The problem is that I want to show the comment writer in the template.
I can't do that because when I add a comment from the database it is always in the Handler monad.
Maybe my whole idea is wrong I don't know.

This is the comment entity:

ManComment
text Text sqltype=varchar(500)
created UTCTime sqltype=DateTime
writer UserId Maybe sqltype=varchar(255) default=NULL  -- comment writer
manId ManifestationId sqltype=varchar(255) default=NULL
deriving Show Typeable

This is the handler function:

getManDetailsR :: ManifestationId -> Handler Html
getManDetailsR mid = do 
  (ui, user) <- requireAuthPair
  comments <- runDB $ getComFromMan mid
  defaultLayout $ do
     setTitle "Manifestation details"
     $(widgetFile "man-details")

Part of hamlet file (in last line trying to show writer):

$if null comments 
  <h4>There is not comments! 
$else 
  $forall Entity cid com <- comments 
    <form method=post action=@{DeleteManCommentR mid cid}> 
      <li .list-group-item> 
        <div class="row"> 
         <div class="col-xs-10 col-md-11"> 
         <div> 
          <div .mic-info> By: <a href=@{ProfileR}>#{getCommentWriter $ com}</a> 

I'm trying to get a comment writer here:

getCommentWriter :: ManComment -> Handler Text
getCommentWriter c = do 
  user <- runDB $ get404 $ fromJust $ manCommentWriter c  --get writer from database
  let username = userIdent user 
  return username  -- but return puts in Handler monad

Eventually an error occurred:

No instance for (blaze-markup-0.8.2.7:Text.Blaze.ToMarkup
                         (Handler Text))
        arising from a use of ‘toHtml’ 

Solution

  • you should be able to use it in the same way you use comments there: bind it to a local value and access this in your hamlet-file/code.

    Of course you want to pair this up with the comment itself so I'd propose something like this:

    
    getCommentAndWriter :: ManComment -> Handler (Entity ManComment, Text)
    getCommentAndWriter c = do 
      user <- runDB $ get404 $ fromJust $ manCommentWriter c  --get writer from database
      let username = userIdent user 
      return (c, username)
    
    getManDetailsR :: ManifestationId -> Handler Html
    getManDetailsR mid = do 
      (ui, user) <- requireAuthPair
    
      cs <- runDB $ getComFromMan mid
      comments <- forM cs getCommentAndWriter
      
      defaultLayout $ do
         setTitle "Manifestation details"
         $(widgetFile "man-details")
    
    $if null comments 
      <h4>There is not comments! 
    $else 
      $forall (Entity cid com, writer) <- comments 
        <form method=post action=@{DeleteManCommentR mid cid}> 
          <li .list-group-item> 
            <div class="row"> 
             <div class="col-xs-10 col-md-11"> 
             <div> 
              <div .mic-info> By: <a href=@{ProfileR}>#{writer}</a>
    

    Of course you should look into querying both of this together if you can but basically this should work.


    PS: if you want to use more fields you probably would want to use a record - here I choose to tuple it up for simplicity.