[added tutorial stefan@aeschbacher.ch**20060422162940] { adddir ./doc addfile ./doc/tutorial.html hunk ./doc/tutorial.html 1 + + + + +HAppS Tutorial + + +

HAppS Tutorial

+

Installing

+ +

+To install HAppS you need the following modules: +

+ + + +

+All modules except fps and HaXml are included with ghc and hugs. +

+ +

+To install do the following: +

+ +
+runhaskell Setup.hs configure
+runhaskell Setup.hs build
+
+ +

+and then as root: +

+ +
+runhaskell Setup.hs install
+
+ +

+On Mac OS X one might need to compile Setup.hs first due to a bug +in GHC/Cabal. "ghc --make Setup.hs -o setup" and use "./setup" +instead "runhaskell Setup.hs". (as of 2006-04) +

+ +

+As an alternative you could consider using search path. For details +how to use see [4]. +

+ +

+If you want to use XSLT on the server side, you have to install +libxslt [8] which contains the xsltproc program. This is used to +apply your xslt stylesheets to the xml files. +

+ +

Overview

+ +

+The application model in HAppS is to help separate state, application +logic, wire formats, protocols, and presentation layer: +

+ + + +

First Steps

+

+This chapter will run you through some first simple programs written in +HAppS. For other programs have a look at the directory named 'examples'. +

+ +

Hello World

+

+As usual with some programming languages (not Haskell) we will begin with a +'hello world' program. +

+ +

+Copy the following code into a file hello_world.hs: +

+ +
+module Main where
+
+import HAppS
+import Control.Monad.State
+
+data MyState = MyState String deriving (Read, Show)
+
+instance StartState MyState where
+    startStateM = return $ MyState "Hello World!"
+instance Serialize MyState where
+    typeString _ = "MyState"
+    encodeStringM = defaultEncodeStringM
+    decodeStringM = defaultDecodeStringM
+
+app :: Method -> Host -> [String] -> Ev MyState Request Result
+app _ _ _ = do
+	MyState message <- get
+	sresult 200 message
+
+main = stdMain $ simpleHTTP "" [] app :*: End
+
+ +

+Now run the code. With ghc you can do: +

+ +
+ghc --make -fallow-overlapping-instances -o hello_world hello_world.hs
+./hello_world
+
+ +

+If all goes well you will see your first HAppS application starting. Point +your browser at http://localhost:8000/ and you should see the line +"Hello World!". +

+ +

+Stop the application by typing 'e' and hitting enter. You will notice +that a new directory with the name 'hello_world_state' has been created. It +contains the data needed by your application to run (or to continue it's +operation when it's started anew, but more of this later). +

+ +

+Let's observe the simple program we used to generate the "Hello World!" line: +

+ +
+module Main where
+
+import HAppS
+import Control.Monad.State
+
+ +

+The first three lines are the usual definition of the Main module and the +needed imports. HAppS gives us access to the most common functions. The State +Monad [7] is used for the state keeping (in this example it is barely used). +

+ +
data MyState = MyState String deriving (Read, Show)
+ +

+With this line the state for this program is defined. Currently it is very +simple and contains only a single string. The state derives Read and Show to +simplify the serialization of it. +

+ +
+instance StartState MyState where
+    startStateM = return $ MyState "Hello World!"
+
+ +

+To initialize a HAppS application, a start state has to be defined. By deriving +from the StartState class this can easily be done. Here we set the content of +our state to "Hello World!". This content will be read by our application quite +soon. +

+ +
+instance Serialize MyState where
+    typeString _ = "MyState"
+    encodeStringM = defaultEncodeStringM
+    decodeStringM = defaultDecodeStringM
+
+ +

+To be able to save the application state to a file, the user defined state has +to be able to be serialized. This is achieved by deriving from the Serialize +class. The given code shows an easy way how to do this when the state derives +Read and Show. +

+ +
app :: Method -> Host -> [String] -> Ev MyState Request Result
+ +

+The app function contains the application. Currently we ignore all the +parameters. We will have a look at them a little later. As you can see, the +application runs in the Ev Monad. This Monad guarantees the ACID properties +you might already know from your favourite database system. For more information +on what this means read [1] and [2]. +

+ +
+app _ _ _ = do
+    MyState message <- get
+    sresult 200 message
+
+ +

+The actual application is rather simple. The get function (from the State Monad) +is used to access the actual content of the state. +With the sresult function, a return code 200 (OK, see [3]) and the message +itself is returned. +

+ +
main = stdMain $ simpleHTTP "" [] app :*: End
+ +

+To run the entire application this main function is provided. simpleHTTP is +called with the application app as a parameter. Ignore the rest of the line for +the moment. +

+ +

Adding dynamic behaviour

+

+Until now, we printed always the same string. For some applications this could +be useful but for the vast majority of applications a little more dynamic behaviour +would help. +

+ +

+In the following example, the state can be modified through a HTML-Form. As in the +example before, copy the following program in a file (e.g. change_state.hs), compile +it and run it. +

+ +
+module Main where
+
+import HAppS
+import Text.Html
+import Control.Monad.State
+
+data MyState = MyState String deriving (Read,Show)
+
+instance StartState MyState where
+    startStateM = return $ MyState "Hello World!"
+instance Serialize MyState where
+    typeString _ = "MyState"
+    encodeStringM = defaultEncodeStringM
+    decodeStringM = defaultDecodeStringM
+
+app :: Method -> Host -> [String] -> Ev MyState Request Result
+app GET _ ["change"] = do
+    sresult 200 $ renderHtml page
+    where 
+        page = header 
+            << thetitle << "Change State"
+            +++ h1 << "Change State"
+            +++ gui "/change" 
+            << ((textfield "state") +++ submit "change" "change")
+app POST _ ["change"] = do
+    rq <- getEvent
+    put $ MyState (lookS 100 rq "state")
+    redirect "/"
+app _ _ _ = do
+    MyState message <- get
+    sresult 200 message
+
+main = stdMain $ simpleHTTP "" [] app :*: End
+
+ +

+When you point your browser at http://localhost:8000/ you will see the same as +before. 'Hello World! on an otherwise empty page. You can now point your +browser at http://localhost:8000/change and enter a new String which will +be stored in the state. +

+ +

+Let's have a look at what happens in this program: +

+ +

+The first few lines are almost identical to the first program. The only change +is the fact, that the module Text.Html [6] is imported. This is used for the +generation of the HTML form later. +

+ +
app :: Method -> Host -> [String] -> Ev MyState Request Result
+ +

+The type definition stayed also the same but we will now use some of the +parameters. The first parameter identifies the HTTP-Method [5] that has +been called. The third parameter contains the path components. The second +parameter contains information about the host that issued the request but +this parameter will not be used in this example. +

+ +
+app GET _ ["change"] = do
+    sresult 200 $ renderHtml page
+    where 
+        page = header 
+            << thetitle << "Change State"
+            +++ h1 << "Change State"
+            +++ gui "/change" 
+            << ((textfield "state") +++ submit "change" "change")
+
+ +

+The first partial definition of the application handles the case where a +HTTP-Get request arrives. The path component we are interested in is the +string "change". This definition will be executed when someone wants to +see the page located at http://localhost:8000/changes. (For the URL +http://localhost:8000/foo/bar/baz, the component list would look +["foo", "bar", "baz"]) +

+ +

+The HTML code generate by the 'page' function - which defines a simple +HTML-page that contains a title and a form - is rendered to a String +with the renderHtml function (all from the Text.Html module). +

+ +
+app POST _ ["change"] = do
+    rq <- getEvent
+    put $ MyState (lookS 100 rq "state")
+    redirect "/"
+
+ +

+This part of the application handles the POST-request that is issued +by the web browser when the user submits the form. +

+ +

+The getEvent function allows to get the request information out of the +Ev monad. It contains all the information about the request like +the complete query string, browser information, submitted variables and +more. +

+ +

+To access the variables stored in the request, the function lookS is +used. It takes three parameters, a number that indicates the maximum +length of the string to read, the request and the name of the +variable to read. If the string in the variable is longer than the +given length, it is truncated. +

+ +

+The 'put' function is the counterpart of the get function we already +used. We use it to write back the new state. +

+ +

+After the state is written back (so that others can now access it), +the web browser is redirected to the root path "/" of this application. +This is done by sending the code 302 (undefined redirect [3]) to the +browser. +

+ +
+app _ _ _ = do
+    MyState message <- get
+    sresult 200 message
+
+ +

+This last part handles the printing of the state for all the queries +that do not satisfy one of the patterns used before. +

+ +

+The last line is the same as in the first example. +

+ +

Adding Side Effects

+

+Doing stuff that changes the world around the program (like printing a string +or writing something into a database) is something that crosses your way +sooner or later. +As you probably know, such operations are said to produce side effects and +have to be treated specially in a pure functional language. +

+ +

+Side effects can easily be added to a HAppS program with the function +'addSideEffect'. It takes a time in seconds and an IO function as parameters. +The time in seconds is a timeout. If the computation takes longer than the +given time in seconds, it is aborted. +

+

+A simple example can be seen here: +

+ +To be Continued + +

References

+[1] The HAppS Readme
+[2] Wikipedia on ACID
+[3] HTTP Status Codes
+[4] Searchpath
+[5] HTTP Methods
+[6] Text.Html
+[7] The State Monad
+[8] libxslt
+ + }