簡體   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