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

Nicolas Frisby nicolas.frisby at gmail.com
Thu Nov 2 14:51:16 EST 2006


OI has been lingering in the back of my mind during my own comonadic adventures.

One thing that's bothered me is the signature of comain.

  comain :: OI () -> ()

If the OI comonad is to represent values "in the context of the
RealWorld", and yet we can have multiple and differing copies of the
RealWorld context, then this seems to match the multiple worlds
perspective (which is why I think Kieburtz' experimental
implementation was doomed to violate referential transparency). That
is I want any deterministic program to be of top-level type:

  comain :: OI () -> OI ()

The intuition is that the program as a whole receives the initial
world description, then is allowed to duplicate and manipulate that
world in any number of ways, but it has to choose one final world as
its choice. All other real worlds are discarded. The OI () -> ()
signature doesn't allow for the program to tell me what the resulting
RealWorld ought to be.

An implementation of this doesn't seem impossible (as opposed to
improbable) for file IO or references (your filesystem/cell store
would have to be transactional and "multi-ported"). It does become a
bit mind boggling for concurrency or any other multi-agent paradigm
(networking, screen and keyboard IO, etc.)--it seems that the other
agents would have to be savvy to the multiple worlds perspective.

Transactional systems certainly seem like a ripe application area for
the Product comonad with a RealWorld abstract data type as the carried
context.

My apologies for not actually commenting on your code.

Nick

On 11/2/06, Sven Biedermann <Sven.Biedermann at biedermann-consulting.de> wrote:
> 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
>
> _______________________________________________
> 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