繁体   English   中英

如何将Dynamic转换为Forall的东西

[英]How to convert Dynamic to Forall something

我有一个动态值缓存。 其中一些类型为Delayed a

通常当我访问缓存时,我知道类型a ,所以这不是问题,我可以使用fromDynamic转换为Maybe a

我想调用一个函数,它不需要知道关于Dynamic列表中的类型a任何信息。 (方法是cancel :: Delay a -> IO () )。 有办法吗?

基本上我需要一种从DynamicForall a . Delayed a Forall a . Delayed a

编辑

有关信息,Delayed保持挂起的异步值和MVar以启动或取消它。 它相当于

 data Delayed m a = Delayed { blocker :: MVar Bool, async :: Async m a }

这些值存储在缓存中(使用Dynamic并存储其他内容)。 当显示缓存状态时,我需要能够获得Delayed值的状态(涉及访问阻止程序但与实际值无关)。

值类型为forall a . X a forall a . X a是一个值,可以实例化为X IntX BoolX String等中的任何一个。据推测,您的缓存存储了许多不同类型的值,但没有一个值对每个可能的类型参数都有效。 你真正需要的是一个类型的值exists a . Delayed a exists a . Delayed a 但是,Haskell没有一流的存在量词,因此您必须以某种方式编码该类型。 一种特殊的编码是:

castToDelayed :: (forall a . Typeable a => Delayed a -> r) -> Dynamic -> r

假设你有这个功能; 那么你可以简单地编写castToDelayed cancel :: Dynamic -> IO () 请注意, castToDelayed的函数参数提供了一个Typeable约束,但您可以自由地忽略该约束(这是cancel正在执行的操作)。 另请注意,由于单独的类型,此函数必须是部分的(显然并非每个Dynamic都是某个a的Delayed a a ),因此在实际代码中,您应该生成例如Maybe r 在这里,我将忽略这个细节,只是抛出一个错误。

您实际编写此函数的方式取决于您使用的GHC版本(最新版本,8.2版本或某些旧版本)。 在8.2,这是一个非常好的,简单的功能:

{-# LANGUAGE ViewPatterns #-}
-- NB: probably requires some other extensions

import Data.Dynamic
import Type.Reflection

castToDelayed :: (forall a . Typeable a => Delayed a -> r) -> Dynamic -> r
castToDelayed k (Dynamic (App (eqTypeRep (typeRep :: TypeRep Delayed) -> Just HRefl) a) x)
  = withTypeable a (k x) 
castToDelayed _ _ = error "Not a Delayed"

(旁白:起初我认为Con模式同义词在这里很有用,但是在更深入的检查中它似乎完全无用。你必须使用eqTypeRep 。)

简而言之,此功能的工作原理如下:

  • 它对Dynamic值进行模式匹配,以获得存储在其中的实际值(某些存在量化的类型a ),以及其类型的表示(类型为TypeRep a )。

  • 它在TypeRep a上模式匹配以确定它是否是应用程序(使用App )。 显然, Delayed a是类型构造函数的应用程序,因此这是我们必须检查的第一件事。

  • 它将类型构造函数( App的第一个参数)与对应于DelayedTypeRep进行比较(注意,您必须instance Typeable Delayed设置一个instance Typeable Delayed )。 如果该比较成功,则它在证明(即Just HRefl )上的模式匹配,即AppDelayed的第一个参数实际上是相同的类型。

  • 此时,编译器知道某个x a ~ Delayed x x 所以,你可以调用函数forall a . Typeable a => Delayed a -> r forall a . Typeable a => Delayed a -> r在值x :: aforall a . Typeable a => Delayed a -> r 它还必须提供证据,证明xTypeable ,这是类型的值精确给出TypeRep x - withTypeable具体化这个值水平证明作为一个类型级约束(或者,你可以有输入功能需要作为参数TypeRep a ,或者只是完全省略约束,因为您的特定用例不需要它;但这种类型是最普遍的可能)。

在旧版本中,原理基本相同。 但是, TypeRep当时没有采用类型参数; 你可以在它的模式匹配,发现如果是TypeRep相应Delayed ,但你不能证明存储内部值编译器Dynamic的类型是Delayed x的一些x 因此,在将函数k应用于值x的步骤中,它将需要unsafeCoerce 此外,在GHC 8.2之前没有withTypeable ,所以你必须编写带有类型的函数(forall a . Delayed a -> r) -> Dynamic -> r (幸运的是,这对你的用例来说已经足够了); 或者自己实现这样的函数(请参阅函数的来源以了解如何;在旧版GHC上的实现将类似,但将具有类型TypeRep -> (forall a . Typeable a => Proxy a -> r) -> r反而)。

以下是GHC <8.2(在8.0.2上测试)中的实现方法。 这是一个可怕的黑客,我没有声称它会在任何情况下都正确。

{-# LANGUAGE DeriveDataTypeable, MagicHash, ScopedTypeVariables, PolyKinds, ViewPatterns #-}

import Data.Dynamic
import Data.Typeable
import Unsafe.Coerce
import GHC.Prim (Proxy#)
import Data.Proxy

-- This part reifies a `Typeable' dictionary from a `TypeRep'.
-- This works because `Typeable' is a class with a single field, so 
-- operationally `Typeable a => r' is the same as `(Proxy# a -> TypeRep) -> r'
newtype MagicTypeable r (kp :: KProxy k) =
  MagicTypeable (forall (a :: k) . Typeable a => Proxy a -> r)

withTypeRep :: MagicTypeable r (kp :: KProxy k)
            -> forall a . TypeRep -> Proxy a -> r
withTypeRep d t = unsafeCoerce d ((\_ -> t) :: Proxy# a -> TypeRep)

withTypeable :: forall r . TypeRep -> (forall (a :: k) . Typeable a => Proxy a -> r) -> r
withTypeable t k = withTypeRep (MagicTypeable k) t Proxy

-- The type constructor for Delayed
delayed_tycon = fst $ splitTyConApp $ typeRep (Proxy :: Proxy Delayed)

-- This is needed because Dynamic doesn't export its constructor, and
-- we need to pattern match on it.
data DYNAMIC = Dynamic TypeRep Any

unsafeViewDynamic :: Dynamic -> DYNAMIC
unsafeViewDynamic = unsafeCoerce

-- The actual implementation, much the same as the one on GHC 8.2, but more 
-- 'unsafe' things
castToDelayed :: (forall a . Typeable a => Delayed a -> r) -> Dynamic -> r
castToDelayed k (unsafeViewDynamic -> Dynamic t x) =
  case splitTyConApp t of
    (((== delayed_tycon) -> True), [a]) ->
      withTypeable a $ \(_ :: Proxy (a :: *)) -> k (unsafeCoerce x :: Delayed a)
    _ -> error "Not a Delayed"

我不知道实际上是什么Delayed ,但我们假设它的定义如下,用于测试目的:

data Delayed a = Some a | None deriving (Typeable, Show)

然后考虑这个简单的测试用例:

test0 :: Typeable a => Delayed a -> String
test0 (Some x) = maybe "not a String" id $ cast x
test0 None     = "None"

test0' =
  let c = castToDelayed test0 in
   [ c (toDyn (None :: Delayed Int))
   , c (toDyn (Some 'a'))
   , c (toDyn (Some "a")) ]

为什么不定义

{-# LANGUAGE ExistentialQuantification #-}

data Delayed' = forall a. Delayed' (Delayed a)

然后存储比在Dynamic 然后,您可以cast其从动态的casecancel ,并将结果传递给cancel (根据您的使用情况,您甚至可能不再需要Dynamic 。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM