Haskell.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview
newer
Fwd: Sql join feature in...

[Yesod] widgets in default layout

older
Sql join feature in persistent

Dmitry Kurochkin

13 Feb 2011 13 Feb '11
5:51 a.m.

Hello. I am trying to make a menu widget for a site. It would render list of menu items and mark the active one. I started with a new Menu module that exports (mainMenu :: Widget ()) function. The function gets the current route, iterates over a list of (item title, item route) list and constructs the menu. Now I can import Menu in a handler module and use ^{mainMenu} in hamlet template. Next I tried to put the menu widget to default layout - menu should be on every page so default layout is where it belongs. Rest of the email describes problems I got. 1. Module import loop. I need to import the widget module in the module which defines defaultLayout, i.e. the main application module. But the widget module uses the main application module to have routes and probably other staff, hence import loop. I tried to separate foundation and route declaration from Yesod instance declaration, so that the widget module could import just routes declaration and Yesod instance could import the widget module. Turns out that does not work: Attempting to interpret your app... Compile failed: Menu.hs:20:23: Couldn't match expected type `Route m' against inferred type `TestAppRoute' NB: `Route' is a type function, and may not be injective In the first argument of `\ u[a7Kv] -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] []', namely `AboutR' In a stmt of a 'do' expression: \ u[a7Kv] -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] [] AboutR In the first argument of `hamlet-0.7.0.2:Text.Hamlet.Quasi.toHamletValue', namely `do { (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "

</div>
hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] [] AboutR; (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "\">itemTitle item</a></div></div></div>" }' I do not understand details, but it is clear that the widget needs the Yesod instance. So I have to put the widget in the main application module. This does not look good considering that application may have many widgets. I have also stumbled upon a bug in devel server: It tries to recompile the source in a loop, without changing source of course. Annoying but not a critical issue. 2. Widget does not work in default layout. I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu. I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it. The last issue is that (mainMenu :: Widget ()) does not work, I had to change it to (GWidget sub TestApp ()). Again I do not know details, but this was unexpected to me. Perhaps the Widget type synonim should be changed? I would appreciate advices on how to solve the above problems. Perhaps I am just missing something and there is a proper way to do what I want. For now I will avoid using widgets in default layout and move part of layout to individual handler templates, primarily because I find it too ugly to define widgets in the main application module. Regards, Dmitry

0 0
Reply
Sign in to reply online Use email software

Show replies by date

Michael Snoyman

13 Feb 13 Feb
8:35 a.m.

On Sun, Feb 13, 2011 at 12:51 PM, Dmitry Kurochkin wrote:

...

Hello.

I am trying to make a menu widget for a site. It would render list of menu items and mark the active one. I started with a new Menu module that exports (mainMenu :: Widget ()) function. The function gets the current route, iterates over a list of (item title, item route) list and constructs the menu. Now I can import Menu in a handler module and use ^{mainMenu} in hamlet template.

Next I tried to put the menu widget to default layout - menu should be on every page so default layout is where it belongs. Rest of the email describes problems I got.

For the record, I think this is a very good approach. Let's address specific issues below.

...

1. Module import loop.

I need to import the widget module in the module which defines defaultLayout, i.e. the main application module. But the widget module uses the main application module to have routes and probably other staff, hence import loop. I tried to separate foundation and route declaration from Yesod instance declaration, so that the widget module could import just routes declaration and Yesod instance could import the widget module. Turns out that does not work:

The simplest solution is to just define the mainMenu widget in the same file as the Yesod instance. Is there a reason to avoid this? It's definitely possible to split up the Yesod instance and the call to mkYesodData, but doing so requires an orphan instance. It's not a particularly *dangerous* orphan instance, but even so I think avoiding orphans is a good goal.

...

Attempting to interpret your app... Compile failed:

Menu.hs:20:23:    Couldn't match expected type `Route m'           against inferred type `TestAppRoute'      NB: `Route' is a type function, and may not be injective    In the first argument of `\ u[a7Kv]                                  -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad                                       u[a7Kv] []', namely        `AboutR'    In a stmt of a 'do' expression:        \ u[a7Kv]            -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] []          AboutR    In the first argument of `hamlet-0.7.0.2:Text.Hamlet.Quasi.toHamletValue', namely        `do { (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad             . preEscapedString)                "

</div>
hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] []                AboutR;              (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad             . preEscapedString)                "\">itemTitle item</a></div></div></div>" }'

I do not understand details, but it is clear that the widget needs the Yesod instance. So I have to put the widget in the main application module. This does not look good considering that application may have many widgets.

Without seeing your code, I can't be certain what's going on. However, that doesn't look like a problem about missing a Yesod instance. It actually looks like the kind of problem that could be solved with more explicit type signatures.

...

I have also stumbled upon a bug in devel server: It tries to recompile the source in a loop, without changing source of course. Annoying but not a critical issue.

