Data.Dynamic and dynamically loaded code

(lengthy) Hey, In the hs-plugins library I'm using Data.Dynamic to provide runtime type checking of plugin values when they are loaded. There is a problem, however: when using fromDyn/fromDynamic to check the type of the plugin's value against the type the application loading the plugin expects, they always report that types are unequal, even if they are the same type on both sides. So I'm reporting a limitation of the existing Data.Dynamic, and have a couple of questions about why this exists. The problem appears to be in the hash keys of the type representation used to compare to types for equality. A dynamic value in the (statically compiled) application never has the same key as its equivalent type in the dynamically loaded code. The type in the dynamically-loaded plugin code is never recognised as having the same type as in the application (static) code. The following comment from Data.Typeable seems to be relevant: -- In GHC we use the RTS's genSym function to get a new unique, -- because in GHCi we might have two copies of the Data.Typeable -- library running (one in the compiler and one in the running -- program), and we need to make sure they don't share any keys. -- -- This is really a hack. A better solution would be to centralise the -- whole mutable state used by this module, i.e. both hashtables. But -- the current solution solves the immediate problem, which is that -- dynamics generated in one world with one type were erroneously -- being recognised by the other world as having a different type. An example. The following code uses eval() to compile the string "7 + 8" to object code, and dynamically load the result. main = do i <- eval "7 + 8 :: Int" :: IO Int putStrLn $ show i When checking the dynamically loaded type using fromDynamic, we have <Int> /= <Int>, which is obviously wrong. Running the equivalent code in GHCi doesn't generate this error, nor does statically linked code. It is only if we loadObj the plugin and check it against a type statically compiled into the application doing the loading. I currently work around this with a reimplemented Data.Dynamic that compares the string representations of the types, which works mostly (so that "Int" == "Int", in the above code). However, when there is no explicit type declaration in the dynamically loaded code, for non-simple types, the *string* type representations differ. I.e. in the following code: main = do fn <- eval "\\(x::Int) -> (x,x)" :: IO (Int -> (Int,Int)) putStrLn $ show (fn 7) we have type "-> Int (Int,Int)" doesn't match "Int -> (Int,Int)" (which looks like a Core type in the first case). And for: i <- eval "map (+1) [0..10::Int]" :: [Int] we have "[] Int" /= "[Int]". So, the string comparison of types doesn't always work. So... for safe dynamically loaded plugins we need to fix Data.Dynamic to provide the a unique integer key for types across both static and dynamic code, I think. Does that seem like reasonable? And a question: why do we get different strings from TypeRep's when the type is inferred? I can see that we are getting Core type reps, but why? I would have thought that the TypeRep would still have to be constructed in the same way as in an explicit declaration. -- Don
participants (1)
-
dons@cse.unsw.edu.au