[Haskell-cafe] Towards an IO-Comonad that preserves referential transparency (was: "comonads, io" Jan 02, 2003)

Sven Biedermann Sven.Biedermann at Biedermann-Consulting.de
Thu Nov 2 14:02:22 EST 2006


I just realized that I mixed up data bases and functional programming  
and apologize for this. Of course, I meant "referential transparency"!

Sven Biedermann

Am 02.11.2006 um 16:43 schrieb Sven Biedermann:

> Dear Haskellers,
>
> The OI Comonad in Richard Kieburtz' paper does break referential
> integrity, but he worte, that the implementation is just something
> "...to experiment with". In this papers he states, that a real
> OI needs special properties. For instance enableOI needs "...to  
> have the
> effect of copying pointers to currently accessible IO resources, in
> effect duplicating the current IO environment."
>
> So, I wrote a simple OI-Comonad for stdin/stdout only, that preserves
> referential integrity. At least, my reasoning and the tests I made
> didn't reveal anything different.
> The code and some explanations are presented at the end of this mail.
>
> Haskellers: Could you review the solution, please?  Does it really
> preserves referential integrity? Or am I completely wrong?
> Do you have any suggestions on generalizing the idea?
>
> I would be glad to hear from you!
>
> Best regards
>
> Sven Biedermann
>
>
> ----------------------------------------------------------------
>
>  The implementation is based on two ideas:
>
> 1) The OI outside world is modeled an infinite structure (OIReality)
> that keeps track effects created in the outside world. All  
> "futures" of
> a reality at a point of "time" are remembered in a lazy fashion.  
> So, if
> one probes on referential integrity, the structure will be re-iterated
> through, playing back already computed results. If a reality at some
> point of "time" isn't referenced any more, that reality will be
> reclaimed by the garbage collector.
>
> The following statement is an example, that no space leaks occur as  
> long
> as a specific reality isn't remembered (stdPutStr builds on basic
> stdPutChar using the standard operators .>> and =>>):
>
> 	extract $ mkOI 'x' .>> (cycle ['A'..'z']) =>> oiPutStr
>
> Amazingly,
>
> 	extract $ mkOI 'x' .>> (cycle ['A'..'z']) =>> oiPutStrLn
>
> produces a space leak, because the implementation isn't lazy  
> enough, yet.
>
> 2) The OI-Comonad is a pointer to the current reality, together  
> with the
> value, that can be extracted from OI. Whenever an OI ist  
> rememberable by
> the user, the pointer will be duplicated. This is crucial for
> referential integrity.
>
> Comain, as defined by Andrew Bromage in :
>
> 	comain :: OI a -> ()
> 	comain w = extract (w .>> show (a,b) =>> oiPutStrLn)
> 			where a = extract (w .>> () =>> oiGetChar)
> 			          b = extract (w .>> () =>> oiGetChar)
>
>
> now works as intended and delivers, if 'x' is typed in on standard
> input:
>
> 	('x','x')
>
> "a = ..." runs on reality w, yielding 'x' . "b = ..." is just a play
> back of "a = ...". "extract (w.>> show (a,b) =>> stdPutStrLn)" is
> another future of w, which just prints ('x','x').
>
>
>
>
>
> module SimpleOI where
>
> import Control.Monad.Instances
> import System.IO.Unsafe
> import Data.Char
> import Data.IORef
>
> --------------------------------------
> -- copied from http://www.eyrie.org/~zednenem/2004/hsce/
>
> class Functor w => Comonad w where
>   extract   :: w a -> a
>   duplicate :: w a -> w (w a)
>   extend    :: (w a -> b) -> (w a -> w b)
>
>   extend f  = fmap f . duplicate
>   duplicate = extend id
>
> -- | 'fmap' defined in terms of 'extend'
> liftW :: Comonad w => (a -> b) -> (w a -> w b)
> liftW f = extend (f . extract)
>
> -- | 'extend' with the arguments swapped. Dual to '>>=' for monads.
> (=>>) :: Comonad w => w a -> (w a -> b) -> w b
> (=>>) = flip extend
>
> -- | Injects a value into the comonad.
> (.>>) :: Comonad w => w a -> b -> w b
> w .>> b = extend (\_ -> b) w
>
> -- end of copy
> -------------------------------------
>
> data OIReality = OIReality GetChar [PutChar] Char
> data GetChar = GetChar Char OIReality
> data PutChar = PutChar () OIReality
>
> constructReality :: Char -> OIReality
> constructReality c = let r = OIReality (constructGet r)  
> (constructPuts r) c
> 					 in r
>
> constructGet :: OIReality -> GetChar
> constructGet r = let c = unsafePerformIO $ seq r getChar in GetChar  
> c (constructReality c)
>
>
> constructPut :: OIReality -> Char -> PutChar
> constructPut r c = let v = unsafePerformIO  $ seq r $ putChar c in   
> PutChar v (constructReality c)
>
> constructPuts :: OIReality -> [PutChar]
> constructPuts r = map (constructPut r.chr) [0..255]
>
> nextGetCharReality :: OIReality -> OIReality
> nextGetCharReality (OIReality (GetChar c r) _ _) = r
>
> nextPutCharReality :: Char -> OIReality -> OIReality
> nextPutCharReality c (OIReality _ puts _)  = let PutChar v r =  
> puts !! (ord c) in  seq v r
>
>
>
> data OIComonad a = OI (IORef OIReality) a
>
> -- extract    OI _ a   = a
> instance Functor OIComonad where
> 	fmap f (OI ref a) = seq a $ OI (unsafeDuplicateRef ref) (f a) --  
> too many seq's at the moment
>
> instance Comonad OIComonad where
>     duplicate w = fmap (const w) w
>     extract (OI _ a) = a
>
> mkOI :: Char -> OIComonad Char
> mkOI c = OI (unsafePerformIO $ newIORef $ constructReality c) c
>
> oiGetChar :: OIComonad a -> Char
> oiGetChar (OI ref _) = unsafePerformIO $ do {
> 							modifyIORef ref nextGetCharReality;
> 							OIReality _ _ c <- readIORef ref;
> 							return (c) }
>
> oiPutChar :: OIComonad Char -> ()
> oiPutChar (OI ref c) = unsafePerformIO $ do {
> 							modifyIORef ref (nextPutCharReality c);
> 							OIReality _ _ d <- readIORef ref; -- not!? modified without  
> further read
> 							return $ seq d ()}
>
> unsafeDuplicateRef :: IORef OIReality -> IORef OIReality
> unsafeDuplicateRef ref = unsafePerformIO $ do {
> 										r <- readIORef ref;
> 										newIORef r; }				
>
> oiPutStrLn w = extract $ w =>> oiPutStr .>> '\n' =>> oiPutChar --  
> space leak if input string is suffiently long
>                                                                --  
> too many seqs in code
>
> oiPutStr w = extract $ oiPutStrS w .>> ()
>
> oiPutStrS :: OIComonad String -> OIComonad String
> oiPutStrS w = case (extract w) of
>                    [] -> w
>                    (c:cs) -> oiPutStrS (w .>> c =>> oiPutChar .>> cs)
>
> oiGetLine = "t.b.d. using oiGetChar"
>
>
> -- adapted from http://www.mail-archive.com/haskell- 
> cafe at haskell.org/msg02408.html
> comain w = extract (w .>> show (a,b) =>> oiPutStr)
>             where
>                 a = extract (w .>> () =>> oiGetChar)
>                 b = extract (w .>> () =>> oiGetChar)
>
> 										
> {- Original from http://www.mail-archive.com/haskell- 
> cafe at haskell.org/msg02408.html
>
> -- Bootstrap into the OI comonad
>         main :: IO ()
>         main = return $! comain stdOI
>
>         -- The following are the OI functions which we use.
>         -- stdGetChar :: OI () -> Char
>         -- stdPutStrLn :: OI String -> ()
>
>         comain :: OI a -> ()
>         comain w
>             = coeval (w .>> show (a,b) =>> stdPutStrLn)
>             where
>                 a = coeval (w .>> () =>> stdGetChar)
>                 b = coeval (w .>> () =>> stdGetChar)
>
> -}
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe



More information about the Haskell-Cafe mailing list