It's not a bug, it's the intended behavior. Let's say that you write module A that does not depend on any modules and you start up devel-server. Everything compiles and runs fine, and devel-server begins monitoring A.hs for file changes. Meanwhile, you write module B (which contains a bug), and then add "import B" to the import list for A. Now, devel-server is going to try and recompile module A, but will fail since B is invalid. At this point, devel-server will not know that A depends on B. If devel-server simply waits for there to be a change to its monitored files, it will never notice that you've corrected the bug in module B: it will simply idle until someone makes a change to module A. That's why it continuously retries to compile the whole thing once there's an error.

...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need. [1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

...

The last issue is that (mainMenu :: Widget ()) does not work, I had to change it to (GWidget sub TestApp ()). Again I do not know details, but this was unexpected to me. Perhaps the Widget type synonim should be changed?

OK, now I might have a better understanding of the error message you were referencing above. Take a look at the type signatures for the functions you are calling: defaultLayout is a function that can be called from either a master site handler or a subsite handler. For example, if I wrote a blog subsite, that subsite should be able to use the same styles as the master site. Now: type Widget = GWidget TestApp TestApp in your application, which means that it only works for a situation for where the subsite is the same as the master site. This is the case with most of your handler functions, which is why the scaffolded site provides this convenience synonym. However, when you want to write a function which is generic enough to work for arbitrary subsites, you can't use this convenience synonym.

...

I would appreciate advices on how to solve the above problems. Perhaps I am just missing something and there is a proper way to do what I want. For now I will avoid using widgets in default layout and move part of layout to individual handler templates, primarily because I find it too ugly to define widgets in the main application module.

I suppose that's a matter of personal preference, but to me it makes perfect sense to declare a mainMenu function in the same module that defines defaultLayout. You can find plenty of ways around this. Introducing orphans instances is one. A particularly ugly approach could be to include the mainMenu widget as part of your foundation datatype and have defaultLayout refer to that, though I in no way recommend such a course of action. I'm just saying it's available for masochists ;). Michael

0 0
Reply
Sign in to reply online Use email software

Dmitry Kurochkin

9:23 a.m.

On Sun, 13 Feb 2011 15:35:48 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 12:51 PM, Dmitry Kurochkin wrote:

...

Hello.

I am trying to make a menu widget for a site. It would render list of menu items and mark the active one. I started with a new Menu module that exports (mainMenu :: Widget ()) function. The function gets the current route, iterates over a list of (item title, item route) list and constructs the menu. Now I can import Menu in a handler module and use ^{mainMenu} in hamlet template.

Next I tried to put the menu widget to default layout - menu should be on every page so default layout is where it belongs. Rest of the email describes problems I got.

For the record, I think this is a very good approach. Let's address specific issues below.

Glad to hear :)

...
...

1. Module import loop.

I need to import the widget module in the module which defines defaultLayout, i.e. the main application module. But the widget module uses the main application module to have routes and probably other staff, hence import loop. I tried to separate foundation and route declaration from Yesod instance declaration, so that the widget module could import just routes declaration and Yesod instance could import the widget module. Turns out that does not work:

The simplest solution is to just define the mainMenu widget in the same file as the Yesod instance. Is there a reason to avoid this?

Application may have many widgets. They can be pretty complex and big. Each widget is not related to another. I like putting separate things to separate modules, just like handlers for different resources.

...

It's definitely possible to split up the Yesod instance and the call to mkYesodData, but doing so requires an orphan instance. It's not a particularly *dangerous* orphan instance, but even so I think avoiding orphans is a good goal.

I guess my Haskell and/or Yesod knowlege is not enough to solve this.

...
...

Attempting to interpret your app... Compile failed:

Menu.hs:20:23:    Couldn't match expected type `Route m'           against inferred type `TestAppRoute'      NB: `Route' is a type function, and may not be injective    In the first argument of `\ u[a7Kv]                                  -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad                                       u[a7Kv] []', namely        `AboutR'    In a stmt of a 'do' expression:        \ u[a7Kv]            -> hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] []          AboutR    In the first argument of `hamlet-0.7.0.2:Text.Hamlet.Quasi.toHamletValue', namely        `do { (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad             . preEscapedString)                "

</div>
hamlet-0.7.0.2:Text.Hamlet.Quasi.urlToHamletMonad u[a7Kv] []                AboutR;              (hamlet-0.7.0.2:Text.Hamlet.Quasi.htmlToHamletMonad             . preEscapedString)                "\">itemTitle item</a></div></div></div>" }'

I do not understand details, but it is clear that the widget needs the Yesod instance. So I have to put the widget in the main application module. This does not look good considering that application may have many widgets.

Without seeing your code, I can't be certain what's going on. However, that doesn't look like a problem about missing a Yesod instance. It actually looks like the kind of problem that could be solved with more explicit type signatures.

I will send you code later.

...
...

I have also stumbled upon a bug in devel server: It tries to recompile the source in a loop, without changing source of course. Annoying but not a critical issue.

It's not a bug, it's the intended behavior. Let's say that you write module A that does not depend on any modules and you start up devel-server. Everything compiles and runs fine, and devel-server begins monitoring A.hs for file changes. Meanwhile, you write module B (which contains a bug), and then add "import B" to the import list for A.

