命令2在Haskell投下

时间:2015-03-26 21:53:13

标签: haskell

如果我有v我可以致电

cast :: a - >也许b

所以我可以选择a并在演员表演成功时取回b(我已经忽略了Typeable约束的细节。)

然而,我有一个更糟糕的案例。我有一个Value x y类型。我只关心第一个类型参数,而不是第二个。所以我真的希望能够将v转换为Value x y2并在第二个参数中自由(我的其余代码就可以了)。

我怎样才能做到这一点?要明确的是,此时我不知道v是否属于任何种类的Value,更不用说Value x了。

1 个答案:

答案 0 :(得分:4)

cast单独不能这样做,因为它需要知道所有变量的类型,但它的实现非常简单,所以你可以编写自己的变量。 cast次检查ab类型相同,如果匹配则为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