Send Beginners mailing list submissions to beginners@haskell.org To subscribe or unsubscribe via the World Wide Web, visit http://www.haskell.org/mailman/listinfo/beginners or, via email, send a message with subject or body 'help' to beginners-requ...@haskell.org
You can reach the person managing the list at beginners-ow...@haskell.org When replying, please edit your Subject line so it is more specific than "Re: Contents of Beginners digest..." Today's Topics: 1. New Thread: looking for some advice in regard to LINQ (Patrick Lynch) 2. working off a Yesod example file, need help lifting values from one monad into another. (and probably other things too). (Michael Litchard) 3. Re: New Thread: looking for some advice in regard to LINQ (Daniel Pratt) 4. Re: New Thread: looking for some advice in regard to LINQ (Daniel Pratt) 5. Re: [Haskell-cafe] working off a Yesod example file, need help lifting values from one monad into another. (and probably other things too). (Michael Litchard) ---------------------------------------------------------------------- Message: 1 Date: Mon, 28 Mar 2011 16:08:48 -0400 From: "Patrick Lynch" <kmandpjly...@verizon.net> Subject: [Haskell-beginners] New Thread: looking for some advice in regard to LINQ To: "Haskell Beginners List" <beginners@haskell.org> Message-ID: <C6D5463B1C144BB1B6F5891C6936729E@UserPC> Content-Type: text/plain; format=flowed; charset=iso-8859-1; reply-type=response Good afternoon, I'm just about finished researching Monads - wow! I'm still trying to figure out Category Theory. I would like to explore LINQ. Can anyone give me a book reference or tutorial reference? for it? In addition, should it be studied with F# or with C#? Good day ------------------------------ Message: 2 Date: Mon, 28 Mar 2011 17:28:41 -0700 From: Michael Litchard <mich...@schmong.org> Subject: [Haskell-beginners] working off a Yesod example file, need help lifting values from one monad into another. (and probably other things too). To: beginners@haskell.org, haskell-cafe <haskell-c...@haskell.org> Message-ID: <aanlktikh-nwmr0-ji+qj_px-1rfy9+uyyt+th+d0d...@mail.gmail.com> Content-Type: text/plain; charset=ISO-8859-1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 I'm working off of a example file from Yesod, ajax.lhs I've made an important change in types, and this has resulted in having to make the old code conform to the change. I will point out the specifics, then present my question. In the event I failed to include important information, I will paste in my code as well as the prototype. [Original] > getHomeR :: Handler () > getHomeR = do > Ajax pages _ <- getYesod > let first = head pages > redirect RedirectTemporary $ PageR $ pageSlug first [Changed] > getHomeR :: Handler () > getHomeR = do > Tframe pages _ <- getYesod > let first = head pages > redirect RedirectTemporary $ PageR $ pageSlug first Error Message test.lhs:62:4: Constructor `Tframe' should have 2 arguments, but has been given 1 In the pattern: Tframe pages In a stmt of a 'do' expression: Tframe pages <- getYesod **** This is not what I wrote ***** In the expression: do { Tframe pages <- getYesod; content <- widgetToPageContent widget; hamletToRepHtml (hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue (do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "<!DOCTYPE html><html><head><title>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (Main.pageTitle content); .... })) } As far as I can tell, I only made a cosmetic change. I don't know what's going on here. [Original] > data Page = Page > { pageName :: String > , pageSlug :: String > , pageContent :: String ******** I'm going to change this ********** > } [Changed] > data Page = Page > { pageTitle :: String > , pageSlug :: String -- ^ used in the URL > , pageContent :: IO String ******** This is the change ******* > } Here's where I run into trouble [Original] > json page = jsonMap > [ ("name", jsonScalar $ pageName page) > , ("content", jsonScalar $ pageContent page) ******** I'm going to > change this ******** > ] [My changes] > json page = jsonMap > [ ("name", jsonScalar $ Main.pageTitle page) > , ("content", jsonScalar $ liftIO $ pageContent page) ******* This is > the change *********** > ] Here's the compiler error test.lhs:107:35: Couldn't match expected type `Char' against inferred type `[Char]' Expected type: String Inferred type: [String] In the second argument of `($)', namely `liftIO $ pageContent page' In the expression: jsonScalar $ liftIO $ pageContent page Failed, modules loaded: none. I'd appreciate a discussion about why this is wrong, and perhaps clues as to what is right. Last problem, stemming from the change in type to IO String. I don't have a clue as to what change I should make. test.lhs:100:25: No instance for (Text.Blaze.ToHtml (IO String)) arising from a use of `Text.Blaze.toHtml' at test.lhs:(100,25)-(103,3) Possible fix: add an instance declaration for (Text.Blaze.ToHtml (IO String)) In the second argument of `(.)', namely `Text.Blaze.toHtml' In a stmt of a 'do' expression: (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (pageContent page) In the first argument of `hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue', nam ely `do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "<h1>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (Main.pageTitle page); (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "</h1><article>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (pageContent page); .... }' And finally, both files can be found below, if it is necessary to look at them. [Original] <p>We're going to write a very simple AJAX application. It will be a simple site with a few pages and a navbar; when you have Javascript, clicking on the links will load the pages via AJAX. Otherwise, it will use static HTML.</p> <p>We're going to use jQuery for the Javascript, though anything would work just fine. Also, the AJAX responses will be served as JSON. Let's get started.</p> > {-# LANGUAGE ScopedTypeVariables, TypeFamilies, QuasiQuotes, TemplateHaskell, > MultiParamTypeClasses #-} > import Yesod > import Yesod.Helpers.Static > import Data.Monoid (mempty) Like the blog example, we'll define some data first. > data Page = Page > { pageName :: String > , pageSlug :: String > , pageContent :: String > } > loadPages :: IO [Page] > loadPages = return > [ Page "Page 1" "page-1" "My first page" > , Page "Page 2" "page-2" "My second page" > , Page "Page 3" "page-3" "My third page" > ] loadPages :: IO [Page] loadPages = do > > data Ajax = Ajax > { ajaxPages :: [Page] > , ajaxStatic :: Static > } > type Handler = GHandler Ajax Ajax Next we'll generate a function for each file in our static folder. This way, we get a compiler warning when trying to using a file which does not exist. > staticFiles "static/yesod/ajax" Now the routes; we'll have a homepage, a pattern for the pages, and use a static subsite for the Javascript and CSS files. > mkYesod "Ajax" [$parseRoutes| > / HomeR GET > /page/#String PageR GET > /static StaticR Static ajaxStatic > |] <p>That third line there is the syntax for a subsite: Static is the datatype for the subsite argument; siteStatic returns the site itself (parse, render and dispatch functions); and ajaxStatic gets the subsite argument from the master argument.</p> <p>Now, we'll define the Yesod instance. We'll still use a dummy approot value, but we're also going to define a default layout.</p> > instance Yesod Ajax where > approot _ = "" > defaultLayout widget = do > Ajax pages _ <- getYesod > content <- widgetToPageContent widget > hamletToRepHtml [$hamlet| > \<!DOCTYPE html> > > <html> > <head> > <title>#{pageTitle content} > <link rel="stylesheet" href="@{StaticR style_css}"> > <script > src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> > <script src="@{StaticR script_js}"> > \^{pageHead content} > <body> > <ul id="navbar"> > $forall page <- pages > <li> > <a href="@{PageR (pageSlug page)}">#{pageName page} > <div id="content"> > \^{pageBody content} > |] <p>The Hamlet template refers to style_css and style_js; these were generated by the call to staticFiles above. There's nothing Yesod-specific about the <a href="/static/yesod/ajax/style.css">style.css</a> and <a href="/static/yesod/ajax/script.js">script.js</a> files, so I won't describe them here.</p> <p>Now we need our handler functions. We'll have the homepage simply redirect to the first page, so:</p> > getHomeR :: Handler () > getHomeR = do > Ajax pages _ <- getYesod > let first = head pages > redirect RedirectTemporary $ PageR $ pageSlug first And now the cool part: a handler that returns either HTML or JSON data, depending on the request headers. > getPageR :: String -> Handler RepHtmlJson > getPageR slug = do > Ajax pages _ <- getYesod > case filter (\e -> pageSlug e == slug) pages of > [] -> notFound > page:_ -> defaultLayoutJson (do > setTitle $ string $ pageName page > addHamlet $ html page > ) (json page) > where > html page = [$hamlet| > <h1>#{pageName page} > <article>#{pageContent page} > |] > json page = jsonMap > [ ("name", jsonScalar $ pageName page) > , ("content", jsonScalar $ pageContent page) > ] <p>We first try and find the appropriate Page, returning a 404 if it's not there. We then use the applyLayoutJson function, which is really the heart of this example. It allows you an easy way to create responses that will be either HTML or JSON, and which use the default layout in the HTML responses. It takes four arguments: 1) the title of the HTML page, 2) some value, 3) a function from that value to a Hamlet value, and 4) a function from that value to a Json value.</p> <p>Under the scenes, the Json monad is really just using the Hamlet monad, so it gets all of the benefits thereof, namely interleaved IO and enumerator output. It is pretty straight-forward to generate JSON output by using the three functions jsonMap, jsonList and jsonMap. One thing to note: the input to jsonScalar must be HtmlContent; this helps avoid cross-site scripting attacks, by ensuring that any HTML entities will be escaped.</p> <p>And now our typical main function. We need two parameters to build our Ajax value: the pages, and the static loader. We'll load up from a local directory.</p> > main :: IO () > main = do > > pages <- loadPages > let s = static "static/yesod/ajax" > warpDebug 3000 $ Ajax pages s [My changes] > {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses > #-} > import Yesod > import Yesod.Helpers.Static > import System.Environment > import System.IO > import System.Directory > import System.FilePath.Posix > import Control.Applicative > import Data.List.Split > data Page = Page > { pageTitle :: String > , pageSlug :: String -- ^ used in the URL > , pageContent :: IO String > } > loadPage :: IO [Page] > loadPage = do > let directoryPath = "/home/mlitchard/playground/webTests/files" > let processedPath = map (directoryPath </>) . filter (`notElem` [".",".."]) > pageFileNames <- processedPath <$> getDirectoryContents directoryPath > let pageFiles = map readFile pageFileNames > return $ zipWith popEntries pageFileNames pageFiles -- > return $ zipWith popEntries > popEntries :: FilePath -> IO String -> Page > popEntries pageFileName pageFile = > let pageT = last $ splitOn "/" pageFileName > pageS = "Job" ++ pageT > in Page { Main.pageTitle=pageT, > pageSlug=pageS, > pageContent=pageFile } > data Tframe = Tframe > { tframePages :: [Page] > , tframeStatic :: Static > } > type Handler = GHandler Tframe Tframe > staticFiles "static/yesod/ajax" Routes > mkYesod "Tframe" [$parseRoutes| > / HomeR GET > /page/#String PageR GET > /static StaticR Static tframeStatic > |] defining the Yesod instance > instance Yesod Tframe where > approot _ = "" > defaultLayout widget = do > Tframe pages <- getYesod > content <- widgetToPageContent widget > hamletToRepHtml [$hamlet| > \<!DOCTYPE html> > > <html> > <head> > <title>#{Main.pageTitle content} > <link rel="stylesheet" href="@{StaticR style_css}"> > <script > src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> > <script src="@{StaticR script_js}"> > \^{pageHead content} > <body> > <ul id="navbar"> > $forall page <- pages > <li> > <a href="@{PageR (pageSlug page)}">#{Main.pageTitle page} > <div id="content"> > \^{pageBody content} > |] > getHomeR :: Handler () > getHomeR = do > Tframe pages _ <- getYesod > let first = head pages > redirect RedirectTemporary $ PageR $ pageSlug first > getPageR :: String -> Handler RepHtmlJson > getPageR slug = do > Tframe pages _ <- getYesod > case filter (\e -> pageSlug e == slug) pages of > [] -> notFound > page:_ -> defaultLayoutJson (do > setTitle $ string $ Main.pageTitle page > addHamlet $ html page > ) (json page) > where > html page = [$hamlet| > <h1>#{Main.pageTitle page} > <article>#{pageContent page} > |] > json page = jsonMap > [ ("name", jsonScalar $ Main.pageTitle page) > , ("content", jsonScalar $ liftIO $ pageContent page) > ] > main :: IO () > main = do > > pages <- loadPage > let s = static "static/yesod/ajax" > warpDebug 3000 $ Tframe pages s If you've read to the bottom, thanks for your patience. I appreciate any illumination you can send my way. Michael ------------------------------ Message: 3 Date: Mon, 28 Mar 2011 21:59:50 -0400 From: Daniel Pratt <colorblind...@gmail.com> Subject: Re: [Haskell-beginners] New Thread: looking for some advice in regard to LINQ To: Haskell Beginners List <beginners@haskell.org> Message-ID: <aanlktimttsx44hn4eevtfwvd4tpttsfdk_rcgfeml...@mail.gmail.com> Content-Type: text/plain; charset=ISO-8859-1 On Mon, Mar 28, 2011 at 4:08 PM, Patrick Lynch <kmandpjly...@verizon.net> wrote: > > Good afternoon, > > I'm just about finished researching Monads - wow! > I'm still trying to figure out Category Theory. > > I would like to explore LINQ. Can anyone give me a book reference or tutorial > reference? for it? > In addition, should it be studied with F# or with C#? > > I don't know about a book, but 101 LINQ Samples has proven useful to me on more than one occasion. As far as F# vs. C#, I would strongly recommend trying it with C#. LINQ is much better integrated into C#. My sense of the LINQ support in F# is that it's more of a proof-of-concept. Surprisingly, LINQ also seems to be much more cumbersome in F#, which is counter to my intuition. > > Good day > > _______________________________________________ > Beginners mailing list > Beginners@haskell.org > http://www.haskell.org/mailman/listinfo/beginners ------------------------------ Message: 4 Date: Mon, 28 Mar 2011 22:00:57 -0400 From: Daniel Pratt <colorblind...@gmail.com> Subject: Re: [Haskell-beginners] New Thread: looking for some advice in regard to LINQ To: Haskell Beginners List <beginners@haskell.org> Message-ID: <aanlktim-tcjfwrxsuudw1qmhwqesxexiernriedh9...@mail.gmail.com> Content-Type: text/plain; charset=ISO-8859-1 Oops. Accidentally dropped the link to 101 LINQ Samples: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx On Mon, Mar 28, 2011 at 9:59 PM, Daniel Pratt <colorblind...@gmail.com> wrote: > On Mon, Mar 28, 2011 at 4:08 PM, Patrick Lynch <kmandpjly...@verizon.net> > wrote: >> >> Good afternoon, >> >> I'm just about finished researching Monads - wow! >> I'm still trying to figure out Category Theory. >> >> I would like to explore LINQ. Can anyone give me a book reference or >> tutorial reference? for it? >> In addition, should it be studied with F# or with C#? >> >> > > I don't know about a book, but 101 LINQ Samples has proven useful to > me on more than one occasion. > > As far as F# vs. C#, I would strongly recommend trying it with C#. > LINQ is much better integrated into C#. My sense of the LINQ support > in F# is that it's more of a proof-of-concept. Surprisingly, LINQ also > seems to be much more cumbersome in F#, which is counter to my > intuition. > >> >> Good day >> >> _______________________________________________ >> Beginners mailing list >> Beginners@haskell.org >> http://www.haskell.org/mailman/listinfo/beginners > ------------------------------ Message: 5 Date: Mon, 28 Mar 2011 20:00:51 -0700 From: Michael Litchard <mich...@schmong.org> Subject: Re: [Haskell-beginners] [Haskell-cafe] working off a Yesod example file, need help lifting values from one monad into another. (and probably other things too). To: Luke Palmer <lrpal...@gmail.com> Cc: beginners@haskell.org, haskell-cafe <haskell-c...@haskell.org> Message-ID: <aanlktimwtqnk2vaw1fd12h32nzrqbsuovuizqyijv...@mail.gmail.com> Content-Type: text/plain; charset=ISO-8859-1 I just noticed those. I think that came from hpaste. The first mail was a cut and paste from a post I made there. When I went to look at your reply, I had the very same question as you. On Mon, Mar 28, 2011 at 7:51 PM, Luke Palmer <lrpal...@gmail.com> wrote: > On Mon, Mar 28, 2011 at 6:28 PM, Michael Litchard <mich...@schmong.org> > wrote: >> >> 1 >> 2 >> 3 >> 4 >> 5 >> 6 >> 7 >> 8 >> 9 >> 10 >> 11 >> 12 >> 13 >> 14 >> 15 >> 16 >> 17 >> 18 >> 19 >> 20 >> 21 >> 22 >> 23 >> 24 >> 25 >> 26 >> 27 >> 28 >> 29 >> 30 >> 31 >> 32 >> 33 >> 34 >> 35 >> 36 >> 37 >> 38 >> 39 >> 40 >> 41 >> 42 >> 43 >> 44 >> 45 >> 46 >> 47 >> 48 >> 49 >> 50 >> 51 >> 52 >> 53 >> 54 >> 55 >> 56 >> 57 >> 58 >> 59 >> 60 >> 61 >> 62 >> 63 >> 64 >> 65 >> 66 >> 67 >> 68 >> 69 >> 70 >> 71 >> 72 >> 73 >> 74 >> 75 >> 76 >> 77 >> 78 >> 79 >> 80 >> 81 >> 82 >> 83 >> 84 >> 85 >> 86 >> 87 >> 88 >> 89 >> 90 >> 91 >> 92 >> 93 >> 94 >> 95 >> 96 >> 97 >> 98 >> 99 >> 100 >> 101 >> 102 >> 103 >> 104 >> 105 >> 106 >> 107 >> 108 >> 109 >> 110 >> 111 >> 112 >> 113 >> 114 >> 115 >> 116 >> 117 >> 118 >> 119 >> 120 >> 121 >> 122 >> 123 >> 124 >> 125 >> 126 >> 127 >> 128 >> 129 >> 130 >> 131 >> 132 >> 133 >> 134 >> 135 >> 136 >> 137 >> 138 >> 139 >> 140 >> 141 >> 142 >> 143 >> 144 >> 145 >> 146 >> 147 >> 148 >> 149 >> 150 >> 151 >> 152 >> 153 >> 154 >> 155 >> 156 >> 157 >> 158 >> 159 >> 160 >> 161 >> 162 >> 163 >> 164 >> 165 >> 166 >> 167 >> 168 >> 169 >> 170 >> 171 >> 172 >> 173 >> 174 >> 175 >> 176 >> 177 >> 178 >> 179 >> 180 >> 181 >> 182 >> 183 >> 184 >> 185 >> 186 >> 187 >> 188 >> 189 >> 190 >> 191 >> 192 >> 193 >> 194 >> 195 >> 196 >> 197 >> 198 >> 199 >> 200 >> 201 >> 202 >> 203 >> 204 >> 205 >> 206 >> 207 >> 208 >> 209 >> 210 >> 211 >> 212 >> 213 >> 214 >> 215 >> 216 >> 217 >> 218 >> 219 >> 220 >> 221 >> 222 >> 223 >> 224 >> 225 >> 226 >> 227 >> 228 >> 229 >> 230 >> 231 >> 232 >> 233 >> 234 >> 235 >> 236 >> 237 >> 238 >> 239 >> 240 >> 241 >> 242 >> 243 >> 244 >> 245 >> 246 >> 247 >> 248 >> 249 >> 250 >> 251 >> 252 >> 253 >> 254 >> 255 >> 256 >> 257 >> 258 >> 259 >> 260 >> 261 >> 262 >> 263 >> 264 >> 265 >> 266 >> 267 >> 268 >> 269 >> 270 >> 271 >> 272 >> 273 >> 274 >> 275 >> 276 >> 277 >> 278 >> 279 >> 280 >> 281 >> 282 >> 283 >> 284 >> 285 >> 286 >> 287 >> 288 >> 289 >> 290 >> 291 >> 292 >> 293 >> 294 >> 295 >> 296 >> 297 >> 298 >> 299 >> 300 >> 301 >> 302 >> 303 >> 304 >> 305 >> 306 >> 307 >> 308 >> 309 >> 310 >> 311 >> 312 >> 313 >> 314 >> 315 >> 316 >> 317 >> 318 >> 319 >> 320 >> 321 >> 322 >> 323 >> 324 >> 325 >> 326 >> 327 >> 328 >> 329 >> 330 >> 331 >> 332 >> 333 >> 334 >> 335 >> 336 >> 337 >> 338 >> 339 >> 340 >> 341 >> 342 >> 343 >> 344 >> 345 >> 346 >> 347 >> 348 >> 349 >> 350 >> 351 >> 352 >> 353 >> 354 >> 355 >> 356 >> 357 >> 358 >> 359 >> 360 >> 361 >> 362 >> 363 >> 364 >> 365 >> 366 >> 367 >> 368 > > Ready or not, here I come. > What is the purposes of these 368 numbers? > Luke > >> >> >> I'm working off of a example file from Yesod, ajax.lhs >> I've made an important change in types, and this has resulted in >> having to make the old code conform to the change. I will point out >> the specifics, then present my question. In the event I failed to >> include important information, I will paste in my code as well as the >> prototype. >> >> [Original] >> >> > getHomeR :: Handler () >> > getHomeR = do >> > ? Ajax pages _ <- getYesod >> > ? let first = head pages >> > ? redirect RedirectTemporary $ PageR $ pageSlug first >> >> [Changed] >> >> > getHomeR :: Handler () >> > getHomeR = do >> > ? Tframe pages _ <- getYesod >> > ? let first = head pages >> > ? redirect RedirectTemporary $ PageR $ pageSlug first >> >> Error Message >> >> test.lhs:62:4: >> ? ?Constructor `Tframe' should have 2 arguments, but has been given 1 >> ? ?In the pattern: Tframe pages >> ? ?In a stmt of a 'do' expression: Tframe pages <- getYesod ? **** >> This is not what I wrote ***** >> ? ?In the expression: >> ? ? ? ?do { Tframe pages <- getYesod; >> ? ? ? ? ? ? content <- widgetToPageContent widget; >> ? ? ? ? ? ? hamletToRepHtml >> ? ? ? ? ? ? ? (hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue >> ? ? ? ? ? ? ? ? ?(do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? ? ? ? ? ? . preEscapedString) >> ? ? ? ? ? ? ? ? ? ? ? ? ?"<!DOCTYPE html><html><head><title>"; >> ? ? ? ? ? ? ? ? ? ? ? ?(hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? ? ? ? ? ? . Text.Blaze.toHtml) >> ? ? ? ? ? ? ? ? ? ? ? ? ?(Main.pageTitle content); >> ? ? ? ? ? ? ? ? ? ? ? ?.... })) } >> >> As far as I can tell, I only made a cosmetic change. I don't know >> what's going on here. >> >> >> >> [Original] >> >> > data Page = Page >> > ? { pageName :: String >> > ? , pageSlug :: String >> > ? , pageContent :: String ? ? ******** I'm going to change this >> > ********** >> > ? } >> >> [Changed] >> >> > data Page = Page >> > ? ? ? { pageTitle :: String >> > ? ? ? , pageSlug :: String -- ^ used in the URL >> > ? ? ? , pageContent :: IO String ? ? ? ? ? ******** This is the change >> > ******* >> > ? ? ? } >> >> >> Here's where I run into trouble >> >> [Original] >> > ? json page = jsonMap >> > ? ? ? [ ("name", jsonScalar $ pageName page) >> > ? ? ? , ("content", jsonScalar $ pageContent page) ? ******** I'm going >> > to change this ******** >> > ? ? ? ] >> >> >> >> [My changes] >> >> > ? json page = jsonMap >> > ? ? ? [ ("name", jsonScalar $ Main.pageTitle page) >> > ? ? ? , ("content", jsonScalar $ liftIO $ pageContent page) ******* This >> > is the change *********** >> > ? ? ? ] >> >> >> Here's the compiler error >> >> test.lhs:107:35: >> ? ?Couldn't match expected type `Char' against inferred type `[Char]' >> ? ? ?Expected type: String >> ? ? ?Inferred type: [String] >> ? ?In the second argument of `($)', namely `liftIO $ pageContent page' >> ? ?In the expression: jsonScalar $ liftIO $ pageContent page >> Failed, modules loaded: none. >> >> >> I'd appreciate a discussion about why this is wrong, and perhaps clues >> as to what is right. >> >> >> Last problem, stemming from the change in type to IO String. I don't >> have a clue as to what change I should make. >> >> test.lhs:100:25: >> ? ?No instance for (Text.Blaze.ToHtml (IO String)) >> ? ? ?arising from a use of `Text.Blaze.toHtml' >> ? ? ? ? ? ? ? ? ? at test.lhs:(100,25)-(103,3) >> ? ?Possible fix: >> ? ? ?add an instance declaration for (Text.Blaze.ToHtml (IO String)) >> ? ?In the second argument of `(.)', namely `Text.Blaze.toHtml' >> ? ?In a stmt of a 'do' expression: >> ? ? ? ?(hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? . Text.Blaze.toHtml) >> ? ? ? ? ?(pageContent page) >> ? ?In the first argument of >> `hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue', nam >> >> ? ? ? ? ? ? ? ? ? ? ? ? ?ely >> ? ? ? ?`do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? . preEscapedString) >> ? ? ? ? ? ? ? ?"<h1>"; >> ? ? ? ? ? ? ?(hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? . Text.Blaze.toHtml) >> ? ? ? ? ? ? ? ?(Main.pageTitle page); >> ? ? ? ? ? ? ?(hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? . preEscapedString) >> ? ? ? ? ? ? ? ?"</h1><article>"; >> ? ? ? ? ? ? ?(hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad >> ? ? ? ? ? ? . Text.Blaze.toHtml) >> ? ? ? ? ? ? ? ?(pageContent page); >> ? ? ? ? ? ? ?.... }' >> >> >> >> And finally, both files can be found below, if it is necessary to look at >> them. >> >> >> [Original] >> >> <p>We're going to write a very simple AJAX application. It will be a >> simple site with a few pages and a navbar; when you have Javascript, >> clicking on the links will load the pages via AJAX. Otherwise, it will >> use static HTML.</p> >> >> <p>We're going to use jQuery for the Javascript, though anything would >> work just fine. Also, the AJAX responses will be served as JSON. Let's >> get started.</p> >> >> > {-# LANGUAGE ScopedTypeVariables, TypeFamilies, QuasiQuotes, >> > TemplateHaskell, MultiParamTypeClasses #-} >> > import Yesod >> > import Yesod.Helpers.Static >> > import Data.Monoid (mempty) >> >> Like the blog example, we'll define some data first. >> >> > data Page = Page >> > ? { pageName :: String >> > ? , pageSlug :: String >> > ? , pageContent :: String >> > ? } >> >> > loadPages :: IO [Page] >> > loadPages = return >> > ? [ Page "Page 1" "page-1" "My first page" >> > ? , Page "Page 2" "page-2" "My second page" >> > ? , Page "Page 3" "page-3" "My third page" >> > ? ] >> >> ?loadPages :: IO [Page] >> ?loadPages = do >> >> > >> > data Ajax = Ajax >> > ? { ajaxPages :: [Page] >> > ? , ajaxStatic :: Static >> > ? } >> > type Handler = GHandler Ajax Ajax >> >> Next we'll generate a function for each file in our static folder. >> This way, we get a compiler warning when trying to using a file which >> does not exist. >> >> > staticFiles "static/yesod/ajax" >> >> Now the routes; we'll have a homepage, a pattern for the pages, and >> use a static subsite for the Javascript and CSS files. >> >> > mkYesod "Ajax" [$parseRoutes| >> > / ? ? ? ? ? ? ? ? ?HomeR ? GET >> > /page/#String ? ? ?PageR ? GET >> > /static ? ? ? ? ? ?StaticR Static ajaxStatic >> > |] >> >> <p>That third line there is the syntax for a subsite: Static is the >> datatype for the subsite argument; siteStatic returns the site itself >> (parse, render and dispatch functions); and ajaxStatic gets the >> subsite argument from the master argument.</p> >> >> <p>Now, we'll define the Yesod instance. We'll still use a dummy >> approot value, but we're also going to define a default layout.</p> >> >> > instance Yesod Ajax where >> > ? approot _ = "" >> > ? defaultLayout widget = do >> > ? Ajax pages _ <- getYesod >> > ? content <- widgetToPageContent widget >> > ? hamletToRepHtml [$hamlet| >> > \<!DOCTYPE html> >> > >> > <html> >> > ? <head> >> > ? ? <title>#{pageTitle content} >> > ? ? <link rel="stylesheet" href="@{StaticR style_css}"> >> > ? ? <script >> > src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> >> > ? ? <script src="@{StaticR script_js}"> >> > ? ? \^{pageHead content} >> > ? <body> >> > ? ? <ul id="navbar"> >> > ? ? ? $forall page <- pages >> > ? ? ? ? <li> >> > ? ? ? ? ? <a href="@{PageR (pageSlug page)}">#{pageName page} >> > ? ? <div id="content"> >> > ? ? ? \^{pageBody content} >> > |] >> >> <p>The Hamlet template refers to style_css and style_js; these were >> generated by the call to staticFiles above. ?There's nothing >> Yesod-specific about the <a >> href="/static/yesod/ajax/style.css">style.css</a> and <a >> href="/static/yesod/ajax/script.js">script.js</a> files, so I won't >> describe them here.</p> >> >> <p>Now we need our handler functions. We'll have the homepage simply >> redirect to the first page, so:</p> >> >> > getHomeR :: Handler () >> > getHomeR = do >> > ? Ajax pages _ <- getYesod >> > ? let first = head pages >> > ? redirect RedirectTemporary $ PageR $ pageSlug first >> >> And now the cool part: a handler that returns either HTML or JSON >> data, depending on the request headers. >> >> > getPageR :: String -> Handler RepHtmlJson >> > getPageR slug = do >> > ? Ajax pages _ <- getYesod >> > ? case filter (\e -> pageSlug e == slug) pages of >> > ? ? ? [] -> notFound >> > ? ? ? page:_ -> defaultLayoutJson (do >> > ? ? ? ? ? setTitle $ string $ pageName page >> > ? ? ? ? ? addHamlet $ html page >> > ? ? ? ? ? ) (json page) >> > ?where >> > ? html page = [$hamlet| >> > <h1>#{pageName page} >> > <article>#{pageContent page} >> > |] >> > ? json page = jsonMap >> > ? ? ? [ ("name", jsonScalar $ pageName page) >> > ? ? ? , ("content", jsonScalar $ pageContent page) >> > ? ? ? ] >> >> <p>We first try and find the appropriate Page, returning a 404 if it's >> not there. We then use the applyLayoutJson function, which is really >> the heart of this example. It allows you an easy way to create >> responses that will be either HTML or JSON, and which use the default >> layout in the HTML responses. It takes four arguments: 1) the title of >> the HTML page, 2) some value, 3) a function from that value to a >> Hamlet value, and 4) a function from that value to a Json value.</p> >> >> <p>Under the scenes, the Json monad is really just using the Hamlet >> monad, so it gets all of the benefits thereof, namely interleaved IO >> and enumerator output. It is pretty straight-forward to generate JSON >> output by using the three functions jsonMap, jsonList and jsonMap. One >> thing to note: the input to jsonScalar must be HtmlContent; this helps >> avoid cross-site scripting attacks, by ensuring that any HTML entities >> will be escaped.</p> >> >> <p>And now our typical main function. We need two parameters to build >> our Ajax value: the pages, and the static loader. We'll load up from a >> local directory.</p> >> >> > main :: IO () >> > main = do >> > >> > ? pages <- loadPages >> > ? let s = static "static/yesod/ajax" >> > ? warpDebug 3000 $ Ajax pages s >> >> >> [My changes] >> > {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, >> > MultiParamTypeClasses >> > ?#-} >> > import Yesod >> > import Yesod.Helpers.Static >> > import System.Environment >> > import System.IO >> > import System.Directory >> > import System.FilePath.Posix >> > import Control.Applicative >> > import Data.List.Split >> >> >> >> > data Page = Page >> > ? ? ? { pageTitle :: String >> > ? ? ? , pageSlug :: String -- ^ used in the URL >> > ? ? ? , pageContent :: IO String >> > ? ? ? } >> >> >> >> > loadPage :: IO [Page] >> > loadPage = do >> > ?let directoryPath = "/home/mlitchard/playground/webTests/files" >> > ?let processedPath = map (directoryPath </>) . filter (`notElem` >> > [".",".."]) >> > ?pageFileNames <- processedPath <$> getDirectoryContents directoryPath >> > ?let pageFiles = map readFile pageFileNames >> > ?return $ zipWith popEntries pageFileNames pageFiles >> >> -- > ?return $ zipWith popEntries >> >> > popEntries :: FilePath -> IO String -> Page >> > popEntries pageFileName pageFile = >> > ? let pageT = last $ splitOn "/" pageFileName >> > ? ? ? pageS = "Job" ++ pageT >> > ? in ?Page { Main.pageTitle=pageT, >> > ? ? ? ? ? ? ?pageSlug=pageS, >> > ? ? ? ? ? ? ?pageContent=pageFile } >> >> > data Tframe = Tframe >> > ? { tframePages :: [Page] >> > ? , tframeStatic :: Static >> > ? } >> >> > type Handler = GHandler Tframe Tframe >> >> > staticFiles "static/yesod/ajax" >> >> Routes >> >> > mkYesod "Tframe" [$parseRoutes| >> > / ? ? ? ? ? ? ? ? ? ?HomeR ? GET >> > /page/#String ? ? ? ?PageR ? GET >> > /static ? ? ? ? ? ? ?StaticR Static tframeStatic >> > |] >> >> defining the Yesod instance >> >> > instance Yesod Tframe where >> > ? approot _ = "" >> > ? defaultLayout widget = do >> > ? Tframe pages <- getYesod >> > ? content <- widgetToPageContent widget >> > ? hamletToRepHtml [$hamlet| >> > \<!DOCTYPE html> >> > >> > <html> >> > ? <head> >> > ? ? <title>#{Main.pageTitle content} >> > ? ? <link rel="stylesheet" href="@{StaticR style_css}"> >> > ? ? <script >> > src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> >> > ? ? <script src="@{StaticR script_js}"> >> > ? ? \^{pageHead content} >> > ? <body> >> > ? ? <ul id="navbar"> >> > ? ? ? $forall page <- pages >> > ? ? ? ? <li> >> > ? ? ? ? ? <a href="@{PageR (pageSlug page)}">#{Main.pageTitle page} >> > ? ? <div id="content"> >> > ? ? ? \^{pageBody content} >> > |] >> >> >> > getHomeR :: Handler () >> > getHomeR = do >> > ? Tframe pages _ <- getYesod >> > ? let first = head pages >> > ? redirect RedirectTemporary $ PageR $ pageSlug first >> >> > getPageR :: String -> Handler RepHtmlJson >> > getPageR slug = do >> > ? Tframe pages _ <- getYesod >> > ? case filter (\e -> pageSlug e == slug) pages of >> > ? ? ? [] -> notFound >> > ? ? ? page:_ -> defaultLayoutJson (do >> > ? ? ? ? ? setTitle $ string $ Main.pageTitle page >> > ? ? ? ? ? addHamlet $ html page >> > ? ? ? ? ? ) (json page) >> > ?where >> > ? html page = [$hamlet| >> > <h1>#{Main.pageTitle page} >> > <article>#{pageContent page} >> > |] >> >> > ? json page = jsonMap >> > ? ? ? [ ("name", jsonScalar $ Main.pageTitle page) >> > ? ? ? , ("content", jsonScalar $ liftIO $ pageContent page) >> > ? ? ? ] >> >> > main :: IO () >> > main = do >> > >> > ? pages <- loadPage >> > ? let s = static "static/yesod/ajax" >> > ? warpDebug 3000 $ Tframe pages s >> >> >> If you've read to the bottom, thanks for your patience. I appreciate >> any illumination you can send my way. >> >> >> Michael >> >> _______________________________________________ >> Haskell-Cafe mailing list >> haskell-c...@haskell.org >> http://www.haskell.org/mailman/listinfo/haskell-cafe > > ------------------------------ _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners End of Beginners Digest, Vol 33, Issue 39 *****************************************