Now, devel-server is going to try and recompile module A, but will fail since B is invalid. At this point, devel-server will not know that A depends on B. If devel-server simply waits for there to be a change to its monitored files, it will never notice that you've corrected the bug in module B: it will simply idle until someone makes a change to module A. That's why it continuously retries to compile the whole thing once there's an error.

I see. Thanks for the explanation.

...
...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it? Besides, even if there is such function, it would not allow for convenient usage of widgets in default layout IMO. defaultLayout code would look like: pc <- widgetToPageContent ... -- for handler widget pc1 <- widgetToPageContent ... -- for menu widget ... -- code to merge parts of pc1 with pc pc2 <- widgetToPageContent ... -- for another widget ... -- code to merge parts of pc2 with pc -- and so on for every widget And in default layout you use ^{pageBody pc1}, ^{pageBody pc2}, etc. The workaround described above is more convenient: Just use ^{menuWidget} and merging of CSS and javascript is done implicitly. But this comes at a cost of layout splitting. I wonder if it can be made transparent for users. Sorry if I did not understand what you propose. Would appreciate an example.

...

[1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

...

The last issue is that (mainMenu :: Widget ()) does not work, I had to change it to (GWidget sub TestApp ()). Again I do not know details, but this was unexpected to me. Perhaps the Widget type synonim should be changed?

OK, now I might have a better understanding of the error message you were referencing above. Take a look at the type signatures for the functions you are calling: defaultLayout is a function that can be called from either a master site handler or a subsite handler. For example, if I wrote a blog subsite, that subsite should be able to use the same styles as the master site. Now:

type Widget = GWidget TestApp TestApp

in your application, which means that it only works for a situation for where the subsite is the same as the master site. This is the case with most of your handler functions, which is why the scaffolded site provides this convenience synonym. However, when you want to write a function which is generic enough to work for arbitrary subsites, you can't use this convenience synonym.

I confess I did not look at subsites yet (AFAIK that part of the book is not uptodate yet?). But I see your point: Since defaultLayout works with subsites, widgets should be general. Makes sense.

...
...

I would appreciate advices on how to solve the above problems. Perhaps I am just missing something and there is a proper way to do what I want. For now I will avoid using widgets in default layout and move part of layout to individual handler templates, primarily because I find it too ugly to define widgets in the main application module.

I suppose that's a matter of personal preference, but to me it makes perfect sense to declare a mainMenu function in the same module that defines defaultLayout. You can find plenty of ways around this. Introducing orphans instances is one. A particularly ugly approach could be to include the mainMenu widget as part of your foundation datatype and have defaultLayout refer to that, though I in no way recommend such a course of action. I'm just saying it's available for masochists ;).

See above why I think it is better to put widgets to distinct modules. As for workarounds, I guess I am just not such a great haskeller :) I think there should be a blessed way to put widgets in a separate modules if you want to. Similar to handlers: There is Controller and you can put everything there, but there are Handler.* modules as well for those who likes it separate. This way poor haskellers like me will not have invent workarounds :) Regards, Dmitry

...

Michael

0 0
Reply
Sign in to reply online Use email software

Michael Snoyman

9:54 a.m.

On Sun, Feb 13, 2011 at 4:23 PM, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 15:35:48 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 12:51 PM, Dmitry Kurochkin wrote:

...

Hello.

I am trying to make a menu widget for a site. It would render list of menu items and mark the active one. I started with a new Menu module that exports (mainMenu :: Widget ()) function. The function gets the current route, iterates over a list of (item title, item route) list and constructs the menu. Now I can import Menu in a handler module and use ^{mainMenu} in hamlet template.

Next I tried to put the menu widget to default layout - menu should be on every page so default layout is where it belongs. Rest of the email describes problems I got.

For the record, I think this is a very good approach. Let's address specific issues below.

Glad to hear :)

...
...

1. Module import loop.

I need to import the widget module in the module which defines defaultLayout, i.e. the main application module. But the widget module uses the main application module to have routes and probably other staff, hence import loop. I tried to separate foundation and route declaration from Yesod instance declaration, so that the widget module could import just routes declaration and Yesod instance could import the widget module. Turns out that does not work:

The simplest solution is to just define the mainMenu widget in the same file as the Yesod instance. Is there a reason to avoid this?

Application may have many widgets. They can be pretty complex and big. Each widget is not related to another. I like putting separate things to separate modules, just like handlers for different resources.

I'm referring specifically to widgets called by defaultLayout: if the widget is not needed by defaultLayout, there's no cyclical dependency introduced. [snip]

...
...
...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it?

Just combine the two widgets and call widgetToPageContent once: defaultLayout widget = do pc <- widgetToPageContents $ do menuWidget widget hamletToRepHtml ... You can see an example of this in the Yesod docs site[1]. [1] https://github.com/snoyberg/yesoddocs/blob/master/site/YesodDocs.hs#L89

...

