# gaetan-sumup/opalang forked from MLstate/opalang

[enhance] doc/book: Partial re-write of all wiki-based chapters.

```We are now using Markdown instad of Templates. Plus took this
opportunity to do a little clean-up.```
1 parent 76c582d commit 626cab31368b4835f97f926280b7777993179afa akoprow committed Nov 15, 2011
 @@ -826,9 +826,47 @@ a comparison is a boolean. We write that the type of function ======================== In Opa, booleans are values +{true = void}+ and +{false = void}+, or, more concisely but equivalently, +\{true\}+ and +\{false\}+. -You can check whether boolean +b+ is true or false by using +if b then ... else ...+ or, -equivalently, +match b with \{true\} -> ... | \{false\} -> ...+. + +Their type declaration looks as follow: +type bool = \{true\} / \{false\}+. +Such types, admitting one of a number of variants, are called sum types. +======================== + +[TIP] +.About sum types ======================== +A value has a _sum type_ +t / u+, meaning that the values of this type are either +of the two variants: either a value of type +t+ or a value of type +u+. + +A good example of sum type are the aforementioned boolean values, which are defined +as +type bool = \{false\}/\{true\}+. + +Another good example of sum type is the type +list+ of linked lists; its definition +can be summed up as +\{nil\} / \{hd: ...; tl: list\}+. + +Note that sum types are not limited to two cases. Sum types with tens of cases +are not uncommon in real applications. +======================= + +Safely determining which variant was used to construct a value of a sum type +can be accomplished with pattern matching. + +[TIP] +.About pattern-matching +======================== +The operation used to branch according to the case of a sum type +is called _pattern-matching_. A good example of pattern-matching +is +if ... then ... else ...+ . The more general syntax for pattern matching is ++match ... with | case_1 \-> ... | case_2 \-> ... | case_3 \-> ...+ + +The operation is actually more powerful than just determining which case of a +sum type is used. Indeed, if we use the vocabulary of languages such as Java or +C#, pattern-matching combines features of +if+, +switch+, +instanceof+/+is+, +multiple assignment and dereferenciation, but without the possible safety issues +of +instanceof+/+is+ and with fewer chances of misuse than +switch+. + +As an example, you can check whether boolean +b+ is true or false by using ++if b then ... else ...+ or, equivalently, +match b with \{true\} -> ... | \{false\} -> ...+. +======================= Distinguishing messages between users ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
