[added tutorial stefan@aeschbacher.ch**20060422162940] { adddir ./doc addfile ./doc/tutorial.html hunk ./doc/tutorial.html 1 + + + +
++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. +
+ ++The application model in HAppS is to help separate state, application +logic, wire formats, protocols, and presentation layer: +
+ ++ State is just a haskell data type you define. ACID [2] Consistency + enforced by Haskell's type system. ACID Durability is handled by + MACID write-ahead logging and checkpointing. +
++ Incoming events are gathered in individual haskell threads and + then pushed onto a single application queue for processing. The + queue model gives you ACID Atomicity and Isolation and lets your + app be simply a set of functions with types like: +
+ ++ SomeInputType -> MACID SomeOutputType ++ +
+ The MACID monad lets you update your state and *schedule* + side-effects. To be clear, MACID is not in the IO monad so you + cannot execute side effects, you can only schedule them. The + framework takes care of making sure they are executed + at-least-once (if they can be completed by a deadline you + specify). +
++ Since your app consists of a set of functions with various haskell + input and output types, somewhere you need a place to convert + between those internal haskell types and external protocol event + types; e.g. from URL Encoded HTTP requests to SomeInputType and + from SomeOutputType to XML encoded HTTP responses. +
++ HAppS currently provides support for HTTP Requests/Responses and + SMTP Envelopes. To be clear HAppS provides ACID Atomicity at the + protocol event level. So if you write a protocol with SMTP + envelopes being the arriving event type then your app will have + atomicity in processing incoming SMTP envelopes. If you write a + protocol with SMTP commands being the arriving event type, then + your app will have atomicity at the level of individual smtp + commands. +
+ ++ If your application outputs XML as its wire format, HAppS provides + a lot of support for using XSLT to transform it for presentation + purposes. For example, you can send XML mail and HAppS will take + care of applying the relevant XSLT stylesheet before it is + delivered. If you output XML HTTP responses, HAppS takes care of + applying the XSLT stylesheet server side for user-agents that + don't support doing so on the client. The value here is that you + can have designer types who know XSLT modify presentation stuff + without touching your application code. +
+ ++This chapter will run you through some first simple programs written in +HAppS. For other programs have a look at the directory named 'examples'. +
+ ++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. +
+ ++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. +
+ ++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 + +