Besides, even if there is such function, it would not allow for convenient usage of widgets in default layout IMO. defaultLayout code would look like:

   pc <- widgetToPageContent ... -- for handler widget    pc1 <- widgetToPageContent ... -- for menu widget    ... -- code to merge parts of pc1 with pc    pc2 <- widgetToPageContent ... -- for another widget    ... -- code to merge parts of pc2 with pc    -- and so on for every widget

And in default layout you use ^{pageBody pc1}, ^{pageBody pc2}, etc.

The workaround described above is more convenient: Just use ^{menuWidget} and merging of CSS and javascript is done implicitly. But this comes at a cost of layout splitting. I wonder if it can be made transparent for users.

Sorry if I did not understand what you propose. Would appreciate an example.

I think I didn't understand you the first time around, my apologies. I think the example from YesodDocs should clear things up.

...
...

[1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

...

The last issue is that (mainMenu :: Widget ()) does not work, I had to change it to (GWidget sub TestApp ()). Again I do not know details, but this was unexpected to me. Perhaps the Widget type synonim should be changed?

OK, now I might have a better understanding of the error message you were referencing above. Take a look at the type signatures for the functions you are calling: defaultLayout is a function that can be called from either a master site handler or a subsite handler. For example, if I wrote a blog subsite, that subsite should be able to use the same styles as the master site. Now:

    type Widget = GWidget TestApp TestApp

in your application, which means that it only works for a situation for where the subsite is the same as the master site. This is the case with most of your handler functions, which is why the scaffolded site provides this convenience synonym. However, when you want to write a function which is generic enough to work for arbitrary subsites, you can't use this convenience synonym.

I confess I did not look at subsites yet (AFAIK that part of the book is not uptodate yet?). But I see your point: Since defaultLayout works with subsites, widgets should be general. Makes sense.

Also something on my (ever growing) TODO list is to describe subsites in more detail.

...
...
...

I would appreciate advices on how to solve the above problems. Perhaps I am just missing something and there is a proper way to do what I want. For now I will avoid using widgets in default layout and move part of layout to individual handler templates, primarily because I find it too ugly to define widgets in the main application module.

I suppose that's a matter of personal preference, but to me it makes perfect sense to declare a mainMenu function in the same module that defines defaultLayout. You can find plenty of ways around this. Introducing orphans instances is one. A particularly ugly approach could be to include the mainMenu widget as part of your foundation datatype and have defaultLayout refer to that, though I in no way recommend such a course of action. I'm just saying it's available for masochists ;).

See above why I think it is better to put widgets to distinct modules. As for workarounds, I guess I am just not such a great haskeller :)

I think there should be a blessed way to put widgets in a separate modules if you want to. Similar to handlers: There is Controller and you can put everything there, but there are Handler.* modules as well for those who likes it separate. This way poor haskellers like me will not have invent workarounds :)

Just to confirm: are you talking for general widgets, or just widgets to be called from defaultLayout? As I mention above, the former can easily be put in separate modules, the latter would require more work. Michael

0 0
Reply
Sign in to reply online Use email software

Dmitry Kurochkin

10:28 a.m.

On Sun, 13 Feb 2011 16:54:00 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 4:23 PM, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 15:35:48 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 12:51 PM, Dmitry Kurochkin wrote:

...

Hello.

I am trying to make a menu widget for a site. It would render list of menu items and mark the active one. I started with a new Menu module that exports (mainMenu :: Widget ()) function. The function gets the current route, iterates over a list of (item title, item route) list and constructs the menu. Now I can import Menu in a handler module and use ^{mainMenu} in hamlet template.

Next I tried to put the menu widget to default layout - menu should be on every page so default layout is where it belongs. Rest of the email describes problems I got.

For the record, I think this is a very good approach. Let's address specific issues below.

Glad to hear :)

...
...

1. Module import loop.

I need to import the widget module in the module which defines defaultLayout, i.e. the main application module. But the widget module uses the main application module to have routes and probably other staff, hence import loop. I tried to separate foundation and route declaration from Yesod instance declaration, so that the widget module could import just routes declaration and Yesod instance could import the widget module. Turns out that does not work:

The simplest solution is to just define the mainMenu widget in the same file as the Yesod instance. Is there a reason to avoid this?

Application may have many widgets. They can be pretty complex and big. Each widget is not related to another. I like putting separate things to separate modules, just like handlers for different resources.

I'm referring specifically to widgets called by defaultLayout: if the widget is not needed by defaultLayout, there's no cyclical dependency introduced.

Still the argument remains. I want to have all menu-related code in a separate module.

...

[snip]

...
...
...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it?

Just combine the two widgets and call widgetToPageContent once:

defaultLayout widget = do pc <- widgetToPageContents $ do menuWidget widget hamletToRepHtml ...

You can see an example of this in the Yesod docs site[1].

I thought this would result in menuWidget placed directly before the main widget body, right? In many cases simple concatenation is not enough.

...

[1] https://github.com/snoyberg/yesoddocs/blob/master/site/YesodDocs.hs#L89

