ambiguous (partially defined) type problem / active objects
Dear all, I'm trying to create active nodes that have a common base part and a user definable custom part, run in their own thread and can communicate/send message to each other. For some reason the compiler complains about an ambiguous type, while I feel, though the type may be partially ambiguous, I'm not using that particular part in related function calls. So in some ways I think there is nothing to complain. Is there a way to resolve this? Are there better/easier solutions to this problem? My solution so far is as follows (see code is below): The active nodes have different types that have a common base state and a changing top state. The base state will be responsible for connection nodes to each other, the top state is user defined. 1. I 'piggy-bag' two State transformers (and IO) onto each other, one with the base state and one with the custom state 2. In their own thread, the top state transformer processes the contents of a list (in which each element is an action) that comes from a channel. 3. The channel is basically the active object and can receive message. 4. Since 'active objects' need to be stored in a list in the base state they are wrapped in a general node data structure (GN) that hides the type of the active object. In this way, actions can be fed to the channel and are executed in the thread and context of the state monad. One can define a changing topstate and add basic functionality to it. This works reasonably well, except that when I store a node into another node (using the base functionality) and later on retrieve it and then want to apply some other suitably typed functions functions using only the base functionality, the compiler complains that the type is ambiguous. However I feel it shouldn't complain, since the functions doesn't use the ambiguous part. Of course the problem is resolved as soon as a function establishes the type. However, in many cases I don't know (and don't want to know the type). Can this be resolved? The code below is unfortunately rather long. Of course it doesn't compile, unless the last comment is removed. If removed it works fine. Any comments and pointers appreciated. Thanks. (I'm quite new to haskell, so I may be missing some obvious things.) Maarten P.S. (Sorry for the long code sample below, but I don't know how to condens is into something smaller, while retaining the relevant properties). {-# OPTIONS -fglasgow-exts -cpp -fallow-undecidable-instances #-} module Main where import Data.Generics import Control.Monad.State import Data.Unique import Control.Concurrent (forkIO, forkOS) import Control.Concurrent.Chan import Control.Concurrent.MVar instance Show Unique where show a = show (hashUnique a) -- default node state; same for all states; nodes are active objects (see below) data DefNodeState = DefNodeState { id::Unique, nodes::ActiveObjects } deriving Show -- make this default state a base state transformer type BaseStateT m = StateT DefNodeState m type BaseStateTIO = BaseStateT IO -- TopStateT with parameterizable state type TopStateT ts a = StateT ts BaseStateTIO a instance Show (TopStateT b ()) where show a = "TopStateT Monad" tsTc = mkTyCon "TopStateT" instance forall ts. (Show ts, Typeable ts) => Typeable1 (StateT ts BaseStateTIO) where typeOf1 _ = mkTyConApp tsTc [] -- hide top state type so we can put them in a list; typeable so it can be cast back data GN = forall o. (Show o,Typeable o) => GN o instance Show (GN) where show (GN o) = "GN " ++ show o gnTc = mkTyCon "GN" instance Typeable GN where typeOf _ = mkTyConApp gnTc [] -- convenience type type ActiveObjects = [GN] -- active objects -- make topstate an active object by putting it in a chan data ActiveObject a = ActiveObject (Chan (TopStateT a ())) -- make active object instance of show instance Show (ActiveObject o) where show o = "ActiveObject Chan StateT" -- make active object typeable activeObjectTc = mkTyCon "ActiveObject" instance Typeable1 ActiveObject where typeOf1 _ = mkTyConApp activeObjectTc [] -- make new active object over some state newActiveObject action state t = do chan <- newChan contents <- getChanContents chan forkIO (start (action contents) state) return (ActiveObject chan) -- send an action if we are not interested in the result send :: forall a . (Typeable a, Show a) => ActiveObject a -> TopStateT a () -> IO () send (ActiveObject chan) act = do writeChan chan act -- sync an action if we are interested in the result sync :: forall a b. (Typeable a, Show a) => ActiveObject a -> TopStateT a b -> IO b sync ao f = do mv <- newEmptyMVar send ao (f' mv) a <- takeMVar mv return a where f' mv = do a <- f lift $ lift $ putMVar mv a class SendSync t where sendM :: forall a. (Typeable a, Show a) => t a -> TopStateT a () -> IO () syncM :: forall a b. (Typeable a, Show a) => t a -> TopStateT a b -> IO b instance SendSync (ActiveObject) where sendM ao m = send ao m syncM ao m = sync ao m {- unforunately doesn't work instance SendSync (GN) where sendM (GN ao) m = sendM ao m syncM (GN ao) m = syncM ao m -} castGN :: forall a. (Show a,Typeable a) => GN -> ActiveObject a castGN (GN o) = mcast o where mcast o = case (cast o) of Nothing -> error "No cast possible" Just a -> a -- some test states for toplevel data TestState = TestState { val::Int } deriving (Show,Typeable) data TestState2 = TestState2 { val2::Int } deriving (Show,Typeable) uid :: forall b. TopStateT b Unique uid = lift $ gets Main.id changeUid :: forall b. Unique -> TopStateT b () changeUid n = do lift $ modify (\s -> s { Main.id = n }) newUid :: forall b. TopStateT b () newUid = do n <- lift $ lift $ newUnique lift $ modify (\s -> s { Main.id = n }) -- add a node to list in basestate addNode :: forall o a. (Typeable a, Show o,Typeable o) => ActiveObject o -> TopStateT a () addNode n = do ns <- lift $ gets nodes lift $ modify (\s -> s { nodes = (GN n):ns }) -- get a node from list in basestate getNode :: forall a o. (Show o,Typeable o) => Integer -> (TopStateT a (ActiveObject o)) getNode i = do ns <- lift $ gets nodes let ao = ns !! (fromInteger i) return (castGN ao) -- action that executes messages send to it action [] = return () action (e:es) = do -- just for testing i <- uid s <- get ns <- lift get lift $ lift $ putStrLn $ "event (" ++ show i ++ "):" ++ " state:" ++ show s ++ " nstate:" ++ show ns -- e action es -- create a new node state with a unique id newNodeState = do u <- newUnique return (DefNodeState u []) -- start evaluation start action state = do ns <- newNodeState execStateT (execStateT action state) ns return () main = do ao <- newActiveObject action (TestState 5) TestState ao2 <- newActiveObject action (TestState2 5) TestState2 -- simple test u1 <- syncM ao (gets val) putStrLn $ "u1:" ++ show u1 -- show complete state r <- syncM ao (get) putStrLn $ show r -- connect ao to ao2 sendM ao (addNode ao2) -- get node back ao2' <- syncM ao (getNode 0) -- get unique id u <- syncM ao2' (uid) putStrLn $ "u:" ++ show (hashUnique u) -- set a new id sendM ao2' (newUid) -- show it u' <- syncM ao2' (uid) putStrLn $ "u':" ++ show (hashUnique u') -- u2 <- syncM ao2' (gets val2) getChar return ()
participants (1)
-
Maarten