--- Log opened Tue Mar 03 00:00:29 2009
03:27 < solidsnack> mae: hi
05:48 < mae> website is updated: http://happstack.com/docs/0.2/index.html
05:48 < mae> thanks saizan :)
05:53 < mae> bbl.
05:55 < vegai> smoking.
07:09 < koeien> mae: the font is extremely ugly here
09:51 < frevidar> I've just been browsing through the happstack tutorial...
09:51 < frevidar> presumably there's a function somewhere that takes an old state and produces a new state?
09:52 < frevidar> I just can't seem to find where that is?
09:59 < stepcut> frevidar: http://nhlab.blogspot.com/2008/12/data-migration-with-happs-data.html
10:00 < frevidar> stepcut: I'm more talking about just to go from state A to state B
10:00 < frevidar> not changing the format of the data
10:00 < frevidar> stepcut: I'm now looking at this: http://tutorial.happstack.com/tutorial/macid-updates-and-queries
10:00 < frevidar> is that on the right track?
10:01 < stepcut> frevidar: oh, yes that is on the right track.
10:02 < frevidar> stepcut: there's not a simpler tutorial is there?
10:02 < frevidar> stepcut: like, one number as state, and says "hello world, the current state is 15" or something?
10:02 < frevidar> just very simple read and write?
10:03 < stepcut> frevidar: you could try this one, http://nhlab.blogspot.com/2008/07/extending-asterisk-with-happs.html
10:03 < stepcut> frevidar: it just has a hit counter that gets incremented.
10:05 < stepcut> frevidar: also, if you are using happstack from darcs, there is a working project in happstack/happstack/template/project
10:05 < stepcut> frevidar: it simple guestbook application, it only has one query function and one update function
10:09  * stepcut thinks that turning ADTs into JSON objects is ugly business
10:13 < wchogg> frevidar : you found the bit on macid on the happstack tutorial a bit confusing?
10:14 < frevidar> wchogg: I'm just having a read through it at the moment
10:14 < frevidar> wchogg: I'm finding the whole thing a bit confusing
10:14 < wchogg> frevidar : any suggestions on what might help?
10:14 < frevidar> wchogg: it seems more complicated than what I've conceptualised in my head
10:15 < frevidar> wchogg: more of a big picture
10:15 < frevidar> wchogg: and a much simpler example
10:15 < frevidar> wchogg: something that fits into one source file
10:15 < frevidar> wchogg: 20-30 lines preferably
10:15 < wchogg> frevidar : Okay.
10:16 < frevidar> wchogg: that does the important bits, reads and writes
10:16 < wchogg> frevidar:  Thanks.  I'll try and come up with a nicely annotated example for this chapter.
10:16 < frevidar> I notice you're releasing on square root day
10:16 < frevidar> nice
10:17 < frevidar> wchogg: I'm interested in using happstack to develop an online election system
10:17 < frevidar> wchogg: which is not trivial, but not huge
10:17 < frevidar> wchogg: I guess initially, I'll be expecting around 50000 or so users
10:18 < frevidar> wchogg: for universities
10:18 < frevidar> wchogg: (although, turn out rates are of the order of 5%)
10:18 < frevidar> wchogg: so considering people only vote once, I think its something it can handle
10:19 < wchogg> Sounds like a neat project.
10:21 < frevidar> wchogg: how does the transaction log work?
10:21 < frevidar> wchogg: from what little I understand of how this works...
10:23 < frevidar> wchogg: lets say I have a state consisting of large binary tree, and I change it by deleting one of the leafs, does the transaction log just have a leaf deletion?
10:24 < stepcut> frevidar: the transaction log stores the Event that caused the deletion. But not that actually data type that had the leaf deleted.
10:24 < stepcut> frevidar: because all the update events are pure, if the server restarts, it can reconstruct the previous state by simply replaying the events.
10:25 < frevidar> stepcut: ah, I see now
10:25 < stepcut> frevidar: when you call, $(mkMethods ''SomeType ['method1, 'method2]), you are turning the functions method1 and method2 in events
10:26 < frevidar> stepcut: and thats template haskell doing some fancy stuff there?
10:26 < stepcut> basically, it just creates the data types, Method1 and Method2
10:26 < frevidar> stepcut: which presumably readable and showable?
10:27 < frevidar> as a sidenote, this would be good to see in the tutorial
10:27 < stepcut> if you had, deleteLeaf :: NodeNum -> Update Tree (), it would  get turned into, data DeleteLeaf = DeleteLeaf NodeNum, and that is what would be stored in the transaction log
10:27 < frevidar> this discussion, roughly
10:28 < frevidar> well, deleteLeaf would be something like deleteLeaf :: NodeNum -> Tree -> Tree yes?
10:29 < stepcut> frevidar: yes, it is readable/showable, though it actually uses the Serialize class which is similar to read/show, but uses a compact binary format and supports migration when you change the structure of the types
10:30 < stepcut> frevidar: well, there are probably two functions like deleteLeaf. There is a non-happstack specific function, deleteLeaf :: NodeNum -> Tree -> Tree, that deletes an leaf from a Tree
10:30 < frevidar> stepcut: ok, and a wrapper of some sort?
10:31 < stepcut> frevidar: yes.. though I am not sure I would think of it as a wrapper.
10:31 < stepcut> frevidar: it would look a bit like:
10:33 < stepcut> frevidar: deleteLeafMethod nodeNum = modify (deleteLeaf nodeNum)
10:34 < stepcut> frevidar: in this particular example, it does look a lot like a 'wrapper' but in general the update events can do more complex processing if they need to.
10:34 < stepcut> frevidar: though, in general, you want to minimize the amount of work done in the update event.
10:36 < wchogg> frevidar : Sorry, I was away for a minute.  Do you think it'd be useful to see some expansions of the splices such as $(mkMethods ''Foo ['bar']) in the tutorial?
10:37 < frevidar> wchogg: I think it would be good to get a conceptual plain english outline before delving into the code...
10:37 < stepcut> frevidar: I guess what I am trying to say is that the function you write to update the state is just a normal function, and you can do whatever you want (provided it does not require IO). It just so happens that in this example, all we really need to do is call another function that already does what we want. But, that does not mean you should expect it to always look like that.
10:38 < frevidar> I guess I have this in my head
10:38 < frevidar> lets say I was trying to do what happs was trying to achieve
10:38 < frevidar> and you can tell me if this is roughly what happs is doing
10:40 < frevidar> you'd have a function f :: Request -> Event
10:40 < frevidar> and then g :: Event -> State -> (State, Output)
10:40 < frevidar> Event would be showable and readable
10:41 < frevidar> so then you just have a mainloop that turns every request into an event, and that event is combined with the state to produce a new state and the output the user sees
10:42 < frevidar> the only issue I guess here is if Event doesn't write
10:42 < stepcut> frevidar: if it is a query event, then it just returns the state unmodified
10:43 < frevidar> you could have h :: Event -> State -> Output for read events
10:43 < frevidar> or something
10:43 < frevidar> thats a fudge
10:43 < stepcut> frevidar: at the core, there is a function quite like g
10:43 < frevidar> anyway, forgeting about read events, because they're obviously simpler anyway, you get the idea
10:44 < stepcut> frevidar: there is a event loop function like, eventLoop :: State -> IO ()
10:45 < frevidar> stepcut: probably my knowledge of Haskell monads isn't strong
10:45 < frevidar> stepcut: but I think I get the idea
10:45 < stepcut> at the beginning of the event it reads an event from the eventQueue and executes it to get (State, Output)
10:45 < stepcut> each event is bundled with a callback function, (Output -> IO ()), which the event loop calls to return the Output value
10:45 < frevidar> roughly the monad encapulates the current state?
10:46 < stepcut> and then it calls, eventLoop newState
10:46 < frevidar> without having to pass it around?
10:46 < stepcut> frevidar: which Monad?
10:47 < frevidar> stepcut: the IO, in this case
10:47 < frevidar> I guess I need to play around with monads more, to get things like (Output -> IO ())
10:48 < frevidar> most of the haskell I've written so far is read lines and write lines.
10:48 < stepcut> frevidar: the IO in that case is not related to State. It is just there so that you can use messaging systems which require IO
10:49 < frevidar> so IO () is the external state yes?
10:49 < frevidar> as in, everything outside our nice pure haskell program?
10:50 < stepcut> frevidar: yes
10:51 < frevidar> so shouldn't eventLoop be eventLoop :: Event -> State -> (State, IO ())
10:51 < frevidar> oh, hang on
10:51 < frevidar> eventLoop needs to write to the transaction log
10:51 < frevidar> so it needs to return a new IO ()
10:52 < frevidar> but it also needs to tell us the new state
10:52 < frevidar> and it needs to recieve an event?
10:53 < stepcut> frevidar: yes, it receives the event from an eventQueue
10:54 < frevidar> ...
10:54 < stepcut> this is the actual eventLoop, http://hpaste.org/fastcgi/hpaste.fcgi/view?id=2048#a2048
10:55 < stepcut> IHR context ev fun <- atomically $ readTChan queue, gets the event that needs to be executade
10:56 < frevidar> so you said eventLoop was eventLoop :: State -> IO () ?
10:56 < stepcut> (mst,ra) <- fun context st -- executes the event, and gets back a possibly new state and return value
10:56 < frevidar> stepcut: does State contain the eventQueue?
10:57 < frevidar> as in, does State have a list of events?
10:58 < frevidar> (lazy, of course)
10:58 < frevidar> because otherwise I don't see how eventLoop accesses the next event
10:59 < stepcut> frevidar: did you look at the code on hpaste?
10:59 < frevidar> I'll have more of a look now
10:59 < stepcut> frevidar: I can walk you through the interesting parts real quick
10:59 < stepcut> runTxLoop takes three arguments
11:00 < stepcut> the first argument is the saver that will be used to log the events (usually to disk, but it could be S3, memory, or something else)
11:00 < stepcut> the second argument is the event queue
11:01 < stepcut> the third argument is the initial state
11:02 < stepcut> 'loop' is the actually event processing loop. The only argument it takes is 'st' which is the current state. But, it can still access 'eventSaverVar' and 'queue' because they are in scope.
11:03 < stepcut>  IHR context ev fun <- atomically $ readTChan queue -- this reads the next event to be processed from the event queue
11:03 < frevidar> stepcut: ah, we never really exit runTxLoop
11:03 < stepcut>       (mst,ra) <- fun context st -- this executes the event, getting back a possibly new state and a return value
11:03 < stepcut>  
11:03 < frevidar> stepcut: so all we need to return is the new state of the outside world when it finally does exit
11:05 < stepcut> if the state was not modified, we call 'ra' which sends the results back to the caller, and then we call, loop st, to restart the loop with the same state
11:06 < stepcut> if the state was modified, then we log the event, call ra to emit the results to the caller, and do, loop st', using the modified state st'
11:08 < frevidar> stepcut: but when we call "loop st" or "loop st' " as the case may be, isn't the queue unchanged?
11:09 < frevidar> stepcut: conceptually, if the queue was a list, shouldn't we be like "loop queue st" and feed back in  "loop (tail queue) st' "?
11:09 < frevidar> stepcut: I know it may not be a haskell list, but you know what I mean?
11:09 < stepcut> frevidar: if queue was a list, then you would have to do it like that.
11:09 < frevidar> we should loop queue' st'
11:09 < frevidar> where queue' is the new queue?
11:11 < stepcut> frevidar: if queue was just a list, yes.
11:12 < frevidar> stepcut: whatever the queue is, if you don't modify it, you'll get the same queue item next time won't you?
11:12 < stepcut> frevidar: but queue is actually a STM-based transactional queue that provides a sane way of communicating between threads. So queue is really more like a file handle.
11:13 < stepcut> for the same reason that, hGetLine h, can return a different value even if you pass in the same h, atomically $ readTChan queue, can return a new value each time it is called
11:14 < stepcut> it has the type, atomically $ readTChan queue :: IO (IHR st)
11:15 < stepcut> it's not pure, so it is allow to do that sort of craziness :)
11:15 < frevidar> stepcut: now I need to think about this. Aren't all haskell functions pure?
11:15 < frevidar> stepcut: like I said, I haven't develed into this enough
11:16 < frevidar> *delved
11:16 < frevidar> stepcut: there has to be something different about what we pass to hGetLine each time
11:17 < frevidar> stepcut: for example the handle changing in some way?
11:17 < stepcut> frevidar: IO is a bit tricky. Depending on your view point you can consider it pure or impure.
11:17 < stepcut> frevidar: the thing that is different is the state of the external world
11:17 < stepcut> frevidar: which is implicitly passed around in IO
11:17 < frevidar> stepcut: which has to be somehow fed into hGetLine yes?
11:18 < stepcut> frevidar: conceptually, yes, though implementation wise it is done differently.
11:18 < stepcut> frevidar: an explicit version of IO would look like this:
11:18 < stepcut> hGetLine :: World -> Handle -> (String, World)
11:18 < frevidar> frevidar: ah, thats the funky do transformation yes?
11:19 < stepcut> where World is the state of the external world
11:19 < frevidar> errr, I mean stepcut
11:19 < frevidar> actually, I was probably talking to myself :o
11:19 < frevidar> stepcut: I'm talking about when you get rid of the do notation, or what its shorthand for, that is
11:19 < stepcut> then when we called hGetLine we would do it like this:
11:20 < stepcut> func world0 h = let (line1, world1) = hGetLine world0 h ; let (line2, world2) = hGetLine world1 h
11:20 < stepcut> where we pass in the state of the world and get back the new state of the world.
11:22 < stepcut> but, if the programmer accidently passed world0 to both instances of hGetLine, then things might be screwy. If instead of hGetLine, it was hLaunchMissles, then the second time hLaunchMissles gets called, the program is supposed to some how reset the state of external world back to the way it was at world0 and launch the missles. That is a bit outside the scope of Haskell.
11:22 < stepcut> the fix is to ensure that each world* variable gets used exactly once
11:23 < stepcut> so we make a type like, newtype IO a = IO { runIO :: World -> (World, a ) }
11:23 < stepcut> and make a monad instance like
11:23 < stepcut> instance Monad IO where
11:24 < stepcut>    return a = IO (\world -> (world, a)) -- does not affect the state of the world
11:25 < stepcut>     m >>= f = IO $ \world0 -> let (world1, a) = (runIO m) world0 ; runIO (f a) world1
11:26 < stepcut> and hGetLine is defined like:
11:27 < stepcut> hGetLine :: Handle -> IO String ; hGetLine h = IO $ \world -> internalHGetLine world h
11:28 < stepcut> internalHGetLine :: World -> Handle -> (World, String)
11:29 < stepcut> since the developers are working with high-level hGetLine :: Handle -> IO String, interface, they can't accidently screw up the passing around of the world variables. That is done automatically by the IO monad (and the implementations of functions like hGetLine)
11:30 < frevidar> so IO a :: World -> (World, a)
11:30 < frevidar> and our main :: IO ()
11:31 < stepcut> yes
11:31 < frevidar> so what a haskell main function returns is a function which transforms the world
11:31 < stepcut> frevidar: yes
11:31 < stepcut> frevidar: so, from that view point, main is a pure function, if you pass in the same intial state of the world, you will get the same output
11:32 < frevidar> ok, cool, and the details of the IO monad achieve that...
11:32 < frevidar> but...
11:32 < stepcut> though, passing in the same initial state of the world can be tricky :)
11:33 < frevidar> that gets us back to the queue...
11:34 < stepcut> frevidar: so, reading the queue is an IO operation. Meaning that each time you read it the implicit world variable is different, which is why it can return different values.
11:34 < frevidar> *thinks*...
11:35 < frevidar> so loop st :: IO ()
11:40 < frevidar> so... readTChan :: Queue -> World -> World
11:40 < frevidar> so readTChan is pure
11:40 < frevidar> because even though its getting the same queue, its getting a different world?
11:41 < stepcut> yes
11:41 < frevidar> so returning different results with the same queue is ok
11:41 < frevidar> it all makes sense now!
11:41 < frevidar> thanks!
11:41 < frevidar> well, besides how the do-notation does all the details
11:41 < frevidar> but I've kinda got the vibe of that
12:24 < stepcut> grr, ghc: panic! (the 'impossible' happened) (GHC version 6.10.1 for i386-unknown-linux): loadObj: failed
15:04 < stepcut> ToMessage is troublesome if you have an applicatino which renders the same type more than one way
15:59 < wchogg> I'm not clear on what the second parameter in seeOther is used for.  Is it used as some kind of default?
15:59 < stepcut> wchogg: it gets sent in the body of the response
16:02 < stepcut> wchogg: according to, http://libraries.ucsd.edu/about/tools/rfc2616-10.html, for a 303 you should include a short hypertext note with a hyperlink to the new url(s)
16:02 < wchogg> stepcut:  Oh!!  I was expecting it to somehow be appended to the final Response, which is why I didn't understand why I saw no affect.  Yeah, that makes sense now.
16:02 < wchogg> stepcut: thanks
--- Log closed Wed Mar 04 00:00:29 2009