...

Besides, even if there is such function, it would not allow for convenient usage of widgets in default layout IMO. defaultLayout code would look like:

   pc <- widgetToPageContent ... -- for handler widget    pc1 <- widgetToPageContent ... -- for menu widget    ... -- code to merge parts of pc1 with pc    pc2 <- widgetToPageContent ... -- for another widget    ... -- code to merge parts of pc2 with pc    -- and so on for every widget

And in default layout you use ^{pageBody pc1}, ^{pageBody pc2}, etc.

The workaround described above is more convenient: Just use ^{menuWidget} and merging of CSS and javascript is done implicitly. But this comes at a cost of layout splitting. I wonder if it can be made transparent for users.

Sorry if I did not understand what you propose. Would appreciate an example.

I think I didn't understand you the first time around, my apologies. I think the example from YesodDocs should clear things up.

...
...

[1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

...

The last issue is that (mainMenu :: Widget ()) does not work, I had to change it to (GWidget sub TestApp ()). Again I do not know details, but this was unexpected to me. Perhaps the Widget type synonim should be changed?

OK, now I might have a better understanding of the error message you were referencing above. Take a look at the type signatures for the functions you are calling: defaultLayout is a function that can be called from either a master site handler or a subsite handler. For example, if I wrote a blog subsite, that subsite should be able to use the same styles as the master site. Now:

    type Widget = GWidget TestApp TestApp

in your application, which means that it only works for a situation for where the subsite is the same as the master site. This is the case with most of your handler functions, which is why the scaffolded site provides this convenience synonym. However, when you want to write a function which is generic enough to work for arbitrary subsites, you can't use this convenience synonym.

I confess I did not look at subsites yet (AFAIK that part of the book is not uptodate yet?). But I see your point: Since defaultLayout works with subsites, widgets should be general. Makes sense.

Also something on my (ever growing) TODO list is to describe subsites in more detail.

...
...
...

I would appreciate advices on how to solve the above problems. Perhaps I am just missing something and there is a proper way to do what I want. For now I will avoid using widgets in default layout and move part of layout to individual handler templates, primarily because I find it too ugly to define widgets in the main application module.

I suppose that's a matter of personal preference, but to me it makes perfect sense to declare a mainMenu function in the same module that defines defaultLayout. You can find plenty of ways around this. Introducing orphans instances is one. A particularly ugly approach could be to include the mainMenu widget as part of your foundation datatype and have defaultLayout refer to that, though I in no way recommend such a course of action. I'm just saying it's available for masochists ;).

See above why I think it is better to put widgets to distinct modules. As for workarounds, I guess I am just not such a great haskeller :)

I think there should be a blessed way to put widgets in a separate modules if you want to. Similar to handlers: There is Controller and you can put everything there, but there are Handler.* modules as well for those who likes it separate. This way poor haskellers like me will not have invent workarounds :)

Just to confirm: are you talking for general widgets, or just widgets to be called from defaultLayout? As I mention above, the former can easily be put in separate modules, the latter would require more work.

I am talking about defaultLayout widgets. In general, reasons for putting widgets into separate modules does not depend on whether they are used in defaultLayout. Though, I agree that the fact that only defaultLayout widgets must be put into the Yesod instance module somewhat improves the situation. IMO from user point of view it does not matter much if widget is used in handler or in defaultLayout. In most cases, at least. I just create a menu widget and want be able to use it anywhere (even both in handlers and defaultLayout). E.g. in my case the only difference between handler and defaulLayout widget is the type signature, implementation does not change. Regards, Dmitry

...

Michael

0 0
Reply
Sign in to reply online Use email software

Michael Snoyman

10:44 a.m.

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

...
...

[snip]

...
...
...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it?

Just combine the two widgets and call widgetToPageContent once:

    defaultLayout widget = do         pc <- widgetToPageContents $ do             menuWidget             widget         hamletToRepHtml ...

You can see an example of this in the Yesod docs site[1].

I thought this would result in menuWidget placed directly before the main widget body, right? In many cases simple concatenation is not enough.

Then just modify menuWidget to take a Widget as an argument: menuWidget :: GWidget s m () -> GWidget s m () menuWidget w = do earlierStuff w laterStuff This is the very reason why we have polymorphic hamlet, so you can even do: menuWidget w = [$hamlet| <p>Header ^{w} <p>Footer |] In fact, if you put that into a separate menu-widget.hamlet file, you might get the results you were looking for originally all the back-bending. [snip]

...
...

Just to confirm: are you talking for general widgets, or just widgets to be called from defaultLayout? As I mention above, the former can easily be put in separate modules, the latter would require more work.

I am talking about defaultLayout widgets. In general, reasons for putting widgets into separate modules does not depend on whether they are used in defaultLayout. Though, I agree that the fact that only defaultLayout widgets must be put into the Yesod instance module somewhat improves the situation.

IMO from user point of view it does not matter much if widget is used in handler or in defaultLayout. In most cases, at least. I just create a menu widget and want be able to use it anywhere (even both in handlers and defaultLayout). E.g. in my case the only difference between handler and defaulLayout widget is the type signature, implementation does not change.

