Yesod (web framework)

Yesod (; , "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 listed entities, and dynamic content process functions, through Template Haskell constructs to host eDSL content templates called QuasiQuotes, where the content is translated into code expressions by metaprogramming instructions.
There are also web-like language snippet templates that admit code expression interpolations, making them fully type-checked at compile-time.
Yesod divides its functionality in separate libraries, so you may choose your {database, html rendering, forms, etc.} functionality library of your choice.
MVC architecture.
Controller.
Server interface.
Yesod uses a "Web application interface" API, abbreviated "WAI", to isolate servlets, aka web apps., from servers, with handlers for the server protocols CGI, FastCGI, SCGI, Warp, "Launch" (open as local "URL" to the default browser, closing the server when the window is closed),
The "foundation" type.
See ref. 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 Template Haskell primitives to generate the code 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.
Hello World.
"Hello world" example based on a CGI server interface (The actual handler types have changed, but the philosophy remains):
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)
-- the Foundation type
data MyApp = MyApp
-- sitemap template, listing path, resource name and methods accepted
-- `mkYesod` takes the foundation type name as param. for name composition of dispatch functions
mkYesod "MyApp" [parseRoutes|
/ HomeR GET
instance Yesod MyApp
-- indentation structured CSS template
myStyle :: [Text] → CssUrl url
myStyle paramStyle =
 [cassius|
.box
 where
 boxColor = case paramStyle of
 ["high-contrast"] → colorBlack
 _ → Color 0 0 255
-- indentation structured HTML template
myHtml :: [(Text, Text)] → HtmlUrl url
myHtml params = [hamlet|
<!-- indentation, or lack of it, under starting tags or commands ('$' prefix) 
 describe the content or sequence tree structure -->
<!-- '.' or '#' prefixes in tags introduce css styled "class" or "id" attribute values -->
<!-- interpolation of haskell expressions follow the "shakespeare templates" #{expr} syntax -->
<p>Hello World! There are <span .box>#{length params} parameters</span>:
$if null params
 <p>Nothing to list 
$else
 <ul>
 $forall param <- params
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
-- there are "run" function variants for different WAI handlers
main = toWaiApp MyApp »= run
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
Resources, routes and HTTP method handlers.
See ref. 
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).
URL segment capture as parameter is possible specifying a '#' prefix for single segment capture or '*' for multisegment capture, followed by the parameter type.
-- 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 handler parameter
/article/#ArticleId ArticleR GET PUT
-- the '*' prefix specify the parameter as a sequence of path pieces
/branch/*Texts BranchR GET
-- to simplify the grammar, compound types must use an alias, eg. type Texts for "[Text]"
data Route MyApp = 
-- 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.
See ref.
Authentication and authorization.
See ref. Authentication plugins: OpenId, BrowserId, Email, GoogleEmail, HashDB, RpxNow.
Sessions.
See ref. Session back-ends: ClientSession (it stores the session in a cookie), ServerSession (it stores most of the session data at the server)
Session messages.
A success, failure or indicative message can be stored ("setMessage") in the Session and will be shown, if it exists, by the "default_layout" routine through the codice_1 template, being cleared on consultation.
Subsites.
Common URL prefix subsites for workflows, file serving or site partitioning. See ref.
Built-in subsites: Static, Auth
Form processing and layout generation.
See ref.
The "Form" type here is an object that is used in the "controller" to parse and process the form fields user input and produce a (FormResult, Widget) pair were the widget holds the layout of the next rendering of the form with error messages and marks. It can also be used to generate a new form with blanks or default values.
The form type takes the shape of a function of an html snippet to be embedded in the view, that will hold security purpose hidden fields.
A form object is generated from an Applicative/Monadic composition of fields for a combined/sequential parsing of field inputs.
There are three types of forms:
The field generators, whose names are composed by the form type initial "codice_2 followed by codice_3", have a fieldParse component and a fieldView one. 
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 codice_4 result values
Monadic forms permit free form layout and better treatment of "hiddenField" members.
A sample of an "Applicative" form:
-- a record for our form fields
-- the Form type has an extra parameter for an html snippet to be embedded, containing a CSRF token hidden field for security
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
 "areq" (required fld AForm comp.) will insert the "required" attribute
personForm master languages mbPersonDefaults = renderTable $ 
 Person <$> areq textField fldSettingsName mbNameDefault 
 <*> areq customPersonAgeField fldSettingsAge mbAgeDefault 
 <*> aopt textareaField fldSettingsLikings mbLikingsDefault 
 where
 mbNameDefault = fmap personName mbPersonDefaults
 mbAgeDefault = fmap personAge mbPersonDefaults
 mbLikingsDefault = fmap personLikings mbPersonDefaults
 -- "fieldSettingsLabel" returns an initial fieldSettings record 
 -- recently the "FieldSettings" record can be defined from a String label since it implements IsString
 fldSettingsAge = fieldSettingsLabel MsgAge
 customPersonAgeField = check validateAge intField
 validateAge y
 | y < 18 = Left $ renderMessage master languages MsgUnderAge
 | otherwise = Right y
View.
The types shown correspond to an older version, but the philosophy remains.
The Handler monad returns content in one or more of several formats as components of types that implement the "HasReps" class {RepHtml, RepJson, RepXml, RepPlain, the dual RepHtmlJson, a pair or list of pairs "[(ContentType, Content)]", ..}. Json examples:
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.
A Widget monad, based on a Writer one and argument to "defaultLayout", facilitate to piece the widgets together.
Indentation based templates for tree structured markup.
'$' prefixes lines of logic statements.
Automatic closing tags are generated only for the tag at line start position.
toWidget [hamlet|
$doctype 5
<html>
 <!-- only the tag at the beginning of the line will be automatically closed -->
 <!-- '.' or '#' prefixes in tags introduce class/id names, à la CSS -->
 <!-- ":boolVar:" prefix in attributes makes them conditionally generated -->
 <!-- interpolation of haskell expressions follow the "shakespearean templates" 
 syntax introduced in the so named section -->
 <head>
 <title>#{pageTitle} - My Site
 <link rel=stylesheet href=@{Stylesheet_route}>
 <body>
 <header>
 <section #mySectionId>
 <p><span .titleClass>_{MsgArticleListTitle}</span>
 $if null articles
 $else
 <ul>
 $forall art <- articles
 <footer>
Template interpolation - "Shakespearean templates".
See ref.
These are content view templates that follow a common substitution pattern of code expressions within curly brackets with different character prefix to refer to 
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.
Localizable messages.
See ref.
Yesod app messages are localizable (i18n). They should be held within the "messages" folder, in files named based on ISO, as "<iso-language>.msg"
Message entries follow the EBNF pattern:
-- EBNF: identifier, {' ', parameter, '@', type}, ":", text with interpolations
-- in code
myMsg :: MyAppMessage -- datatype appending "Message" to the foundation type
myMsg = MsgArticleUnexistent myArticleId -- constructor prepending "Msg" to the msg. label
-- in widget templates
 _{MsgArticleUnexistent myArticleId} 
Actual i18n support is missing from the "stack" app template. You have to add the codice_15 to the "Foundation.hs" file to get the messages instantiated.
Model.
Using in-memory mutable data (in the foundation datatype).
E.g. a visitor count. See ref.
The Database layer.
There is first class support for PostgreSQL, SQLite, MongoDB, CouchDB and MySQL, with experimental support for Redis.
The Database layout is described in a template listing the entities, fields and constraints.
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
 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 -- table name and entity record type
 -- implicit autoincrement column "id" as primary key, typed EmailId
 email Text
 user UserId -- foreign key by specifying other tables EntityField types
 verkey Text Maybe
 newlyAddedColumn Text "default='sometext'::character varying" -- sql level Default constraint
 UniqueEmail email -- unique constraint
Example for "persistent rawSQL" and "Esqueleto" queries.
E-mail.
The following packages are part of the "yesod-platform":
Development cycle.
New Yesod apps are generated from the HaskellStack tool templates, replacing previous command "yesod init"
"Stack" based app. template names are prefixed by yesod as "yesod-{minimal | postgres | sqlite | mysql | mongo | ...}"
Deploying with Keter: A web app server monitor and reverse proxy server.
See refs.
Keter is a process as a service that handles deployment and restart of Yesod web app servers, and, per "web app", database creation for PostgreSQL.
The console command codice_18 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.
Old documentation (Nginx based).
Integration with JavaScript generated from functional languages.
See ref.