It's very easy to state this problem without enough details and mislead
people into providing a solution for a different problem, so I'll try to
include all the information and use-case. I need a function that can
store a value in a concrete opaque type. I know the type of the value
when I store it, and I know the type of the value when I extract it,
insofar as I know the type inclusive of a class constraint context. But
the container must store many different things at once. Given that I
know the type of the thing when I store it and when I extract it, there
ought to be a “safe’ way to do this, in the same way that Data.Dynamic
is “safe”.

The use-case is I want to store a table of IO-like actions in an opaque
like [(Name,Dynamic)] and I write into it statically, e.g. in GHCi.

The reason is due to this approach:

go_ref = newIORef go_

go_ = putStrLn "Hello, World!"

go = do
  go' <- liftIO $ readIORef go_ref

main = forkIO $ forever $ go

then in GHCi I writeIORef go_ (putStrLn "What's up?")

then go will now start printing "What's up?" This works now, I can do
this presently. However, I want to write this as a core-to-core
translation as a ghc-plugin. I want the definition go = putStrLn "Hello
World!" to be translated to what I wrote above. Core cannot generate new
names to be exported from a module, so go_ is now gone. So how does the
`go' function read and write the IORef? With a table:

table = unsafePerformIO $ newIORef [("go",Dynamic go_ref)]

Hurrah! I can now put table in some module like DynamicUpdate.table and
then read/write to it from Core in the generated definition of
go. Done. But how do I update it from the GHCi REPL? What follows is
where I'm stuck.

So with Data.Dynamic, I have:

toDyn :: Typeable a => a -> Dynamic

With toDyn I can store concrete values and functions and then extract

λ> fmap ($ 12) (fromDynamic (toDyn ((*2) :: Int -> Int)) :: Maybe (Int -> Int))
Just 24

But the problem with toDyn is that it can only store concrete values due
to the Typeable constraint. So then I turn to existentials and

λ> data Anything = forall a. Anything a
λ> let x = Anything id
λ> case x of Anything (unsafeCoerce -> (id' :: Int -> Int)) -> id' 123

Great, I was able to store a value which contained non-concrete
types. But now consider the case of a value with type-class
polymorphic type in it:

y = Anything (print :: Show a => a -> IO ())

Which cannot be type checked, because the Show instance is
ambiguous. So you might propose to use rank-N types, like this:

data Anything = Anything (Show a => a -> IO ())

Now I can store print in there happily. But now the type is not
Anything, it's not generic anymore. Maybe I want to store `print', maybe
I want to store `readLn'. Dead end. I don't want to have to generate
existential wrappers for all functions I might possibly want to store.

The support of Constraints in this page makes me think that it's still
possible. It would be cool to somehow store a tuple of the dictionary of
the constraint and the value itself and then I'd later be able to
extract it. But I'm finding it difficult developing anything even

Any ideas/help appreciated!
Haskell-Cafe mailing list

Reply via email to