Nonetheless, these *are* the rules that exist in Haskell. To a certain extent, separating out mkYesodDispatch into Controller.hs is a similar hack to this. In that case, I think that the added benefit of separate handler modules definitely justifies using this technique. I'm simply not (yet) convinced that the benefit here is big enough to warrant a similar approach. Michael

0 0
Reply
Sign in to reply online Use email software

Dmitry Kurochkin

11:13 a.m.

On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

I will try to send you some an example later. Unfortunately, I do not have time for this right now. Playing with Yesod in my free time only.

...
...
...

[snip]

...
...
...

2. Widget does not work in default layout.

I guess this is a known and expected behavior. My feeling is that hamletToRepHtml can not embed widgets because it may be too late to add cassius and julius. As a workaround I split default layout into outer and inner layout. Outer layout renders just HTML <head> and <body>. While outer layout is rendered as a widget that embeds the actual page contents. Since outer layout is rendered as a widget, it may embed other widgets like menu.

I imagine that hamletToRepHtml could render all embedded widgets before the main body. Though, it may be difficult to implement, have performance or other issues. Anyway, I think it is not uncommon to include a widget in default layout. So Yesod should provide an easy way to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it?

Just combine the two widgets and call widgetToPageContent once:

    defaultLayout widget = do         pc <- widgetToPageContents $ do             menuWidget             widget         hamletToRepHtml ...

You can see an example of this in the Yesod docs site[1].

I thought this would result in menuWidget placed directly before the main widget body, right? In many cases simple concatenation is not enough.

Then just modify menuWidget to take a Widget as an argument:

menuWidget :: GWidget s m () -> GWidget s m () menuWidget w = do earlierStuff w laterStuff

This is the very reason why we have polymorphic hamlet, so you can even do:

menuWidget w = [$hamlet| <p>Header ^{w} <p>Footer |]

In fact, if you put that into a separate menu-widget.hamlet file, you might get the results you were looking for originally all the back-bending.

This does not feel right in my case. Essentially, it moves part of layout to widget. If I have many widgets to combine, e.g. mainMenu, sideMenu, anotherCoolWidget, it becomes more complex. I think introducing a separate layout (my original workaround) is a better solution here. I just need to come up with a better name for it :) defaultLayout content = do mmsg <- getMessage pc <- widgetToPageContent $ do addCassius $(Settings.cassiusFile "default-layout") addWidget $(Settings.cassiusFile "inner-layout") hamletToRepHtml $(Settings.hamletFile "outer-layout")

...

[snip]

...
...

Just to confirm: are you talking for general widgets, or just widgets to be called from defaultLayout? As I mention above, the former can easily be put in separate modules, the latter would require more work.

I am talking about defaultLayout widgets. In general, reasons for putting widgets into separate modules does not depend on whether they are used in defaultLayout. Though, I agree that the fact that only defaultLayout widgets must be put into the Yesod instance module somewhat improves the situation.

IMO from user point of view it does not matter much if widget is used in handler or in defaultLayout. In most cases, at least. I just create a menu widget and want be able to use it anywhere (even both in handlers and defaultLayout). E.g. in my case the only difference between handler and defaulLayout widget is the type signature, implementation does not change.

Nonetheless, these *are* the rules that exist in Haskell. To a certain extent, separating out mkYesodDispatch into Controller.hs is a similar hack to this. In that case, I think that the added benefit of separate handler modules definitely justifies using this technique. I'm simply not (yet) convinced that the benefit here is big enough to warrant a similar approach.

Well, you have my vote for what it's worth :) Regards, Dmitry

...

Michael

0 0
Reply
Sign in to reply online Use email software

Dmitry Kurochkin

20 Feb 20 Feb
4:04 p.m.

Hi Michael. On Sun, 13 Feb 2011 19:13:06 +0300, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

I will try to send you some an example later. Unfortunately, I do not have time for this right now. Playing with Yesod in my free time only.

I took another look at it. Turns out my problem was caused by using getCurrentRoute in the widget. If there is no getCurrentRoute, it works fine and I can use the widget in the defaultLayout. Here is a simple code to demonstrate the issue: testWidget :: GWidget sub Test () testWidget = do Just route <- lift getCurrentRoute addWidget [$hamlet|@{route}|] Add the above code to the main application module. In the above case Test is the foundation type. I get the following error: Couldn't match expected type `TestRoute' against inferred type `Route sub' NB: `Route' is a type function, and may not be injective I would appreciate if you explain what the problem is and how to solve it. Regards, Dmitry

...
...
...
...

[snip]

...
...

> 2. Widget does not work in default layout. > > I guess this is a known and expected behavior. My feeling is that > hamletToRepHtml can not embed widgets because it may be too late to add > cassius and julius. As a workaround I split default layout into outer > and inner layout. Outer layout renders just HTML <head> and <body>. > While outer layout is rendered as a widget that embeds the actual page > contents. Since outer layout is rendered as a widget, it may embed other > widgets like menu. > > I imagine that hamletToRepHtml could render all embedded widgets before > the main body. Though, it may be difficult to implement, have > performance or other issues. Anyway, I think it is not uncommon to > include a widget in default layout. So Yesod should provide an easy way > to do it.

