Есть ли менее неуклюжий способ использовать blaze-html с монадой Reader?
В учебнике BlazeHtml предлагается использовать монаду Reader для создания реальных шаблонов с BlazeHtml, но без иллюстрации того, как это следует делать. Я пытался следовать этой рекомендации. Результат оставляет меня в замешательстве.
Для иллюстрации предположим, что у меня простой пользовательский тип, и я хочу составить свой HTML-код, используя отдельные функции, одну для макета, а другую для части HTML-страницы, где я отображаю информацию о пользователе. Если я использую Reader Monad, это выглядит так:
data User = User {
username :: Text
, userId :: nt
} deriving (Show)
userBox :: Reader User Html
userBox = do
user <- ask
return $ do
dl $ do
dt $ "Username"
dd $ H.toHtml $ username user
dt $ "UserId"
dd $ H.toHtml $ userId user
page :: Reader User Html
page = do
user <- ask
return $ H.docTypeHtml $ do
H.head $ title "Reader Monad Blaze Example"
H.body $ do
h1 $ "Hello world"
runReader userBox user
Сравните это с моей версией, которая не использует монаду Reader:
userBox :: User -> Html
userBox user = do
dl $ do
dt $ "Username"
dd $ H.toHtml $ username user
dt $ "UserId"
dd $ H.toHtml $ userId user
page :: User -> Html
page user = do
H.docTypeHtml $ do
H.head $ title "Blaze Example, No Reader Monad"
H.body $ do
h1 $ "Hello world"
userBox user
Поэтому я не могу понять, как Reader Monad действительно может сжать шаблонный код в реальных случаях использования. Я что-то пропустил?
2 ответа
Если вы расширите свои типы, вы увидите, что
page :: Reader User Html
:: Reader User Markup
:: Reader User (MarkupM ())
Таким образом, вы можете получить больше рычагов, используя стек трансформеров.
l :: (Html -> Html) -> ReaderT r MarkupM () -> ReaderT r MarkupM ()
l = mapReaderT
r :: Html -> ReaderT r MarkupM ()
r = lift
asksHtml :: ToMarkup a => (r -> a) -> ReaderT r MarkupM ()
asksHtml f = ReaderT (asks (H.toHtml . f))
userBox :: ReaderT User MarkupM ()
userBox = do
l dl $ do
r $ dt "Username"
l dd (asksHtml username)
r $ dt "UserId"
l dd (asksHtml userId)
page :: ReaderT User MarkupM ()
page = do
l H.docTypeHtml $ do
r $ H.head $ title "Reader Monad Blaze Example"
l H.body $ do
r $ h1 "Hello world"
userBox
Блейза Markup
Тип не является преобразователем, поэтому он не может быть использован для преобразования ReaderT
,
type Markup = MarkupM ()
data MarkupM a
Вы можете использовать lucid
пакет ( учебник здесь), вот так:
$ stack build lucid
$ stack exec ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
> :set -XOverloadedStrings -XExtendedDefaultRules
> import Lucid
> import Control.Monad.Trans.Reader
> import Control.Monad.Trans
> runReaderT (renderTextT (do p_ (do title <- lift ask; strong_ (toHtml title)))) "Hi"
"<p><strong>Hi</strong></p>"
>