Posts

Showing posts from February, 2008

How I Learned to Stop Worrying And Love Haskell's Type Inference

In developing the HAppS example that I have been posting here, I came upon a problem that gave me new insight to Haskell. I don't think it was a particularly deep insight, but it is significant to me as someone new to the language, and new to type inference. Consider a user authentication function that retrieves a map from the reader monad, looks up a username, and compares the password retrieved with a passed in parameter. I first implemented this function something like this: data User = User { username :: String, password :: String } authUser name pass = do u <- liftM (M.lookup name) ask liftM2 (==) (liftM password u) (return pass) In the process of getting there, I stumbled around rearranging and inserting various liftM calls until I finally got it to work. Many of the reasons behind the type errors still seemed like voodoo to me. And my haphazard approach to fixing them is evident looking at the code. The problem w

Transactional Integrity Problem

An astute reader pointed out that there is a transactional integrity problem with the HAppS application built over the last 4 posts. The function checkAndAdd in Finished HAppS Application contains a call to "query $ IsUser" as well as a call to "update $ AddUser". This violates that ACID guarantee that was desired from the checkAndAdd function. If two people simultaneously try to create the same username, it's possible that both of them could get past the "query" and "if exists" statements before either of the "update AddUser" statements are executed. In this case, both of the AddUser updates would succeed and both users would think their account was created. But if they had the same username, then first one would be overwritten by the second one. The second user wouldn't notice a problem, but the first user would not be able to log in to the newly created account because his password would probably be different from the

Finished HAppS Application

Update: Due to popular demand, I put the plain haskell code for this app on hpaste.org. Here are links for Session.hs , Main.hs , and login.html . The past three posts have laid the groundwork for a full HAppS web app. We have built the infrastructure for an authentication system that can store usernames and passwords as well as the session IDs for active sessions. Here we will tie everything together to make a functional web app. It should all compile with HAppS 0.9.2. Let's get the imports out of the way. > {-# OPTIONS -fglasgow-exts #-} > {-# LANGUAGE TemplateHaskell , FlexibleInstances, > UndecidableInstances, OverlappingInstances, > MultiParamTypeClasses, GeneralizedNewtypeDeriving #-} > > module Main where > > import Control.Concurrent > import Control.Monad > import HAppS.Server > import HAppS.State > > import Session --The session and state code already developed In the first post, we created code to

Using HAppS-State

Update: A demo of the finished application is now available. See this post for more information. In the last post, I outlined the requirements for making a data type an instance of Component. This is great, but not very useful without a mechanism for accessing the state data. HAppS persists its state by storing functions that operate on the state. This requires a way to serialize the functions. HAppS does this for you with the TemplateHaskell function mkMethods. So how does this affect you? Your functions that manipulate state must be either Update or Query functions. Update functions use the State monad, and Query functions use the Reader monad. First we'll set up some convenience functions that will be used to construct the actual Query and Update functions. > askUsers :: MonadReader State m => m (M.Map String User) > askUsers = return . users =<< ask > > askSessions::MonadReader State m => m (Sessions SessionData) > askSessions = return

Intro to HAppS-State

Update: A demo of the finished application is now available. See this post for more information. This post is written in literate haskell, so it should compile as-is in a .lhs file. In my last article on HAppS, I gave a brief introduction to working with the HAppS web server to serve a basic user login/registration page and handle the form submission. In this article we are going to develop the framework for basic session management. The example file AllIn.hs in the HAppS source tree is very similar to this. I have made a few changes to demonstrate some different cases that one might encounter. So without further ado, we'll start with our standard import statements. > {-# OPTIONS -fglasgow-exts #-} > {-# LANGUAGE TemplateHaskell , FlexibleInstances, > UndecidableInstances, OverlappingInstances, > MultiParamTypeClasses, GeneralizedNewtypeDeriving #-} > > module Session where > > import qualified Data.Map as M > impor

Intro to HAppS Part 1

Update: A demo of the finished application is now available. See this post for more information. This post is the first in a series outlining the process I am taking to get a basic stateful web application working with the Haskell web framework HAppS. There is very little documentation available for versions of HAppS after 0.8.8. While this is not documentation, it should aid in getting an idea of how HAppS manages state. The code in these posts should work with HAppS 0.9.2. For this application, we'll focus on the basic capabilities needed for user creation, authentication, and session management. The first thing we need is a form for logging in or creating a new user, and two URLs to handle the submission of these forms. Well use /login for the form, and we'll have the login form POST to the same place. The registration form will POST to /newuser. All other pages will return an error. Here is the basic HAppS code to do that: impl = [ dir "login" [me