You should try looking at the scaffolded site: the function you want to use is widgetToPageContent[1]. It converts a complete Widget into the individual pieces that you need.

Yes, widgetToPageContent is used to convert the widget from handler and produces a set of pieces for page generation (pc). If I use it for a menu widget, I will get another PageContent (pc1). Now I need to take body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget can produce javascript and CSS which needs to be merged with the main PageContent. I did not find an existing function to do this. Did I miss it?

Just combine the two widgets and call widgetToPageContent once:

    defaultLayout widget = do         pc <- widgetToPageContents $ do             menuWidget             widget         hamletToRepHtml ...

You can see an example of this in the Yesod docs site[1].

I thought this would result in menuWidget placed directly before the main widget body, right? In many cases simple concatenation is not enough.

Then just modify menuWidget to take a Widget as an argument:

menuWidget :: GWidget s m () -> GWidget s m () menuWidget w = do earlierStuff w laterStuff

This is the very reason why we have polymorphic hamlet, so you can even do:

menuWidget w = [$hamlet| <p>Header ^{w} <p>Footer |]

In fact, if you put that into a separate menu-widget.hamlet file, you might get the results you were looking for originally all the back-bending.

This does not feel right in my case. Essentially, it moves part of layout to widget. If I have many widgets to combine, e.g. mainMenu, sideMenu, anotherCoolWidget, it becomes more complex. I think introducing a separate layout (my original workaround) is a better solution here. I just need to come up with a better name for it :)

defaultLayout content = do mmsg <- getMessage pc <- widgetToPageContent $ do addCassius $(Settings.cassiusFile "default-layout") addWidget $(Settings.cassiusFile "inner-layout") hamletToRepHtml $(Settings.hamletFile "outer-layout")

...

[snip]

...
...

Just to confirm: are you talking for general widgets, or just widgets to be called from defaultLayout? As I mention above, the former can easily be put in separate modules, the latter would require more work.

I am talking about defaultLayout widgets. In general, reasons for putting widgets into separate modules does not depend on whether they are used in defaultLayout. Though, I agree that the fact that only defaultLayout widgets must be put into the Yesod instance module somewhat improves the situation.

IMO from user point of view it does not matter much if widget is used in handler or in defaultLayout. In most cases, at least. I just create a menu widget and want be able to use it anywhere (even both in handlers and defaultLayout). E.g. in my case the only difference between handler and defaulLayout widget is the type signature, implementation does not change.

Nonetheless, these *are* the rules that exist in Haskell. To a certain extent, separating out mkYesodDispatch into Controller.hs is a similar hack to this. In that case, I think that the added benefit of separate handler modules definitely justifies using this technique. I'm simply not (yet) convinced that the benefit here is big enough to warrant a similar approach.

Well, you have my vote for what it's worth :)

Regards, Dmitry

...

Michael

0 0
Reply
Sign in to reply online Use email software

Michael Snoyman

21 Feb 21 Feb
12:23 a.m.

On Sun, Feb 20, 2011 at 11:04 PM, Dmitry Kurochkin wrote:

...

Hi Michael.

On Sun, 13 Feb 2011 19:13:06 +0300, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

I will try to send you some an example later. Unfortunately, I do not have time for this right now. Playing with Yesod in my free time only.

I took another look at it. Turns out my problem was caused by using getCurrentRoute in the widget. If there is no getCurrentRoute, it works fine and I can use the widget in the defaultLayout.

Here is a simple code to demonstrate the issue:

   testWidget :: GWidget sub Test ()    testWidget = do        Just route <- lift getCurrentRoute        addWidget [$hamlet|@{route}|]

Add the above code to the main application module. In the above case Test is the foundation type. I get the following error:

   Couldn't match expected type `TestRoute'           against inferred type `Route sub'      NB: `Route' is a type function, and may not be injective

I would appreciate if you explain what the problem is and how to solve it.

