Подтвердить что ты не робот

Как применить полиморфную функцию к динамическому значению

Существует ли разумный способ применить полиморфную функцию к значению типа Dynamic?

Например, у меня есть значение типа Dynamic, и я хочу применить Just к значению внутри Dynamic. Поэтому, если значение было построено с помощью toDyn True, я хочу, чтобы результат был toDyn (Just True). Число различных типов, которые могут встречаться внутри Dynamic, не ограничено.

(У меня есть решение, когда используемые типы происходят из замкнутой вселенной, но это неприятно.)

4b9b3361

Ответ 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 все обратно.