如果我有v
我可以致电
cast :: a - >也许b
所以我可以选择a
并在演员表演成功时取回b
(我已经忽略了Typeable
约束的细节。)
然而,我有一个更糟糕的案例。我有一个Value x y
类型。我只关心第一个类型参数,而不是第二个。所以我真的希望能够将v
转换为Value x y2
并在第二个参数中自由(我的其余代码就可以了)。
我怎样才能做到这一点?要明确的是,此时我不知道v
是否属于任何种类的Value
,更不用说Value x
了。
答案 0 :(得分:4)
cast
单独不能这样做,因为它需要知道所有变量的类型,但它的实现非常简单,所以你可以编写自己的变量。 cast
次检查a
和b
类型相同,如果匹配则为unsafeCoerce
。
您可以使用typeRepCon
执行某些操作。这会将TypeRep
解构为类型构造函数以及使其成为参数的类型列表。首先匹配类型构造函数以确保它是Value
,然后检查y
是否相同。如果一切都匹配,您可以unsafeCoerce
。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ImpredicativeTypes #-}
import Unsafe.Coerce
import Data.Typeable
data Value x y = Value x y
deriving Typeable
data Box where
Box :: Typeable a => a -> Box
value :: Value Int Char
value = Value 3 'a'
box :: Box
box = Box value
box2 :: Box
box2 = Box 'Z'
getValueY :: forall a y. (Typeable a, Typeable y) => a -> Maybe (forall x. Value x y)
getValueY a
| con == typeRepTyCon (typeOf value) && y == typeRep (Proxy :: Proxy y)
= Just $ unsafeCoerce a
| otherwise = Nothing
where
(con, ~[_x, y]) = splitTyConApp (typeOf a)
valueChar :: Value x Char -> Char
valueChar (Value _ c) = c
boxChar :: Box -> Maybe Char
boxChar (Box b) =
case getValueY b of
Just v -> Just (valueChar v)
Nothing -> Nothing
给出了
>>> getY box
Just 'a'
>>> getY box2
Nothing
impredicative类型(forall x. Value x y
)在这里是为了确保你不能选择x是什么,否则只要y
匹配你就可以做任何你想做的事。另一个(可能更好的)解决方案是制作另一个包装器,如
data YValue y where
YValue :: Value x y -> YValue y
deriving Typeable
很多麻烦来自于不知道它是Value
,否则你可以使用gcast
。
修改:您可以通过ImpredicativeTypes
函数完全避免forall x. Value x y -> b
:
withValueY :: forall a y b. (Typeable a, Typeable y)
=> (forall x. Value x y -> b) -> a -> Maybe b
withValueY f a
| con == typeRepTyCon (typeOf value) && y == typeRep (Proxy :: Proxy y)
= Just $ f (unsafeCoerce a)
| otherwise = Nothing
where
(con, ~[_x, y]) = splitTyConApp (typeOf a)
boxChar :: Box -> Maybe Char
boxChar (Box b) = withValueY valueChar b