Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply a polymorphic function to a Dynamic value

Is there a sane way to apply a polymorphic function to a value of type Dynamic?

For instance, I have a value of type Dynamic and I want to apply Just to the value inside the Dynamic. So if the value was constructed by toDyn True I want the result to be toDyn (Just True). The number of different types that can occur inside the Dynamic is not bounded.

(I have a solution when the types involved come from a closed universe, but it's unpleasant.)

like image 987
augustss Avatar asked Jun 04 '12 23:06

augustss


1 Answers

This is perhaps not the sanest approach, but we can abuse my reflection package to lie about a TypeRep.

{-# LANGUAGE Rank2Types, FlexibleContexts, ScopedTypeVariables #-}
import Data.Dynamic
import Data.Proxy
import Data.Reflection
import GHC.Prim (Any)
import Unsafe.Coerce

newtype WithRep s a = WithRep { withRep :: a }

instance Reifies s TypeRep => Typeable (WithRep s a) where
  typeOf s = reflect (Proxy :: Proxy s)

Given that we can now peek at the TypeRep of our Dynamic argument and instantiate our Dynamic function appropriately.

apD :: forall f. Typeable1 f => (forall a. a -> f a) -> Dynamic -> Dynamic
apD f a = dynApp df a
  where t = dynTypeRep a
        df = reify (mkFunTy t (typeOf1 (undefined :: f ()) `mkAppTy` t)) $ 
                  \(_ :: Proxy s) -> toDyn (WithRep f :: WithRep s (() -> f ()))

It could be a lot easier if base just supplied something like apD for us, but that requires a rank 2 type, and Typeable/Dynamic manage to avoid them, even if Data does not.

Another path would be to just exploit the implementation of Dynamic:

data Dynamic = Dynamic TypeRep Any

and unsafeCoerce to your own Dynamic' data type, do what you need to do with the TypeRep in the internals, and after applying your function, unsafeCoerce everything back.

like image 153
Edward Kmett Avatar answered Nov 08 '22 06:11

Edward Kmett