getCurrentRoute[1] returns a route for the current subsite, while you want a route for Test (ie, Route sub vs Route Test). You need to convert your Route sub into a Route Test, by using getRouteToMaster[2]. Michael [1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso... [2] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

0 0
Reply
Sign in to reply online Use email software

Dmitry Kurochkin

22 Feb 22 Feb
5:08 a.m.

On Mon, 21 Feb 2011 07:23:56 +0200, Michael Snoyman wrote:

...

On Sun, Feb 20, 2011 at 11:04 PM, Dmitry Kurochkin wrote:

...

Hi Michael.

On Sun, 13 Feb 2011 19:13:06 +0300, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

I will try to send you some an example later. Unfortunately, I do not have time for this right now. Playing with Yesod in my free time only.

I took another look at it. Turns out my problem was caused by using getCurrentRoute in the widget. If there is no getCurrentRoute, it works fine and I can use the widget in the defaultLayout.

Here is a simple code to demonstrate the issue:

   testWidget :: GWidget sub Test ()    testWidget = do        Just route <- lift getCurrentRoute        addWidget [$hamlet|@{route}|]

Add the above code to the main application module. In the above case Test is the foundation type. I get the following error:

   Couldn't match expected type `TestRoute'           against inferred type `Route sub'      NB: `Route' is a type function, and may not be injective

I would appreciate if you explain what the problem is and how to solve it.

getCurrentRoute[1] returns a route for the current subsite, while you want a route for Test (ie, Route sub vs Route Test). You need to convert your Route sub into a Route Test, by using getRouteToMaster[2].

Thank you! The following works: testWidget :: GWidget sub Test () testWidget = do Just route <- lift getCurrentRoute routeToMaster <- lift getRouteToMaster addWidget [$hamlet|@{routeToMaster route}|] Perhaps a getCurrentMasterRoute function would be useful? Though I am not sure how common is my case. So look like the only real issue with putting widgets for defaultLayout to a separate module is separating mkYesodData and Yesod instance. You were talking about orphan Yesod instance, is it some kind of dummy instance declaration that would allow widgets to use Yesod instance without importing the actual module that defines it? Regards, Dmitry

...

Michael

[1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso... [2] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

0 0
Reply
Sign in to reply online Use email software

Michael Snoyman

10:27 a.m.

On Tue, Feb 22, 2011 at 12:08 PM, Dmitry Kurochkin wrote:

...

On Mon, 21 Feb 2011 07:23:56 +0200, Michael Snoyman wrote:

...

On Sun, Feb 20, 2011 at 11:04 PM, Dmitry Kurochkin wrote:

...

Hi Michael.

On Sun, 13 Feb 2011 19:13:06 +0300, Dmitry Kurochkin wrote:

...

On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman wrote:

...

On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin wrote:

...

Still the argument remains. I want to have all menu-related code in a separate module.

OK. As I said, this is possible, and it sounds like you already got 90% of the way there. I don't know what's holding you up until I see your code. My initial reaction is still that this is a bad idea in general, though I *am* reconsidering that position. It might make sense to alter the approach used by the scaffolded site.

I will try to send you some an example later. Unfortunately, I do not have time for this right now. Playing with Yesod in my free time only.

I took another look at it. Turns out my problem was caused by using getCurrentRoute in the widget. If there is no getCurrentRoute, it works fine and I can use the widget in the defaultLayout.

Here is a simple code to demonstrate the issue:

   testWidget :: GWidget sub Test ()    testWidget = do        Just route <- lift getCurrentRoute        addWidget [$hamlet|@{route}|]

Add the above code to the main application module. In the above case Test is the foundation type. I get the following error:

   Couldn't match expected type `TestRoute'           against inferred type `Route sub'      NB: `Route' is a type function, and may not be injective

I would appreciate if you explain what the problem is and how to solve it.

getCurrentRoute[1] returns a route for the current subsite, while you want a route for Test (ie, Route sub vs Route Test). You need to convert your Route sub into a Route Test, by using getRouteToMaster[2].

Thank you! The following works:

   testWidget :: GWidget sub Test ()    testWidget = do        Just route <- lift getCurrentRoute        routeToMaster <- lift getRouteToMaster        addWidget [$hamlet|@{routeToMaster route}|]

Perhaps a getCurrentMasterRoute function would be useful? Though I am not sure how common is my case.

If you look through Yesod.Handler, there are a number of functions that are duplicated for sub and master sites. I had to choose which one would get the shorter name, and made a call based on my information at the time. However, I've wondered many times if I made the wrong choice on some of them. I think it would be a good idea to revisit all of those decisions before the 1.0 release: making those changes *will* break code, but not in any significant way. This would probably be a good candidate for change.

...

So look like the only real issue with putting widgets for defaultLayout to a separate module is separating mkYesodData and Yesod instance. You were talking about orphan Yesod instance, is it some kind of dummy instance declaration that would allow widgets to use Yesod instance without importing the actual module that defines it?

Nothing magical, it *should* be possible to just move the Yesod instance into a separate module. However, you'll likely get a cascade of error messages which you will need to fix by either moving code around or changing the type signature to work for any instance of Yesod. Plus you'll need to make sure that all your handler code imports that separate module. In other words: you can achieve this, but you'll be going into slightly uncharted territory. I give no guarantees that it won't make your code worse. Michael

...

Regards,  Dmitry

...

Michael

[1] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso... [2] http://hackage.haskell.org/packages/archive/yesod-core/0.7.0.1/doc/html/Yeso...

0 0
Reply
Sign in to reply online Use email software
5208
Age (days ago)
5217
Last active (days ago)

List overview

Download

10 comments
2 participants

Add to favorites Remove from favorites

tags

participants (2)

  • Dmitry Kurochkin
  • Michael Snoyman

HyperKitty Powered by HyperKitty version 1.3.9.