world as value

Fergus Henderson fjh@cs.mu.OZ.AU
Wed, 23 Jun 1999 20:51:52 +1000


On 23-Jun-1999, Fergus Henderson <fjh@cs.mu.OZ.AU> wrote:
> See the code at the end of this mail.

Sorry, I forgot to attach the code.  Here it is.

	import Prelude hiding (IO, getChar, putChar)

	-- this is just like the usual stream I/O technique,
	-- except that I've added process ids and timestamps
	data IO t = MkIO (  (Pid, InfiniteTree Timestamp, [Response])
			 -> (t, [Request]) )

	-- we take as input an infinite tree of time stamps
	data InfiniteTree t = MkTree t (InfiniteTree t) (InfiniteTree t)
	type Timestamp = Integer

	-- requests are annotated with timestamps, so that they can
	-- be sorted in the appropriate order.
	-- requests and responses are both annotated with a process id,
	-- so that each response can be sent to the proper sub-process
	type Request = (Pid, Timestamp, RequestType)
	data RequestType
		= ReqGetChar
		| ReqPutChar Char
		-- other IO operations go here ...

	type Response = (Pid, ResponseType)
	data ResponseType
		= RespGetChar Char
		| RespPutChar
		-- other responses from IO operations go here ...

	-- each process id is repesented as a path from the main process
	-- to that sub-process, specifying whether the sub-process was on the
	-- left hand side or the right hand side of each fork.
	type Pid = [Fork]
	data Fork = ForkLeft | ForkRight

	-- here's the standard stream I/O stuff

	-- define the basic I/O operations using do_io_action
	my_putChar :: Char -> IO ()
	my_putChar c = do_io_action (ReqPutChar c) (\ ~(RespPutChar) -> ())

	getChar :: IO Char
	getChar = do_io_action (ReqGetChar) (\ ~(RespGetChar c) -> c)

	-- do_io_action marks each request with the pid and a timestamp
	do_io_action :: RequestType -> (ResponseType -> t) -> IO t
	do_io_action request_type response_handler = MkIO action where
	    action (pid, timestamps, ~(resp:_)) = (result, [request]) where
		(resp_pid, resp_type) = resp
		(MkTree timestamp _ _) = timestamps
		request = (pid, timestamp, request_type)
		result = response_handler resp_type

	instance Monad IO where
	    (MkIO action1) >>= io_action2 = MkIO action where
		action (pid, timestamps, resps) = (x, reqs) where
		    (MkTree _ timestamps1 timestamps2) = timestamps
		    (y, reqs1) = action1 (pid, timestamps1, resps)
		    resps2 = drop (length reqs1) resps
		    (MkIO action2) = io_action2 y
		    (x, reqs2) = action2 (pid, timestamps2, resps2)
		    reqs = reqs1 ++ reqs2
	    return x = MkIO action where
		action _ = (x, [])
			
	forkIO (MkIO action1) (MkIO action2) = MkIO action where
	   action (pid, timestamps, responses) = (x, requests)
		where child_pid1 = pid ++ [ForkLeft]
		      child_pid2 = pid ++ [ForkRight]
		      (MkTree _ ts1 ts2) = timestamps
		      (responses1, responses2) = split_responses responses
		      (x1, requests1) = action1 (child_pid1, ts1, responses1)
		      (x2, requests2) = action2 (child_pid2, ts2, responses2)
		      requests = merge_requests requests1 requests2
		      x = (x1, x2)

	-- split a list of responses based on their attached process ids
	split_responses (r:rs) =
		let	((fork:forks), resp) = r
			r' = (forks, resp)
			(rs1, rs2) = split_responses rs
		in	case fork of
				ForkLeft  -> ((r':rs1), rs2)
				ForkRight -> (rs1, (r':rs2))

	-- do a sorted merge of requests, based on their time stamps
	merge_requests (x:xs) (y:ys) =
		if get_timestamp x < get_timestamp y
			then x : merge_requests xs (y:ys)
			else y : merge_requests (x:xs) ys
		where get_timestamp (_, timestamp, _) = timestamp

-- 
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3        |     -- the last words of T. S. Garp.