如何将一个多态函数应用于动态值

19

有没有一种合理的方法将多态函数应用于类型为 Dynamic 的值?

例如,我有一个类型为 Dynamic 的值,并且我想将 Just 应用于 Dynamic 内部的值。因此,如果该值是通过 toDyn True 构造的,则希望结果为 toDyn (Just True)。可以出现在 Dynamic 内部的不同类型数量没有限制。

(当涉及的类型来自封闭宇宙时,我有一个解决方案,但它很不愉快。)


看起来 polytypeablepolytypeable-utils 可以用于此 - 尽管在最坏的情况下仍需要实现完整的统一。 - Carl
1个回答

15

这可能不是最明智的方法,但我们可以滥用我的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)

考虑到我们现在可以查看Dynamic参数的TypeRep并适当实例化我们的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将所有内容转换回去。


2
关于 TypeRep 的欺骗是巧妙的,所以我会接受这个答案(使用 unsafeCoerce 来窥视和操作 Dynamic 不被视为明智的做法)。 - augustss
1
@AndreasRossberg 这很简单。:) 但前者更加健壮一些。例如,如果Dynamic的实现发生变化,使得TypeRepAny字段交换位置,前者仍然可以工作,而后者会崩溃。 - augustss
1
现在你不能编写自定义的Typeable实例,所以这不再起作用了,对吧? - GS - Apologise to Monica
那么,现在有什么解决方案吗?那个提议有进展吗? - Alpaca
2
在 GHC 8.0 中,将会有一个新的“适当”的 TypeRep,它实际上将包含类型中的“a”。这将使得许多这样的黑客技巧更容易且更安全。 - Edward Kmett
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接