[Haskell-cafe] Store type-class polymorphic values generically

Christopher Done chrisdone
Fri Oct 4 01:12:52 UTC 2013


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
  go'

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
them:

?> 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
unsafeCoerce:

?> data Anything = forall a. Anything a
?> let x = Anything id
?> case x of Anything (unsafeCoerce -> (id' :: Int -> Int)) -> id' 123
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
http://blog.omega-prime.co.uk/?p=127 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
simple.

Any ideas/help appreciated!




More information about the Haskell-Cafe mailing list