Ответ 1
Это, пожалуй, не самый надежный подход, но мы можем злоупотреблять моим пакетом reflection
, чтобы лгать о 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)
Учитывая, что мы можем теперь заглянуть в TypeRep
нашего аргумента Dynamic
и соответствующим образом создать функцию Dynamic
.
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 ()))
Это может быть намного проще, если base
просто предоставил нам что-то вроде apD
, но для этого требуется тип ранга 2, а Typeable
/Dynamic
удастся избежать их, даже если Data
нет.
Другой путь - просто использовать реализацию Dynamic
:
data Dynamic = Dynamic TypeRep Any
и unsafeCoerce
в свой собственный тип данных Dynamic'
, сделайте то, что вам нужно сделать с TypeRep
во внутренних компонентах, и после применения вашей функции unsafeCoerce
все обратно.