Original author(s) Initial release 2010 Written in Haskell | Developer(s) Michael Snoyman et al. Development status Active | |
Stable release 1.4.1 / November 23, 2014 (2014-11-23) |
Yesod ([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.
Contents
- Server interface
- The foundation type
- Resources routes and HTTP method handlers
- Request data Parameters Cookies Languages and other Header info
- Authentication and authorization
- Sessions
- Subsites
- View
- Template interpolation Shakespearean templates
- Specific views
- Using in memory mutable data in the foundation datatype
- Persistent
- Forms
- E mail
- Getting started
- Scaffolding
- Configuration environments
- Developing
- Logging for debug
- Deploying
- Keter A web app server monitor and reverse proxy server
- Integration with JavaScript generated from functional languages
- from true GHC Haskell with hacked compilers having JavaScript backends
- Elm reactive composition of web user interfaces
- Using Elm with Yesod in a remote echo example
- References
Yesod is based on templates, to generate instances for classes, entities, and dynamic content process functions, making use of Haskell compiled templates called QuasiQuotes, that admits code expression interpolations in web-like language snippets, making it fully type-checked at compile-time.
Server interface
Yesod uses a Web application interface API, abbrev. 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 T.H. 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.
Skeleton app. extracted from next section, "Hello world" example:
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).
Applying the previous template generates the following route constructors:
Request data, Parameters, Cookies, Languages and other Header info
See ref.
Authentication and authorization
See ref. Authentication plugins: OpenId, BrowserId, Email, GoogleEmail, HashDB, RpxNow.
Redirection after authentication.Sessions
See ref. Session back-ends: ClientSession.
>> 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 requestSubsites
See ref.
>> A subsite is a collection of routes and their handlers that can be easily inserted into a master site.Built-in subsites: Static, Auth
Subsite Static
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:
After adding static files, regenerate (::Route Static) symbols at the next recompilation, just updating the StaticFiles.hs date:
View
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.
Widgets 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, based on a Writer one and argument to defaultLayout, facilitate to piece the widgets together.
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
^{...}
^{template params}
,
@{...}
@{HomeR}
,
_{...}
_{MsgMessage params}
#{...}
#{haskell_expression}
which type must be convertible
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 (i18n) messages
See ref. 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 = MsgArticleUnexistant myArticleId -- in templates _{MsgArticleUnexistant myArticleId}HTML-like templates
[qq| ... |]
introduces an indentation based structured html template with '$' prefixed lines of logic statements (See doc.). Automatic closing tags are generated only for the tag at line start position.JavaScript templates
CSS-like templates
Plain text templates
Specific views
Using in-memory mutable data (in the foundation datatype)
E.g. a visitor count. See ref.
Persistent
There is first class support for PostgreSQL, SQLite, MongoDB, CouchDB and MySQL, with experimental support for Redis.
Example for persistent rawSQL and Esqueleto queries.
Forms
See ref. There are Applicative, Monadic and Input (non rendering, input only) kinds of forms.
Field definitions have a fieldParse component and a fieldView one.
The magic is in the Applicative and Functor instances of the data type FormResult, where (<*>) collects the error messages for the case of FormFailure [textErrMsg]
result values
Monadic forms permit free form layout and better treatment of hiddenField members.
A sample of an Applicative form:
See renderMessage signature.
The following packages are part of the yesod-platform:
Getting started
Create a sandboxed cabal-dev repository for yesod and yesod projects. Yesod 1.2 release notes.
Scaffolding
The console command yesod init
after asking for details, generates a starting scaffold application in a subfolder with the project name. Then cd to the project folder.
Create a link to the parent dir. cabal-dev folder (if you want to use the same library repository) and build the project
Configuration environments
They are predefined as {Development, Testing, Staging, Production} and each one heads a chapter in the YAML formatted configuration files ("config/" based settings.yml and <yourDBMS>.yml), with configuration attributes for the purpose stated by the environment name.
For each one, several attributes can be specified, e.g.: base url as approot, port, and other configurations, as well as different database names, its connection parameters, and other db parameters adjustable for the purposes of the specific environment.
You have to specify the name of the environment as argument to your project task. Example:
Developing
The console command yesod --dev devel --port 3000
(the --dev
flag is to look for the cabal-dev library repository) compiles the project in the current folder and starts it as a web server, but also listens for file modifications in the project directory tree, and recompiles and restarts it every time you save a yesod component, whether haskell code or template file.
Adding static files requires to unix touch the Settings/StaticFiles.hs module to generate the correspondent route symbols as explained.
Logging for debug
See ref. The yesod scaffold uses "wai-extra" Network.Wai.Middleware.RequestLogger for request logging, although there are alternatives.
The package "monad-logger" 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.
The following "monad-logger" calls are also available from "yesod-core":
You can set per case log default by overriding the shouldLog method of the Yesod class instance for the site.
Deploying
See ref.
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 web app servers, and, per web app, database creation for PostgreSQL.
The console command yesod --dev 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.
Old documentation (Nginx based).
Integration with JavaScript generated from functional languages
See ref.
from true GHC Haskell with hacked compilers having JavaScript backends
Haste and GhcJs offer alternatives that generate Javascript from the STG 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.
Haste sample. Other examples at the ref.
Equivalent with reactive data-flow style.
Compiling:
Yesod widgets code:
Elm - reactive composition of web user interfaces
Elm is a novel functional reactive programming language (no callbacks) with Haskell like syntax (type annotations included) 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.
Elm is more oriented to graphics than to text flows. Since everything can be scaled, moved and rotated, it needs precise knowledge of dimensions, so it doesn't use html lists, paragraphs and tables, whose positioning rely on the browser. With Elm you put text or graphics in rectangular Elements that can be stacked from lists (with Graphics.Element.flow direction elements) along horizontal, vertical or layer dimensions.
It unifies events and behaviours (varying values) in an effect named Signal handled as a Haskell applicative functor. You define value transformation functions and then you lift these functions to apply them to the Signal effect.
Elm calculator as example:
Instead of mangling the DOM tree nodes, Elm builds Html structure by composing Elements (html block elements) (with flow, container, layers or empty from Graphics.Element), from
Conditional structures may switch to Element.empty.
Styling. Elm needs to know precisely the width and height of elements, so styling is done through Elm styling functions instead of leaving it to external CSS. It lacks margins and paddings for now. You make element (html block) styles as a composition of (Element -> Element) styling functions, and text (html inline) styles in a similar way (Text -> Text).
Instead of web forms, you wrap the status signals of the input elements in a Request signal as input to Elm's Ajax Http.send
The main function effects-sequencing pattern is Applicative (there are no do blocks). You can add state effects through past-dependent folds (Signal.foldp, Signal.count) or state Automatons (an arrow instance) There is a third-party library repository
Elm requires
Styling is not as good as CSS, url encoding requires a third party library, but the language works fairly well. It seems more geared to build game UI's but with some extra work can do ajax web forms as well.
Disadvantages:
Advantages:
Using Elm with Yesod in a remote echo example
"Access-Control-Allow-Origin: *"
for Http GET and extra "Access-Control-Allow-Methods: GET, POST"
for Http POST. Url encoding of requests is implemented in a third-party library named elm_CodecURI.Install Elm and compile:
Yesod handler to return echo of "status" parameter.
Templates for inclusion of Elm functionality