Yesod (web framework)

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search
Yesod
Original author(s) Michael Snoyman
Developer(s) Michael Snoyman et al.
Initial release 2010
Stable release
1.4.1[1][2] / November 23, 2014 (2014-11-23)
Repository Edit this at Wikidata
Written in Haskell
Operating system Cross-platform
Available in Haskell
Type Web framework
License MIT License
Website www.yesodweb.com

Yesod (IPA: [je'sod]; Hebrew: יְסוֺד‬, "Foundation") is a free and open-source web framework based on Haskell for productive development of type-safe, REST model based (where URLs identify resources, and HTTP methods identify transitions), high performance web applications, developed by Michael Snoyman et al.

Yesod is based on templates, to generate instances for classes, entities, and dynamic content process functions, making use of Haskell compiled templates called QuasiQuotes,[3] that admit code expression interpolations in web-like language snippets, making it fully type-checked at compile-time.[4]

MVC architecture[edit]

Controller[edit]

Server interface[edit]

Yesod uses a Web application interface API,[5] abbreviated WAI, to isolate servlets, aka web apps., from servers, with handlers for the server protocols CGI,[6] FastCGI,[7] SCGI,[8] Warp,[9] Launch (open as local URL to the default browser, closing the server when the window is closed),[10]

The foundation type[edit]

See ref.[11] Yesod requires a data type that instantiates the controller classes. This is called the foundation type. In the example below, it is named "MyApp".

The REST model identifies a web resource with a web path. Here REST resources are given names with an R suffix (like "HomeR") and are listed in a parseRoutes site map description template. From this list, route names and dispatch handler names are derived.

Yesod makes use of Template Haskell metaprogramming to generate code from templates at compile time, assuring that the names in the templates match and everything typechecks (e.g. web resource names and handler names).

By inserting a mkYesod call, this will call T.H. primitives to generate the code[12] corresponding to the route type members, and the instances of the dispatch controller classes as to dispatch GET calls to route HomeR to a routine named composing them both as "getHomeR", expecting an existing handler that matches the name.

Skeleton app. extracted from next section, "Hello world" example (changes with newer yesod versions: the actual handler types have changed, but the philosophy remains):

data MyApp = MyApp

instance Yesod MyApp

-- mkYesod generates instances of ParseRoute, RenderRoute and YesodDispatch classes for MyApp
--       with RenderRoute associated data-type "Route t" (see yesodweb "Basics" ref.)

mkYesod "MyApp" [parseRoutes|
/ HomeR GET
|]

-- handler that matches the dispatch function name generated by mkYesod, composing http method and route names

-- the handler may return one or more content-type representations, 
--    to be chosen (with chooseRep from class HasReps) 
--    upon http headers' preferred content-type list

getHomeR :: HasReps t => Handler t
getHomeR = defaultLayout $ setTitle "Yesod example"

-- there are ''run'' function variants for different WAI handlers

main = toWaiApp MyApp >>= run      -- WAI CGI-handler ''run''

WAI CGI Hello World[edit]

{- file wai-cgi-hello.hs -}
{-# LANGUAGE PackageImports, TypeFamilies, QuasiQuotes, MultiParamTypeClasses,
             TemplateHaskell, OverloadedStrings #-}
import "wai" Network.Wai
import "wai-extra" Network.Wai.Handler.CGI (run) -- interchangeable WAI handler

import "yesod" Yesod
import "yesod-core" Yesod.Handler (getRequest)
import "text" Data.Text (Text)
import "shakespeare" Text.Cassius (Color(..), colorBlack)

data MyApp = MyApp

mkYesod "MyApp" [parseRoutes|
/ HomeR GET
|]

instance Yesod MyApp

-- indentation structured CSS template
myStyle :: [Text]  CssUrl url
myStyle paramStyle =
        [cassius|
.box
    border: 1px solid #{boxColor}
|]
        where
          boxColor = case paramStyle of
                        ["high-contrast"]  colorBlack
                        _  Color 0 0 255

-- indentation structured HTML template
myHtml :: [(Text, Text)]  HtmlUrl url
myHtml params = [hamlet|
<!-- only the tag at the beginning of the line will be automatically closed -->
<!-- '.' prefix in tags introduces a class attribute, '#' introduces an id one -->
<!-- interpolation of haskell expressions follow the #{expr} syntax -->

<p>Hello World! There are <span .box>#{length params} parameters</span>:
$if (not . null) params
    <ul>
         $forall param <- params
             <li>#{fst param}: #{snd param}
|]
getHomeR :: Handler RepHtml
getHomeR = do
        req <- getRequest
        let params = reqGetParams req
        paramStyle <- lookupGetParams "style"
        
        defaultLayout $ do
            -- adding widgets to the Widget monad (a ''Writer'' monad)
            setTitle "Yesod example"
            toWidgetHead $ myStyle paramStyle
            toWidgetBody $ myHtml params

main = toWaiApp MyApp >>= run
# cgi test
export REMOTE_ADDR=127.0.0.1
export REQUEST_METHOD=GET
export PATH_INFO=/
export QUERY_STRING='p1=abc;p2=def;style=high-contrast'
./wai-cgi-hello

[11]

Resources, routes and HTTP method handlers[edit]

See ref.[13][14] Yesod follows the REpresentational State Transfer model of access to web documents, identifying docs. and directories as resources with a Route constructor, named with an uppercase R suffix (for example, HomeR).

The routes table
The parseRoutes template should list the resources specifying route pieces, resource name and dispatch methods to be accepted.
-- given a MyApp foundation type

mkYesod "MyApp" [parseRoutes|
/                     HomeR      -- no http methods stated: all methods accepted
/blog                 BlogR      GET POST

-- the '#' prefix specify the path segment as a route parameter
--    the type used must be an instance of class PathPiece 
--    the handlers types will have to be parameterized with it

/article/#ArticleId   ArticleR   GET PUT

-- the '*' prefix specify the parameter as a sequence of path pieces
--        the type used must be an instance of class PathMultiPiece

/branch/*Texts        BranchR    GET

-- compound types must use an alias, eg. type Texts = [Text]
|]

Applying the previous template generates the following route constructors:

-- interpolation of routes in templates follow the @{route_expr} syntax

data Route MyApp = 
    HomeR                    -- referenced in templates as: @{HomeR}
    | BlogR                  -- in templates: @{BlogR}
    | ArticleR ArticleId     -- in templates: @{ArticleR myArticleId}
    | BranchR Texts
Handlers
For every HTTP method a handler function must be created to match the dispatch names generated by mkYesod from the parseRoutes template, by prefixing the method name (or the prefix "handler" if no method stated) to the resource, as described (actual versions handler types have changed, but the philosophy remains):
-- for "/ HomeR"        -- no http methods stated ⇒ only one handler with prefix ''handler''
handlerHomeR :: HasReps t  Handler t

-- for "/blog BlogR GET POST"
getBlogR :: HasReps t  Handler t
postBlogR :: HasReps t  Handler t

-- for "/article/#ArticleId ArticleR GET PUT"
getArticleR :: HasReps t  ArticleId  Handler t
putArticleR :: HasReps t  ArticleId  Handler t

Request data, Parameters, Cookies, Languages and other Header info[edit]

See ref.[13]

Authentication and authorization[edit]

See ref.[15] Authentication plugins: OpenId, BrowserId, Email, GoogleEmail, HashDB, RpxNow.[16]

Redirection after authentication.[17]

Sessions[edit]

See ref.[18] Session back-ends: ClientSession.[19]

>> To avoid undue bandwidth overhead, production sites can serve their static content from a separate domain name to avoid the overhead of transmitting the session cookie for each request

Subsites[edit]

See ref.[20][21]

>> A subsite is a collection of routes and their handlers that can be easily inserted into a master site.[20] It can host a workflow with a common url prefix.

Built-in subsites: Static,[22][23] Auth[24]

Subsite Static[edit]

For every file in the "static" folder, a symbol with type (Route Static) is generated for reference, by means of a compile time splice call in the scaffold module StaticFiles.hs, that replaces non-identifier characters "/-." with underscores:[25]

-- referring to the file "static/css/hk-kate.css"
addStylesheet $ StaticR css_hk_kate_css

After adding static files, regenerate (::Route Static) symbols at the next recompilation, just updating the StaticFiles.hs date:

touch Settings/StaticFiles.hs

View[edit]

The Handler monad returns content in one or more of several formats as components of types that implement the HasReps class[26] {RepHtml, RepJson, RepXml, RepPlain, the dual RepHtmlJson, a pair or list of pairs [(ContentType, Content)], ..}.[27][28] Json examples:[29][30][31]

The HasReps default implementation of chooseRep chooses the document representation to be returned according to the preferred content-type list of the client accept header.[26]

Widgets[32] are HTML DOM code snippets made by specific commands (e.g. setTitle) or from templates of structure (html) / behaviour (javascript) / style (css), whose types instantiate the classes ToWidget, ToWidgetHead or ToWidgetBody.

A Widget monad,[33] based on a Writer[34] one and argument to defaultLayout, facilitate to piece the widgets together.

Template interpolation - Shakespearean templates[edit]

See ref.[35] These are content view templates that follow a common substitution pattern of code expressions within curly brackets with different character prefix to refer to

template expressions with ^{...}
other templates of the same type as ^{template params},
route expressions with @{...}
safe (typed) urls as @{HomeR},
message expressions with _{...}
i18n message rendering as _{MsgMessage params}
other Haskell expressions with #{...}
haskell expression rendering as #{haskell_expression} which type must be convertible
    • in case of hamlet html templates, the expression type must be an instance of Text.Blaze.ToMarkup[36]
    • in case of css templates, the expression type must be an instance of Text.Cassius.ToCss[37]
    • in case of javascript templates, the expression type must be an instance of Text.Julius.ToJavascript [38]
    • in case of i18n message definitions (in "isoLang.msg" files) with parameter interpolations, the expression type must be an instance of Text.Shakespeare.I18N.ToMessage [39]
    • in case of plain text templates, the expression type must be an instance of Text.Shakespeare.Text.ToText [40]

Using non-English text in expressions requires use of the Unicode-aware type Text, since GHC's show for the type String renders non-ASCII characters as escaped numerical codes.

  • external file templates: Template content can be loaded from external files using compile time splice calls as $(expr).[41]
  • reload mode for external files: See doc.[35]
Localizable (i18n) messages[edit]

See ref.[42] For every supported language ISO name there should be a file in the messages subfolder as <iso-language>.msg with entries like

ArticleUnexistant param@Int64: unexistant article #{param}

For each entry in en.msg a message constructor is generated, prefixing the message name by "Msg", so the example msg. can be referred as

-- in code
myMsg :: MyAppMessage  -- datatype attaching "Message" to the foundation type
myMsg = MsgArticleUnexistant myArticleId 
     
-- in templates
  _{MsgArticleUnexistant myArticleId}

Actual i18n support is missing from the stack app template. You have to add the mkMessage "MyApp" messagesFolder isoLangDefault to the "Foundation.hs" file to get the messages instantiated.[43]

HTML-like templates[edit]
  • the hamlet quasiquoter (a parser to compile-time Template Haskell code)[3][44] specified in the T.H. Oxford brackets syntax [qq| ... |] introduces an indentation based structured html template with '$' prefixed lines of logic statements (See doc.[35]).[45] Automatic closing tags are generated only for the tag at line start position.
  • the whamlet quasiquoter returns a Widget expression. (saves toWidget before [hamlet|..|]).
toWidget [hamlet|
$doctype 5
<html>
    <head>
        <title>#{pageTitle} - My Site
        <link rel=stylesheet href=@{Stylesheet_route}>
    <body>
        <header>
           ^{headerTemplate}
        <section>
          <p><span style="font-weight:bold;">_{MsgArticleListTitle}</span>
          $if null articles
            <p>_{MsgSorryNoArticles}
          $else
            <ul>
                $forall art <- articles
                    <li>#{articleNumber art} .- #{articleTitle art}
        <footer>
          ^{footerTemplate}
|]
JavaScript templates[edit]
  • the julius quasiquoter: introduces a javascript template.[46] Javascript variants CoffeeScript and Roy-language[47] have also specific quasiquoters.[3][46]
toWidgetHead [julius|

var myfunc = function(){document.location = "@{SomeRouteR}";}

^{extraJuliusTemplate}
|]
CSS-like templates[edit]
  • the cassius quasiquoter: introduces a css template with indentation based structuring.[48]
toWidget [cassius|
.box
    border: 1px solid #{myColor}
    background-image: url(@{MyImageR})

^{extraCassiusTemplate}
|]
  • the lucius quasiquoter: introduces a css template with standard syntax plus shakespeare-template style substitutions.[49]
toWidgetHead [lucius|
.box { border: 1px solid #{myColor} ;
       background-image: url(@{MyImageR}) ;
     }

^{extraLuciusTemplate}
|]
Plain text templates[edit]
  1. templates: lt: lazy text, st: strict text
  2. templates for text with a left margin delimiter '|': lbt (lazy), sbt (strict)
[lt| Mr./Mrs. #{fullName} ... 
|]

Specific views[edit]

  • Search engines XML Sitemaps,[51] where sitemap returns an XML Sitemap as http response, with the routes we want the search engines to crawl, and attributes to instruct the crawler, from a provided list of SitemapUrl records.
  • Navigation Breadcrumbs.[52] You have to provide a YesodBreadcrumbs instance for the site where the generator function breadcrumb should return a title and parent route for each one. Then, the query function breadcrumbs will return the present route title and ancestors' (route, title) pairs.
  • Web feed views (RSS / Atom).[53] You have handlers that return RepRss, RepAtom, or dual RepAtomRss content (to be selected on accept headers' preferred content-type list) from a given Feed structure.

Model[edit]

Using in-memory mutable data (in the foundation datatype)[edit]

E.g. a visitor count. See ref.[54]

Persistent[edit]

  • persistent is the name of the database access layer with templates for generating types for entities and keys as well as schema initialization.[55][56]

There is first class support for PostgreSQL, SQLite, MongoDB, CouchDB and MySQL, with experimental support for Redis.[55]

automatic table creation, schema update and table migration
Modifications of the entities template produces an schema update with automatic table creation, and migration for the DBMS's that support "ALTER TABLE" SQL commands in a migrateAll procedure, generated from the template content. See "Migrations" in ref.[55] to look for migration aware DBMS.
share [mkPersist sqlSettings, 
       mkMigrate "migrateAll"   -- generates the migration procedure with the specified name 
       ] [persist|

User                            -- table name and entity record type
           -- implicit autoincrement column "id" as primary key, 
           -- typed UserId (Id suffix to rec. type)
    ident Text             -- refers to db. table column "ident"; 
                     -- generates a record field prefixing the table name as  "userIdent"
    password Text Maybe         -- Maybe indicates Nullable field
    UniqueUser ident            -- unique constraint with space sep. field sequence

Email
    email Text
    user UserId                 -- foreign key
    verkey Text Maybe

    -- When adding new columns, in case the table is not empty
    -- the DB system requires a default new column value 
    -- for existing table rows, for the migration to succeed.
    -- A "default constraint" (with sql-level type) is required

    newlyAddedColumn Text "default='sometext'::character varying"

    UniqueEmail email
|]
  • Esqueleto: is a haskell combinators layer to generate correct relational queries to persistent.[57]

Example for persistent rawSQL and Esqueleto queries.[58]

  • Default column values for added columns in automatic migrations.[59]

Forms[edit]

See ref.[60]

A form is a sequential (Applicative/Monadic) composition of fields for a sequential parsing of field inputs giving a pair as result: (FormResult: the parsing result, and xml: the view widget to use in the next rendering of the page including error msgs and marks for missing required fields).

The types of forms are Applicative (with tabular layout), Monadic (with free layout style) in the Yesod.Form.Functions module and Input (for parsing only) in the Yesod.Form.Input module.

Field definitions, which can be (a|m|i){- the form type -}(req|opt){- required or optional -}, have a fieldParse component and a fieldView one.[61]

  • the function runForm{Post|Get} runs the field parsers against the form field inputs and generates a (FormResult, Widget) pair from the views offering a new form widget with the sent form field values as defaults. The method suffix is the form submission method used.
  • while generateForm{Post|Get} ignores the web inputs and generates a blank form widget.[62]

The actual function parameters and types have changed through Yesod versions. Check the Yesod book and libraries signatures.

The magic is in the FormResult data type Applicative instance, where (<*>) collects the error messages for the case of FormFailure [textErrMsg] result values[63]

Monadic forms permit free form layout and better treatment of hiddenField members.[60]

A sample of an Applicative[64] form:

{- A simple entity
[persist|
    Person
        name Text
        age Int
        likings Text Maybe   -- Maybe: nullable field
|]
-}
-- ''persist'' equivalent entity record for Person

data Person = Person {personName :: Text, personAge :: Int, personLikings :: Maybe Text}

type Form sub master x = Html  MForm sub master (FormResult x, Widget)

{-
-- for messages in validation functions:
  @param master: yesod instance to use in renderMessage (return from handler's getYesod)
  @param languages: page languages to use in renderMessage

-- optional defaults record:
  @param mbPersonDefaults: Just defaults_record, or Nothing for blank form
-}

personForm :: MyFoundationType  [Text]  Maybe Person  Form sub master Person
{- ''aopt'' (optional field AForm component) for nullable entity fields (tagged Maybe),
   ''areq'' (required fld AForm comp.) for non-nullable entity ones 
-}
personForm master languages mbPersonDefaults = renderTable $ 
    Person <$> areq textField fldSettingsName mbNameDefault 
    <*> areq customizedPersonAgeField fldSettingsAge mbAgeDefault 
    <*> aopt textareaField fldSettingsLikings mbLikingsDefault 
  where
    mbNameDefault = fmap personName mbPersonDefaults
    mbAgeDefault = fmap personAge mbPersonDefaults
    mbLikingsDefault = fmap personLikings mbPersonDefaults

    fldSettingsName = (fieldSettingsLabel MsgName) {fsAttrs = [("maxlength","20")]}
    fldSettingsAge = fieldSettingsLabel MsgAge
    fldSettingsLikings = (fieldSettingsLabel MsgLikings) {fsAttrs = [("cols","40"),("rows","10")]}

    customizedPersonAgeField = check validateAge intField

    validateAge y
        | y < 18 = Left $ renderMessage master languages MsgUnderAge
        | otherwise = Right y

See renderMessage signature.[65]

Other protocols[edit]

E-mail[edit]

The following packages are part of the yesod-platform:[2]

  • email-validate: Validating an email address.[66]
  • mime-mail: Compose and send MIME email messages.[67]

Facebook[edit]

  • Useful glue functions between the fb library and Yesod.[68]

Development cycle[edit]

Getting started[edit]

Install haskellStack.[69]

Scaffolding[edit]

stack templates                   # list app templates
stack new myproject yesod-sqlite  # or "yesod-{minimal | postgres | mysql | mongo | ...}"

cd myproject

stack build
stack exec myproject
  • Packages not in the stackage repo, but in std. hackage should be referred in the "stack.yaml" extra-deps section.
  • Customized packages, should be in a local subfolder and referred in the "stack.yaml" packages section.

Developing[edit]

Adding static files requires to unix touch the Settings/StaticFiles.hs module to generate the correspondent route symbols as explained.

  • The yesod helper tool [70]
    • yesod devel run from the project site, recompiles and restarts the project at every file tree modification.
    • yesod add-handler adds a new handler and module to the project (Adding it manually requires to add an import clause for the handler in Application.hs)

Logging for debug[edit]

See ref.[71] The yesod scaffold uses "wai-extra" Network.Wai.Middleware.RequestLogger[72] for request logging, although there are alternatives.[71]

The package "monad-logger"[73] brings T.H. generated logging functions with automatic inclusion of line location, which can be used in the monads Handler, Widget, PersistQuery, PersistStore, .. which are instances of MonadLogger.[74]

The following "monad-logger" calls are also available from "yesod-core":

$(logDebug) "This is a debug log message"

$(logInfo), $(logWarn), $(logError) and $(logOther) are also available

You can set per case log default by overriding the shouldLog method of the Yesod class instance for the site.[75]

Deploying[edit]

See ref.[76]

Keter: A web app server monitor and reverse proxy server[edit]

See refs.[77] [78]

Keter is a process as a service that handles deployment and restart of web app servers, and, per web app, database creation for PostgreSQL.

The console command yesod keter packs the web app. as a keter bundle for uploading to a keter folder named "incoming".

Keter monitors the "incoming" folder and unpacks the app. to a temporary one, then assigns the web app a port to listen to, and starts it.

Initially it worked with Nginx as reverse proxy (keter version 0.1*), adding virtual server entries to its configuration and making Nginx reload it, but now Keter itself provides its own reverse proxy functionality, removing Nginx dependency and acting as the main web server.[79]

Old documentation (Nginx based).[80][81]

Integration with JavaScript generated from functional languages[edit]

See ref.[82][83][84]

from true GHC Haskell with hacked compilers having JavaScript backends[edit]

Haste[85][86][87] and GhcJs[88] offer alternatives that generate Javascript from the STG[89] output phase of GHC admitting haskell code compilable with GHC. While GhcJs is more feature complete (concurrency, etc.), it requires and generates much more code than Haste.[84][90][91]

Haste sample.[85] Other examples at the ref.[92]

{- file Haste_example.hs -}
import Haste

main = do
  Just inp  <- elemById "user_input"
  Just outp <- elemById "script_output"

  onEvent inp OnKeyUp $ \_ -> do
      text <- getProp inp "value"
      setProp outp "innerHTML" text

Compiling:

hastec Haste_example.hs    # generates .js

Yesod widgets code:

addScript $ StaticR js_Haste_example_js       -- for static/js/Haste_example.js

toWidget [hamlet| 
 <section>
   <p> Type here: 
      <input #user_input type="text" maxlength="20">
   <p> Script test result:
      <span #script_output>
|]

Elm - reactive composition of web user interfaces[edit]

Elm is a novel functional reactive programming language (no callbacks) with Haskell like syntax (type annotations included)[93] that compiles to Javascript, but with strict evaluation (top down evaluation, no where clauses), with simplified syntax (single pattern definitions, no guards), propagating events and changes from event sources through the dependency graph.

Elm role is not for adding JavaScript behaviour to existing elements, but to build up a variable html structure that recomposes itself in reaction to event streams.

Although it looks nice, JavaScript UIs are not search-engine friendly because the content not present in the first layout will probably not be indexed.

The Elm language has changed a lot. You can find a more recent description here.[94] Elm has abandoned the "functional reactive" aspect and has adopted a client-side Model-View-Controller architecture.[95][96]

See also[edit]

References[edit]

  1. ^ "The yesodweb license". Github.com. Retrieved 2014-11-23. 
  2. ^ a b "The yesod package". Hackage.haskell.org. Retrieved 2014-12-07. 
  3. ^ a b c "HaskellWiki - QuasiQuotation". Haskell.org. 2012-05-26. Retrieved 2012-10-23. 
  4. ^ "Univ. of Kent - Comparing Dynamic and Static Language Approaches to Web Frameworks - Yesod vs Ruby on Rails" (PDF). Retrieved 2012-10-23. 
  5. ^ "The wai package". Hackage.haskell.org. Retrieved 2012-10-23. 
  6. ^ "The wai-extra package with CGI WAI handler". Hackage.haskell.org. Retrieved 2012-10-23. 
  7. ^ "The wai-handler-fastcgi package". Hackage.haskell.org. Retrieved 2012-10-23. 
  8. ^ "The wai-handler-scgi package". Hackage.haskell.org. Retrieved 2012-10-23. 
  9. ^ "The warp package". Hackage.haskell.org. Retrieved 2012-10-23. 
  10. ^ "The wai-handler-launch package". Hackage.haskell.org. Retrieved 2012-10-23. 
  11. ^ a b "book - Basics". Yesodweb.com. Retrieved 2012-10-23. 
  12. ^ The mkYesod code
  13. ^ a b "book - Routing and Handlers". Yesodweb.com. Retrieved 2012-10-23. 
  14. ^ "Playing with Routes and Links". FPComplete.com. 2012-10-17. Retrieved 2012-10-28. 
  15. ^ "book - Authentication and Authorization". Yesodweb.com. Retrieved 2012-10-23. 
  16. ^ "The yesod-auth package". Hackage.haskell.org. Retrieved 2012-10-26. 
  17. ^ "book - Sessions - See section "Ultimate Destination"". Yesodweb.com. Retrieved 2012-11-17. 
  18. ^ "Sessions". Yesodweb.com. Retrieved 2012-10-23. 
  19. ^ "Web.ClientSession". Hackage.haskell.org. Retrieved 2012-10-25. 
  20. ^ a b "Creating a Subsite". Yesodweb.com. Retrieved 2012-10-25. 
  21. ^ "Yesod and subsites: a no-brainer". Monoid.se. 2012-08-22. Retrieved 2012-10-28. []
  22. ^ "The Magic of Yesod, part 2 - See section "Static Subsite"". Yesodweb.com. 2010-12-25. Retrieved 2012-10-25. 
  23. ^ "The package yesod-static - Static Subsite". Hackage.haskell.org. Retrieved 2012-10-25. 
  24. ^ "The package yesod-auth - Auth Subsite". Hackage.haskell.org. Retrieved 2012-10-25. 
  25. ^ "The package yesod-auth - staticFiles routes (replacing non-identifier-characters with underscores)". Hackage.haskell.org. Retrieved 2012-11-04. 
  26. ^ a b "The class HasReps". Hackage.haskell.org. Retrieved 2012-10-23. 
  27. ^ "RESTful Content". Yesodweb.com. Retrieved 2012-10-23. 
  28. ^ "The class ToContent". Hackage.haskell.org. Retrieved 2012-10-23. 
  29. ^ "More Client Side Yesod: todo sample". Yesodweb.com. 2012-04-23. Retrieved 2012-10-23. 
  30. ^ "JSON Web Service". Yesodweb.com. Retrieved 2012-10-23. 
  31. ^ "The yesod-json package". Hackage.haskell.org. Retrieved 2012-10-23. 
  32. ^ "book - Widgets". Yesodweb.com. Retrieved 2012-10-23. 
  33. ^ "The widget monad". Hackage.haskell.org. Retrieved 2012-10-23. 
  34. ^ "The Writer monad". Haskell.org. Retrieved 2012-10-23. 
  35. ^ a b c "book - Shakesperean templates". Yesodweb.com. Retrieved 2012-10-23. 
  36. ^ "Class Text.Blaze.ToMarkup". Hackage.haskell.org. Retrieved 2012-10-23. 
  37. ^ "Class Text.Cassius.ToCss". Hackage.haskell.org. Retrieved 2012-10-23. 
  38. ^ "Class Text.Julius.ToJavascript". Hackage.haskell.org. Retrieved 2012-10-23. 
  39. ^ "Class Text.Shakespeare.I18N.ToMessage". Hackage.haskell.org. Retrieved 2012-10-24. 
  40. ^ "Class Text.Shakespeare.Text.ToText". Hackage.haskell.org. Retrieved 2012-10-24. 
  41. ^ "Template Haskell". haskell.org. Retrieved 2012-11-03. 
  42. ^ "book - Internationalization". Yesodweb.com. Retrieved 2012-10-23. 
  43. ^ mkMessage
  44. ^ "Template Haskell Quasi-quotation". Haskell.org. Retrieved 2012-11-02. 
  45. ^ "The hamlet template module". Hackage.haskell.org. Retrieved 2012-10-23. 
  46. ^ a b "The Julius template module". Hackage.haskell.org. Retrieved 2012-10-23. 
  47. ^ "Roy language". Roy.brianmckenna.org. Retrieved 2012-10-23. 
  48. ^ "The Cassius template module". Hackage.haskell.org. Retrieved 2012-10-23. 
  49. ^ "The Lucius template module". Hackage.haskell.org. Retrieved 2012-10-23. 
  50. ^ "Shakespeare plain text templates module". Hackage.haskell.org. Retrieved 2012-10-24. 
  51. ^ "The yesod-sitemap package". Hackage.haskell.org. Retrieved 2012-10-26. 
  52. ^ "The YesodBreadcrumbs class". Hackage.haskell.org. Retrieved 2012-11-05. 
  53. ^ "The yesod-newsfeed package for RSS / Atom views". Hackage.haskell.org. Retrieved 2012-10-26. 
  54. ^ "Book - Initializing data in the foundation datatype". Yesodweb.com. Retrieved 2014-05-26. 
  55. ^ a b c "book - Persistent". Yesodweb.com. Retrieved 2012-10-23. 
  56. ^ "Yesod-persistent package". Hackage.haskell.org. Retrieved 2012-10-23. 
  57. ^ "esqueleto package". Hackage.haskell.org. Retrieved 2012-10-23. 
  58. ^ "Query example at". Stackoverflow.com. 2012-09-19. Retrieved 2012-10-23. 
  59. ^ "Redundant migrations for fields' default values". GitHub.com. Retrieved 2012-12-04. 
  60. ^ a b "book - Forms". Yesodweb.com. Retrieved 2012-10-23. 
  61. ^ "Yesod.Form.Fields". Hackage.haskell.org. Retrieved 2012-10-23. 
  62. ^ "Yesod.Form.Functions runFormPost". Hackage.haskell.org. Retrieved 2012-10-25. 
  63. ^ "Yesod.Form.Types". Hackage.haskell.org. Retrieved 2012-10-23. 
  64. ^ "HaskellWiki - Applicative functor". haskell.org. Retrieved 2012-10-24. 
  65. ^ "I18N renderMessage". Hackage.haskell.org. Retrieved 2012-11-05. 
  66. ^ "The email-validate package". Hackage.haskell.org. Retrieved 2012-10-26. 
  67. ^ "The mime-mail package". Hackage.haskell.org. Retrieved 2012-10-26. 
  68. ^ "The yesod-fb package". Hackage.haskell.org. Retrieved 2012-10-26. 
  69. ^ Haskell Stack - How to install
  70. ^ The yesod-bin pkg with the helper tool (with instructions for use with the stack tool)
  71. ^ a b "blog - Yesod's new logging system". Yesodweb.com. 2012-07-05. Retrieved 2012-10-23. 
  72. ^ "Network.Wai.Middleware.RequestLogger". Hackage.haskell.org. Retrieved 2012-10-23. 
  73. ^ "The package monad-logger". Hackage.haskell.org. Retrieved 2012-10-23. 
  74. ^ "Database.Persist.GenericSql". Hackage.haskell.org. Retrieved 2012-10-23. 
  75. ^ "class Yesod's method shouldLog". Hackage.haskell.org. Retrieved 2012-11-17. 
  76. ^ "book - Deploying your Webapp". Yesodweb.com. Retrieved 2012-10-23. 
  77. ^ Readme.Md. "Yesod keter readme". GitHub. Retrieved 2012-10-23. 
  78. ^ "The keter package". Hackage.haskell.org. Retrieved 2012-10-23. 
  79. ^ "Keter updates". Yesodweb.com. 2012-10-25. Retrieved 2012-10-25. 
  80. ^ "Keter: Web App Deployment". Yesodweb.com. 2012-05-11. Retrieved 2012-10-23. 
  81. ^ "Keter: It's Alive!". Yesodweb.com. 2012-05-17. Retrieved 2012-10-23. 
  82. ^ "Javascript Options". github.com. Retrieved 2014-03-12. 
  83. ^ "Yesod, AngularJS and Fay". yesodweb.com. 2012-10-30. Retrieved 2014-03-12. 
  84. ^ a b "HaskellWiki - The JavaScript Problem". haskell.org. Retrieved 2014-04-12. 
  85. ^ a b "Haste: Running Haskell in the Browser". cse.chalmers.se. 2013. Retrieved 2014-04-12. 
  86. ^ "Try Haste". haste-lang.org. Retrieved 2014-04-12. 
  87. ^ "valderman/haste-compiler". github.com/valderman. Retrieved 2014-04-12. 
  88. ^ "GhcJs". github.com/ghcjs. Retrieved 2014-04-12. 
  89. ^ "GHC - Code generation". haskell.org. Retrieved 2014-04-12. 
  90. ^ "Haste report - Towards a Declarative Web" (PDF). ekblad.cc. Retrieved 2014-04-12. 
  91. ^ "GHCJS, Concurrent Haskell in the Browser" (PDF). haskell.org. Retrieved 2014-04-12. 
  92. ^ "Haste examples". github.com/valderman. Retrieved 2014-04-12. 
  93. ^ Elm Syntax
  94. ^ "The Elm Architecture + Effects". elm-lang.org. Retrieved 2018-02-15. 
  95. ^ "A Farewell to FRP". elm-lang.org. Retrieved 2018-09-06. 
  96. ^ "Elm from a Haskell perspective". schoolofhaskell.com. Retrieved 2018-09-06. 

External links[edit]

Blog tutorials[edit]

Comparisons[edit]

Other languages[edit]

At GNU/Linux distributions[edit]