假设我有一个隐藏在其中的a
类型的标准proxy:
pxyA = Proxy :: Proxy a
由于我的程序的其他部分,我知道a
实际上是另外两种类型的元组,比如(b,c)
。有没有办法让我从原始代理中提取该信息,其功能如下:
f :: Proxy a -> (Proxy b, Proxy c)
或
f :: Proxy a -> Proxy (b,c)
阻止我的主要原因是我不知道b
或c
类型是什么,只是我需要将它们传递给程序的其他部分。
我已经用类似的形式编写了一个简单的函数:
splitPxyTup :: forall a b . Proxy (a,b) -> (Proxy a, Proxy b)
splitPxyTup _ = (Proxy :: Proxy a, Proxy :: Proxy b)
但是如何说服类型系统我的原始代理实际上是一个元组类型仍然迷失方向。
我还考虑使用cast
,但由于我不知道输出类型是什么,我将无法从中获得任何有意义的信息。
答案 0 :(得分:1)
我一直试图看看你所追求的是什么,但是如果能够产生一个代表你问题的更完整的例子会有所帮助。
在此之前,我似乎对你问题的这一部分有了答案
阻止我的主要原因是我不知道
b
或c
类型将是什么......
从那句话中听起来你需要一个存在主义:
proxyTuple :: Proxy a -> exists b c. Proxy (b,c)
这是无效的Haskell,但我们可以轻松编码:
data ProxyTupleEx where
ProxyTupleEx :: Proxy (b,c) -> ProxyTupleEx
但这还不够,因为我们可以轻松地实现这个功能。
proxyTuple :: Proxy a -> ProxyTupleEx
proxyTuple _ = ProxyTupleEx (Proxy :: Proxy ((), ()))
我们还需要指定与传入a
的连接。我们可以这样做:
data ProxyTupleEx a where
ProxyTupleEx :: (a ~ (b,c)) => Proxy (b,c) -> ProxyTupleEx a
我们现在无法实现proxyTuple
,因为当a
不是元组时它没有实现。我们需要
proxyTuple :: Proxy a -> Maybe (ProxyTupleEx a)
给它机会失败。
现在我们到达你的other question about reflection。我们需要添加Typeable
约束,以便我们可以反映类型,但是一旦习惯了库的节奏,它就相当简单了:
proxyTuple :: Typeable a => Proxy a -> Maybe (ProxyTupleEx a)
proxyTuple p
| App _proxy (App (App tuple a) b) <- typeOf p
, Just HRefl <- eqTypeRep tuple (typeRep :: TypeRep (,))
= Just (ProxyTupleEx p)
| otherwise
= Nothing
现在,如果你在ProxyTupleEx a
上进行模式匹配,编译器就会知道a
实际上是一个元组。
tupleLength :: (a,b) -> Int
tupleLength _ = 2
example :: Typeable a => Proxy a -> a -> Int
example proxy x
| Just (ProxyTupleEx _) <- proxyTuple proxy = tupleLength x
-- x is now known to be a tuple
-- so this typechecks
| otherwise = 0
就像魔法一样,这种类型检查和工作:
ghci> example Proxy (1,2)
2
ghci> example Proxy 0
0
(此处Proxy
也不必要,因为我们已经有a
的类型,但为了与查询保持一致,我将其包含在内。)
答案 1 :(得分:0)
考虑到你的问题可用的上下文有限,这似乎很容易写。只要您能够提供a ~ (b, c)
声称的具体证据,您只需要一些Data.Type.Equality
机制来说服GHC。
{-# LANGUAGE GADTs, TypeOperators #-}
import Data.Proxy
import Data.Type.Equality
splitProxyTuple :: a :~: (b, c) -> Proxy a -> Proxy (b, c)
splitProxyTuple Refl = castWith Refl
并在GHCI会议中使用
> type T = (Int, Char)
> type F = (Int, Bool)
> let prf = Refl :: (T :~: (Int, Char))
> let prx = Proxy :: Proxy T
> :t splitProxyTuple prf prx
splitProxyTuple prf prx :: Proxy (Int, Char)
=> Proxy
> :t splitProxyTuple prf (Proxy :: Proxy F)
• Couldn't match type ‘Bool’ with ‘Char’
Expected type: Proxy T
Actual type: Proxy F
• In the second argument of ‘splitProxyTuple’, namely
‘(Proxy :: Proxy F)’
In the expression: splitProxyTuple prf (Proxy :: Proxy F)