Re: [Yesod] Subsites and type-safe URLs

115 views
Skip to first unread message

Michael Snoyman

unread,
Jun 27, 2012, 5:21:42 AM6/27/12
to yeso...@googlegroups.com
I think you're looking for getRouteToMaster[1]:

getSubRootR = do
toMaster <- getRouteToMaster
defaultLayout [whamlet|<a href=@{toMaster SubRootR}>Link to self|]

Michael

[1] http://hackage.haskell.org/packages/archive/yesod-core/1.0.1.2/doc/html/Yesod-Handler.html#v:getRouteToMaster

On Wed, Jun 27, 2012 at 4:55 AM, Jason Whittle <jasonw...@gmail.com> wrote:
> I’m having a little trouble rendering type-safe URLs from within a Yesod
> subsite. If I copy the Hello World example verbatim
> from http://www.yesodweb.com/book/creating-a-subsite it compiles and works.
>
> However, as soon as I replace line 16 (getSubRootR = …) with
>
>     getSubRootR = defaultLayout [whamlet|<a href=@{SubRootR}>Link to self|]
>
> I get the following error:
>
> simple.hs:16:38:
>     Could not deduce (master ~ HelloSub)
>     from the context (Yesod master)
>       bound by the type signature for
>                  getSubRootR :: Yesod master => GHandler HelloSub master
> RepHtml
>       at simple.hs:16:1-71
>       `master' is a rigid type variable bound by
>                the type signature for
>                  getSubRootR :: Yesod master => GHandler HelloSub master
> RepHtml
>                at simple.hs:16:1
>     Expected type: Route master
>       Actual type: Route HelloSub
>     In the first argument of `\ u_a3g6
>                                 -> urender_a3g5 u_a3g6 []', namely
>       `SubRootR'
>     In the first argument of `toHtml', namely
>       `\ u_a3g6 -> urender_a3g5 u_a3g6 [] SubRootR'
>
>
> I assume that the link is the problem, but I can’t just make it @{SubsiteR
> SubRootR} like in the definition of getRootR, as SubsiteR isn’t in scope.
>
> The book says it will explain the second parameter of mkYesodSub later, but
> I can’t find where that explanation is and the type of that param is
> Language.Haskell.TH.Syntax.Ctx, so without knowing Template Haskell it’s
> difficult for me to judge if that’s the problem.
>
> I’m new to both Haskell and Yesod. Is there something obvious I’m missing?
> I’ve read http://www.yesodweb.com/book/routing-and-handlers and the
> Type-Safe URLs section
> of http://www.yesodweb.com/book/shakespearean-templates several times, but
> I’m having trouble finding follow-on information. Normally, I’d dive into
> the source, but without a background in TH it’s slow going.
>
> Cheers,
> Jason Whittle

Jason Whittle

unread,
Jun 27, 2012, 12:04:43 PM6/27/12
to yeso...@googlegroups.com
On Wednesday, June 27, 2012 5:21:42 AM UTC-4, Michael Snoyman wrote:
I think you're looking for getRouteToMaster[1]:

getSubRootR = do
    toMaster <- getRouteToMaster
    defaultLayout [whamlet|<a href=@{toMaster SubRootR}>Link to self|]

Thank you, Michael—I appreciate the help. I also added a wiki page for this topic at https://github.com/yesodweb/yesod/wiki/Intra-subsite-links for others. 

I thought that once I had intra-subsite links that forms would be easier to get, but that doesn’t seem to be the case. When I take the same code and add a POST to the SubRootR resource with the following function: 

    postSubRootR :: Yesod master => GHandler HelloSub master RepHtml
    postSubRootR = do
      name <- runInputPost $ ireq textField "content"
      defaultLayout [whamlet|<p>Hello, #{name}|]

I get the error: 

simple.hs:21:26:
    Could not deduce (RenderMessage master FormMessage)
      arising from a use of `ireq'
    from the context (Yesod master)
      bound by the type signature for
                 postSubRootR :: Yesod master => GHandler HelloSub master RepHtml
      at simple.hs:(20,1)-(22,44)
    Possible fix:
      add (RenderMessage master FormMessage) to the context of
        the type signature for
          postSubRootR :: Yesod master => GHandler HelloSub master RepHtml
      or add an instance declaration for
         (RenderMessage master FormMessage)
    In the second argument of `($)', namely `ireq textField "content"'
    In a stmt of a 'do' block:
      name <- runInputPost $ ireq textField "content"
    In the expression:
      do { name <- runInputPost $ ireq textField "content";
           defaultLayout
             (do { toWidget
                     ((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
                        "<p>Hello, ");
                   toWidget (toHtml name);
                   .... }) }

When I naïvely change the function’s type to: 

    postSubRootR :: (RenderMessage master FormMessage, Yesod master) => GHandler HelloSub master RepHtml

I get the error: 

simple.hs:11:1:
    Could not deduce (RenderMessage master FormMessage)
      arising from a use of `postSubRootR'
    from the context (Yesod master)
      bound by the instance declaration at simple.hs:(11,1)-(13,2)
    or from (Yesod master)
      bound by the type signature for
                 yesodDispatch :: Yesod master =>
                                  master
                                  -> HelloSub
                                  -> (Route HelloSub -> Route master)
                                  -> (Maybe (SessionBackend master) -> Application)
                                  -> (Route HelloSub
                                      -> Maybe (SessionBackend master) -> Application)
                                  -> Data.Text.Internal.Text
                                  -> [Data.Text.Internal.Text]
                                  -> Maybe (SessionBackend master)
                                  -> Application
      at simple.hs:(11,1)-(13,2)
    Possible fix:
      add (RenderMessage master FormMessage) to the context of
        the type signature for
          yesodDispatch :: Yesod master =>
                           master
                           -> HelloSub
                           -> (Route HelloSub -> Route master)
                           -> (Maybe (SessionBackend master) -> Application)
                           -> (Route HelloSub -> Maybe (SessionBackend master) -> Application)
                           -> Data.Text.Internal.Text
                           -> [Data.Text.Internal.Text]
                           -> Maybe (SessionBackend master)
                           -> Application
        or the instance declaration
      or add an instance declaration for
         (RenderMessage master FormMessage)
    In the second argument of `fmap', namely `postSubRootR'
    In the expression: fmap chooseRep postSubRootR
    In the expression: \ -> fmap chooseRep postSubRootR

I looked for a function similar to getRouteToMaster for forms, but without success. Is there a way to transform a subsite form into a master form? If the error said it couldn’t deduce (RenderMessage HelloSub FormMessage), then I could add an instance for that, but it seems like it’s supposed to be the master site that determines the implementation of renderMessage. 

Thanks again. 

Cheers, 
Jason Whittle

Michael Snoyman

unread,
Jun 28, 2012, 8:56:53 AM6/28/12
to yeso...@googlegroups.com
What you're looking at is probably the most complicated user-facing
aspect of Yesod. What you need to do is specify- via some Template
Haskell code- which constraints to place on the master site in order
to use your subsite. You can see an example of this in yesod-auth[1]

mkYesodSub "Auth"
[ ClassP ''YesodAuth [VarT $ mkName "master"]
]

You would need something similar for your subsite, likely:

mkYesodSub "HelloSub"
[ ClassP ''RenderMessage [VarT $ mkName "master", ConT ''FormMessage]
]

HTH,
Michael

[1] http://hackage.haskell.org/packages/archive/yesod-auth/1.0.2.1/doc/html/src/Yesod-Auth.html

Jason Whittle

unread,
Jul 2, 2012, 4:55:42 PM7/2/12
to yeso...@googlegroups.com
On Thursday, June 28, 2012 8:56:53 AM UTC-4, Michael Snoyman wrote:
What you're looking at is probably the most complicated user-facing
aspect of Yesod. What you need to do is specify- via some Template
Haskell code- which constraints to place on the master site in order
to use your subsite. You can see an example of this in yesod-auth[1]

mkYesodSub "Auth"
    [ ClassP ''YesodAuth [VarT $ mkName "master"]
    ]

You would need something similar for your subsite, likely:

mkYesodSub "HelloSub"
    [ ClassP ''RenderMessage [VarT $ mkName "master", ConT ''FormMessage]
    ]

Thank you for the pointer, Michael. 

For anyone following along who, like me, has no experience with Template Haskell, it didn’t take me very long to understand enough to grasp the examples that Michael gives here. The Template Haskell page on the Haskell wiki gives several tutorials including the following three which I list in the sequence I found them useful in: 
Jason Whittle
Reply all
Reply to author
Forward
0 new messages