You can certainly achieve this, and get very strong static guarantees about the separation of the components.
I am picturing a higher-level monad that would be limited to direct URL handlers and would be able to compose calls to the DB monad and the IO monad. You can certainly achieve this, and get very strong static guarantees about the separation of the components. At its simplest, you want a restricted IO monad.
Using something like a "tainting" technique, you can create a set of IO operations lifted into a simple wrapper, then use the module system to hide the underlying constructors for the types. In this way you'll only be able to run CGI code in a CGI context, and DB code in a DB context. There are many examples on Hackage.
Another way is to construct an interpreter for the actions, and then use data constructors to describe each primitive operation you wish. The operations should still form a monad, and you can use do-notation, but you'll instead be building a data structure that describes the actions to run, which you then execute in a controlled way via an interpreter. This gives you perhaps more introspection than you need in typical cases, but the approach does give you full power to insspect user code before you execute it.
Thanks, Don! The former solution sounds like what I'm looking for. Do you know any specific packages that use this technique, or good terms to google for ("restricted IO monad" didn't turn up much)?
– Bill Mar 5 '10 at 13:19 1 A good example of the 'taint monad' concept, blog.sigfpe. Com/2007/04/trivial-monad. Html – Don Stewart Mar 5 '10 at 17:01 Thanks.
If I choose to use the "tainted monad" pattern for my DB monad, what do I do to extract the data from the DB monad? Does my HTTP action handler have to use a monad transformer with DB in it? – Bill Mar 6 '10 at 0:08 Most monads come with a "run" command.
The usual pattern is that monad type Foo has "runFoo :: Foo a -> a". On the other hand you may want to restrict the destination of your output, so you might have "runFoo :: Foo a -> Bar a" – Paul Johnson Mar 6 '10 at 9:28 1 Also have a look at "Monads a la carte". (I think that's the title...) – Porges 07/040 at 2:55.
Thanks for this question! I did some work on a client/server web framework that used monads to distinguish between different exection environments. The obvious ones were client-side and server-side, but it also allowed you to write both-side code (which could run on both client and server, because it didn't contain any special features) and also asynchronous client-side which was used for writing non-blocking code on the client (essentially a continuation monad on the client-side).
This sounds quite related to your idea of distinguishing between CGI code and DB code. Here are some resources about my project: Slides from a presentation that I did about the project Draft paper that I wrote with Don Syme And I also wrote my Bachelor thesis on this subject (which is quite long though) I think this is an interesting approach and it can give you interesting guarantees about the code. There are some tricky questions though.
If you have a server-side function that takes an int and returns int, then what should be the type of this function? In my project, I used int -> int server (but it may be also possible to use server (int -> int). If you have a couple of functions like this, then it isn't as straightforward to compose them.
Instead of writing goo (foo (bar 1)), you need to write the following code: do be.
This only popped up in my RSS reader because the question was updated, but if I have an answer I suppose I might as well post it. I think there's a third way beyond the two Don Stewart mentioned, which may even be simpler: class Monad m => MonadDB m where someDBop1 :: String -> m () someDBop2 :: String -> m String class Monad m => MonadCGI m where someCGIop1 :: ... someCGIop2 :: ... functionWithOnlyDBEffects :: MonadDB m => Foo -> Bar -> m () functionWithOnlyDBEffects = ... functionWithDBandCGIEffects :: (MonadDB m, MonadCGI m) => Baz -> Quux -> m () functionWithDBandCGIEffects = ... instance MonadDB IO where someDBop1 = ... someDBop2 = ... instance MonadCGI IO where someCGIop1 = ... someCGIop2 = ... The idea is very simply that you define type classes for the various subsets of operations you want to separate out, and then parametrize your functions using them. Even if the only concrete monad you ever make an instance of the classes is IO, the functions parametrized on any MonadDB will still only be allowed to use MonadDB operations (and ones built from them), so you achieve the desired result.
And in a "can do anything" function in the IO monad, you can use MonadDB and MonadCGI operations seamlessly, because IO is an instance. (Of course, you can define other instances if you want to. Ones to lift the operations through various monad transformers would be straightforward, and I think there's actually nothing stopping you from writing instances for the "wrapper" and "interpreter" monads Don Stewart mentions, thereby combining the approaches - although I'm not sure if there's a reason you would want to.).
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.