67 doc/book/hello_web_services/hello_wiki_rest.opa
128 doc/book/hello_web_services/hello_wiki_rest_client.opa
 @@ -1,83 +1,93 @@ -import stdlib.web.template +import stdlib.tools.markdown -uri_for_topic(topic) = Uri.of_absolute({Uri.default_absolute with schema = {some = "http"} : option(string) - domain = "localhost" - port = {some = 8080} : option(int) - path = ["_rest_", topic]}) +uri_for_topic(topic) = + Uri.of_absolute( + {Uri.default_absolute with + schema = {some = "http"} : option(string) + domain = "localhost" + port = {some = 8080} : option(int) + path = ["_rest_", topic] + } + ) + +@publish load_source(topic) = + match WebClient.Get.try_get(uri_for_topic(topic)) with + | {failure = _} -> "Error, could not connect" + | {~success} -> + match WebClient.Result.get_class(success) with + | {success} -> success.content + | _ -> "Error {success.code}" + end -@publish load_source(topic) = match WebClient.Get.try_get(uri_for_topic(topic)) with - | {failure = _} -> "Error, could not connect" - | {~success} -> match WebClient.Result.get_class(success) with - | {success} -> success.content - | _ -> "Error {success.code}" - end @publish load_rendered(topic) = - source = load_source(topic) - match Template.try_parse( Template.default, source) with - | {failure = _} -> <>{source} - | ~{success}-> Template.to_xhtml(Template.default, success) + source = load_source(topic) + Markdown.xhtml_of_string(Markdown.default_options, source) @publish save_source(topic, source) = - match Template.try_parse(Template.default, source) with - | ~{success} -> match WebClient.Post.try_post(uri_for_topic(topic), source) with - | { failure = _ } -> {failure = "Could not reach distant server"} - | { success = s } -> match WebClient.Result.get_class(s) with - | {success} -> {success = Template.to_xhtml(Template.default, success)} - | _ -> {failure = "Error {s.code}"} - end - end - | {failure = _} -> {failure = "Incorrect syntax"} + match WebClient.Post.try_post(uri_for_topic(topic), source) with + | { failure = _ } -> + {failure = "Could not reach the distant server"} + | { success = s } -> + match WebClient.Result.get_class(s) with + | {success} -> {success = load_rendered(topic)} + | _ -> {failure = "Error {s.code}"} + end remove_topic(topic) = - _ = WebClient.Delete.try_delete(uri_for_topic(topic)) - void + _ = WebClient.Delete.try_delete(uri_for_topic(topic)) + void edit(topic) = - do Dom.transform([#show_messages <- <>]) - do Dom.set_value(#edit_content, load_source(topic)) - do Dom.hide(#show_content) - do Dom.show(#edit_content) - do Dom.give_focus(#edit_content) - void + do Dom.transform([#show_messages <- <>]) + do Dom.set_value(#edit_content, load_source(topic)) + do Dom.hide(#show_content) + do Dom.show(#edit_content) + do Dom.give_focus(#edit_content) + void save(topic) = - match save_source(topic, Dom.get_value(#edit_content)) with - | { ~success } -> - do Dom.transform([#show_content <- success]); - do Dom.hide(#edit_content); - do Dom.show(#show_content); - void - | {~failure} -> - do Dom.transform([#show_messages <- <>{failure}]) - void + match save_source(topic, Dom.get_value(#edit_content)) with + | { ~success } -> + do Dom.transform([#show_content <- success]); + do Dom.hide(#edit_content); + do Dom.show(#show_content); + void + | {~failure} -> + do Dom.transform([#show_messages <- <>{failure}]) + void display(topic) = - Resource.styled_page("About {topic}", ["/resources/css.css"], -
-
-
158 doc/book/hello_web_services/hello_wiki_rest_client_customizable.opa
 @@ -1,108 +1,106 @@ -import stdlib.web.template +import stdlib.tools.markdown uri_for_topic = - domain_parser = {CommandLine.default_parser with - names = ["--wiki-server-domain"] - description = "The REST server for this wiki. By default, localhost." - on_param(x) = parser y=Rule.consume -> {no_params = {x with domain = y}} + domain_parser = + {CommandLine.default_parser with + names = ["--wiki-server-domain"] + description = "The REST server for this wiki. By default, localhost." + on_param(x) = parser y=Rule.consume -> {no_params = {x with domain = y}} } - port_parser = {CommandLine.default_parser with - names = ["--wiki-server-port"] - description = "The server port of the REST server for this wiki. By default, 8080." - on_param(x) = parser y=Rule.natural -> {no_params = {x with port = {some = y}}} + port_parser = + {CommandLine.default_parser with + names = ["--wiki-server-port"] + description = "The server port of the REST server for this wiki. By default, 8080." + on_param(x) = parser y=Rule.natural -> {no_params = {x with port = {some = y}}} } - base_uri = CommandLine.filter({title = "Wiki arguments" - init = {Uri.default_absolute with domain = "localhost" - schema = {some = "http"}} - parsers = [domain_parser, port_parser] - anonymous = [] - }) - topic -> Uri.of_absolute({base_uri with path = ["_rest_", topic]}) + base_uri = + CommandLine.filter( + {title = "Wiki arguments" + init = {Uri.default_absolute with domain = "localhost" schema = {some = "http"}} + parsers = [domain_parser, port_parser] + anonymous = [] + } + ) + topic -> Uri.of_absolute({base_uri with path = ["_rest_", topic]}) -@publish load_source(topic) = - match WebClient.Get.try_get(uri_for_topic(topic)) with - | {failure = _} -> "Error, could not connect" - | {~success} -> match WebClient.Result.get_class(success) with - | {success} -> success.content - | _ -> "Error {success.code}" - end +@publish load_source(topic) = + match WebClient.Get.try_get(uri_for_topic(topic)) with + | {failure = _} -> "Error, could not connect" + | {~success} -> + match WebClient.Result.get_class(success) with + | {success} -> success.content + | _ -> "Error {success.code}" + end @publish load_rendered(topic) = - source = load_source(topic) - match Template.try_parse( Template.default, source) with - | {failure = _} -> <>{source} - | ~{success}-> Template.to_xhtml(Template.default, success) + source = load_source(topic) + Markdown.xhtml_of_string(Markdown.default_options, source) @publish save_source(topic, source) = - match Template.try_parse(Template.default, source) with - | ~{success} -> match WebClient.Post.try_post(uri_for_topic(topic), source) with - | { failure = _ } -> {failure = "Could not reach distant server"} - | { success = s } -> match WebClient.Result.get_class(s) with - | {success} -> {success = Template.to_xhtml(Template.default, success)} - | _ -> {failure = "Error {s.code}"} - end - end - | {failure = _} -> {failure = "Incorrect syntax"} + match WebClient.Post.try_post(uri_for_topic(topic), source) with + | { failure = _ } -> + {failure = "Could not reach the distant server"} + | { success = s } -> + match WebClient.Result.get_class(s) with + | {success} -> {success = load_rendered(topic)} + | _ -> {failure = "Error {s.code}"} + end remove_topic(topic) = - _ = WebClient.Delete.try_delete(uri_for_topic(topic)) - void + _ = WebClient.Delete.try_delete(uri_for_topic(topic)) + void edit(topic) = - do Dom.transform([#show_messages <- <>]) - do Dom.set_value(#edit_content, load_source(topic)) - do Dom.hide(#show_content) - do Dom.show(#edit_content) - do Dom.give_focus(#edit_content) - void + do Dom.transform([#show_messages <- <>]) + do Dom.set_value(#edit_content, load_source(topic)) + do Dom.hide(#show_content) + do Dom.show(#edit_content) + do Dom.give_focus(#edit_content) + void save(topic) = - match save_source(topic, Dom.get_value(#edit_content)) with - | { ~success } -> - do Dom.transform([#show_content <- success]); - do Dom.hide(#edit_content); - do Dom.show(#show_content); - void - | {~failure} -> - do Dom.transform([#show_messages <- <>{failure}]) - void + match save_source(topic, Dom.get_value(#edit_content)) with + | { ~success } -> + do Dom.transform([#show_content <- success]); + do Dom.hide(#edit_content); + do Dom.show(#show_content); + void + | {~failure} -> + do Dom.transform([#show_messages <- <>{failure}]) + void display(topic) = - Resource.styled_page("About {topic}", ["/resources/css.css"], -
-
-
: {source} - ) -) + do /wiki[topic] <- source + load_rendered(topic) ----------------- This function takes two arguments: a +topic+, with the same meaning as above, -and a +source+, i.e. a +string+ meant to be understood by our templating engine. -The call to function +Template.try_parse+ submits this +source+ to the default -templating engine, which may either accept it (if the syntax was correct) or -reject it (otherwise). For this wiki, we do not try to fix the syntax -automatically or to fallback to an approximate syntax. - -Function +Template.try_parse+ can produce two kinds of results: _success_ results and -_failure_ results. More precisely, by definition, the value returned by this function -is always either a record +\{ success = something \}+ or +\{ failure = something\}+. We -call this a _sum_ of records, and it has type +\{ success = ... \} /\{ failure = ...\}+ -- -we will fill in the dots later. - -[TIP] -.About sum types -======================== -A value has a _sum type_ +t/u+ if, depending on the execution path, it can -have either values of type +t+ or values of type +u+ . A good example of sum type -is booleans, which are defined as +\{false\}/\{true\}+ . Another good example of -sum type is the type +list+ of linked lists, whose definition can be summed up -as +\{nil\} / \{hd: ...; tl: list\}+. - -Note that sum types are not limited to two cases. Advanced applications commonly -manipulate sum types with ten cases or more. -======================= - -As you can see, we apply a +match ... with ...+ operation to this result. This -operation is known as _pattern-matching_, and it lets us determine safely whether -the result was a _success_ or a _failure_. - -[TIP] -.About pattern-matching -======================== -The operation used to branch according to the case of a sum type -is called _pattern-matching_. A good example of pattern-matching -is +if ... then ... else ...+ . The more general syntax for pattern matching is -+match ... with | case_1 \-> ... | case_2 \-> ... | case_3 \-> ...+ - -The operation is actually more powerful than just determining which case of a -sum type is used. Indeed, if we use the vocabulary of languages such as Java or -C#, pattern-matching combines features of +if+, +switch+, +instanceof+/+is+, -multiple assignment and dereferenciation, but without the possible safety issues -of +instanceof+/+is+ and with fewer chances of misuse than +switch+. -======================= - -This pattern-matching has two cases. Let us concentrate on the first one: - -[source,opa] ------------------ - | {success = x} -> - ( - do /wiki[topic] <- x - Template.to_xhtml(Template.default, x) - ) ------------------ - -The items between the pipe (+|+) and the arrow (+\->+) are known as the -_pattern_. The corresponding case is executed if the _pattern_ _matches_ the -value. Here, we have a pattern that accepts all records containing exactly one -field called +success+. When our pattern-matching encounters such a record, it calls -+x+ the contents of field and executes everything that appears after the arrow -(the _body_). - -The first instruction writes +x+ to the database at path +/wiki[topic]+. -The second instruction produces a +xhtml+-formatted version of +x+, which -is the result of our function. - -Let us now examine the second case of our pattern-matching: -[source,opa] ------------------ - | {failure = _} -> - ( - do /wiki[topic] <- Template.text(source) - <>Syntax error
: {source} - ) ------------------ - -By definition, cases are executed in order. Here, the pattern matches all -records containing exactly one field called +failure+. When our pattern-matching -encounters such a record, it ignores the contents of the field -- recall that -+_+ is pronounced "I don't care" -- and executes the body. - -Here, we are in the error case, i.e. the user has entered a syntactically -incorrect text. We could decide to perform sophisticated error reporting, but -that goes beyond the scope of this chapter. We will rather employ an alternative -strategy: we store the text entered by the user, but as source code, -so that it can be modified at a later stage. For this purpose, we again call -function +Template.text+ to adopt the source code as immediate text, -we write this to the database at path +/wiki[topic]+ and then we produce -a message stating that there was a syntax error. - -With this, we have covered everything related to database, parsing or rendering. -We can now move to the user interface. +and a +source+, i.e. a +string+, which is a representation of the page with Markdown syntax. +The instruction after +do+ writes +source+ to the database at path +/wiki[topic]+. User interface ~~~~~~~~~~~~~~ @@ -407,20 +308,6 @@ This is a total of 30 effective lines of code + CSS. Questions ~~~~~~~~~ -What about the wiki syntax? -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For this tutorial chapter, we have used the default syntax provided by +Template+, which -we have not detailed. That is because you already know this syntax: +Template+ uses a subset -of the syntax of HTML, without JavaScript. As mentioned earlier, this syntax can be extended. -The general idea is to plug new _engines_ into +Template+ which can interpret new namespaces. -We will discuss the specifics in another chapter, or you can already look at the documentation -of module +Template+. - -Opa also features a powerful mechanism for parsing simple or complex syntaxes, -which you can easily use to replace the default syntax of +Template+ with -something closer to the usual Wiki syntax. We will introduce this mechanism -in another chapter. - What about user security? ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -435,8 +322,8 @@ stealing identities. You may attempt to reproduce this with the wiki, the chat, or any other Opa application. This will fail. Indeed, while lower-level web technologies make no difference between JavaScript code, text, or structured data, Opa does, and -ensures that data that has been provided as the one can never be interpreted -as one of the others. +ensures that data that has been provided as one can never be interpreted +as the other one. [CAUTION] .Careful with the +
49 doc/book/hello_wiki/hello_wiki.opa
51 doc/book/hello_wiki/hello_wiki_simple.opa