{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE ScopedTypeVariables #-} {- OOHaskell (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke This module illustrates the notion of self, i.e., open recursion. It is a variation on module Selfish with safety addded. -} module SelfishSafe where import OOHaskell import qualified Prelude (print) import Prelude hiding (print) -- import SMRFix import Debug.Trace -- First, declare the labels. -- We use proxies as of HList/Label4.hs data MutableX; mutableX = proxy::Proxy MutableX data GetX; getX = proxy::Proxy GetX data MoveX; moveX = proxy::Proxy MoveX data Print; print = proxy::Proxy Print printable_point x_init s = do x <- newIORef x_init -- If we uncomment this, we get the type error. -- Alas, the error is reported at the place of smfix rather than here... -- s # print -- srret s (\s-> construct s (\s-> --trace "in srret" $ -- seq (trace "in srret, seq" 1) $ -- s `seq` -- seq (trace "in srret, seq 2" 1) $ mutableX .=. x .*. getX .=. readIORef x .*. moveX .=. (\d -> modifyIORef x ((+) d)) .*. print .=. ((s # getX ) >>= Prelude.print) .*. emptyRecord) test_pp = do -- p <- smrfix (printable_point 7) p <- new (printable_point 7) p # moveX $ 2 p # print -- We need another label. data GetColor; getColor = proxy::Proxy GetColor -- Inheritance is simple: just adding methods ... colored_point x_init (color::String) self = do p <- printable_point x_init self -- This causes the type error (alas, at the confusing place) -- p # print -- srret p $ \p -> getColor .=. (returnIO color) .*. p construct p $ \p -> getColor .=. (returnIO color) .*. p myColoredOOP = do -- p' <- smrfix (colored_point 5 "red") p' <- new (colored_point 5 "red") x <- p' # getX c <- p' # getColor Prelude.print (x,c) -- We derive a better class of colored points, which prints more accurately. -- To this end, we access the overriden method akin to the OCaml super. colored_point' x_init color self = do p <- colored_point x_init color self -- srret p $ \p -> construct p $ \p -> -- Here, it's OK to access the method print of p, -- even if p isn't constructed yet. Courtesy of non-strict -- evaluation, old_print will be evaluated only when the construction -- finishes. let old_print = p # print in print .=. ( do putStr "so far - "; old_print putStr "color - "; Prelude.print color ) .<. p myOverridingOOP = do -- p <- smrfix (colored_point' 5 "red") p <- new (colored_point' 5 "red") p # print testGeneric = do -- p <- smrfix (printable_point 7) -- p' <- smrfix (colored_point 5 "red") p <- new (printable_point 7) p' <- new (colored_point 5 "red") let get_succ_x obj = obj # getX >>= (returnIO . (+ 1)) x <- get_succ_x p x' <- get_succ_x p' Prelude.print $ x+x' -- Note, compared with printable_point, we omitted the virtual methods. -- That made abstract_point uninstantiatable!!! -- This is an optional part in case we want to fix types of virtuals. {- abstract_point (x_init::a) (self :: NotFixed r) | const False (constrain (undefined::r) :: Proxy ( (Proxy GetX, IO a) :*: (Proxy MoveX, a -> IO ()) :*: HNil )) = undefined -} abstract_point x_init self = do x <- newIORef x_init -- srret self $ \self -> construct self $ \self -> mutableX .=. x .*. print .=. (self # getX >>= Prelude.print ) .*. emptyRecord concrete_point x_init self = do p <- abstract_point x_init self -- inherit ... -- srret (p,self) $ \ (p,self) -> constructWithSuper p self $ \ p self -> -- add the missing (pure virtual) methods getX .=. readIORef (self # mutableX) .*. moveX .=. (\d -> modifyIORef (self # mutableX) ((+) d)) .*. p testVirtual = do -- p <- smrfix (concrete_point 7) p <- new (concrete_point 7) -- -- Note, if the latter is uncommented -- p' <- smrfix (abstract_point 7) -- we see an error that means "field getX missing" -- which reads as follows: -- (HasField (Proxy GetX) HNil (IO a)) p # getX >>= Prelude.print p # moveX $ 2 p # getX >>= Prelude.print p # print -- This abstract point class mentions the type of the virtual methods. abstract_point' x_init self = do x <- newIORef x_init -- srret self $ \self -> construct self $ \self -> mutableX .=. x .*. getX .=. (proxy::Proxy (IO Int)) .*. moveX .=. (proxy::Proxy (Int -> IO ())) .*. print .=. (self # getX >>= Prelude.print ) .*. emptyRecord -- Another label for testing purposes data MyLabel; myLabel = proxy::Proxy MyLabel -- This concrete class implements all virtual methods concrete_point' x_init self = do p <- abstract_point' x_init self -- inherit ... -- srret (p,self) $ \(p,self) -> constructWithSuper p self $ \ p self -> -- use disciplined record update getX .=. readIORef (self # mutableX) .^. moveX .=. (\d -> modifyIORef (self # mutableX) ((+) d)) .^. myLabel .=. () -- This line could be activated. -- .^. myLabel .=. (proxy::Proxy ()) -- A proxy that disables mnew. .*. p -- We introduce a constrained new method to refuse proxy fields in records. {- mnew (f::NotFixed a -> m (NotFixed a)) = new f where () = hasNoProxies (undefined::a) -} mnew f = new f where () = hasNoProxies (get_class_type f) get_class_type:: (NotFixed a -> m (NotFixed a)) -> a get_class_type = undefined testVirtual' = do p <- mnew (concrete_point' 7) p # getX >>= Prelude.print p # moveX $ 2 p # getX >>= Prelude.print p # print main = do putStrLn "Simple Printable Pt" ; test_pp putStrLn "myColoredOOP" ; myColoredOOP putStrLn "myOverridingOOP" ; myOverridingOOP putStrLn "testGeneric" ; testGeneric putStrLn "testVirtual" ; testVirtual putStrLn "testVirtual'" ; testVirtual' -- :t colored_point -- :t mfix $ colored_point (1::Int) "red"