我正在尝试编写Data.Typeable.gcast
的变体,它适用于类型* -> *
的类型系列。我正在寻找的是:
{-# LANGUAGE TypeFamilies #-}
import Data.Typeable
type family T
gcastT :: (Typeable a,Typeable b) => T a -> Maybe (T b)
gcastT = undefined -- but that's not working too well!
类似于gcast :: (Typeable a,Typeable b) => f a -> Maybe (f b)
。
这可能吗?
我可以将上下文更改为(Typeable (T a),Typeable (T b)) =>
,但出于审美原因,我更喜欢此签名:gcast
毕竟不需要Typeable1 f
。
一些背景,以防我正在解决我实际想要实现的问题:我的目标是编写一个函数matchAndExtract
:
matchAndExtract :: (Eq a, Typeable a, Eq b, Typeable b) => a -> b -> T b -> Maybe (T a)
matchAndExtract x k v = if (cast x == Just k) then gcastT v else Nothing
检查x
和k
是否属于同一类型并且相等,然后返回提供的T b
(我们知道它们与{{1}相同 - T可能不是单射的,但它是一个函数!)或T a
,否则。
我通过使用Nothing
将T a
包裹在newtype
中,然后再次展开来解决问题:
gcast
但这只会让我错误的方式!这也适用于matchAndExtractF :: (Eq a, Typeable a, Eq b, Typeable b) => a -> b -> f b -> Maybe (f a)
matchAndExtractF x k v = if (cast x == Just k) then gcast v else Nothing
newtype WrapT a = WrapT { unWrapT :: T a }
matchAndExtractViaWrap :: (Eq a, Typeable a, Eq b, Typeable b) => a -> b -> T b -> Maybe (T a)
matchAndExtractViaWrap x k v = fmap unWrapT $ matchAndExtractF a k (WrapT v)
不是T a
的实例的实例;在我看来,这似乎表明不应该需要Typeable
上下文。
解决方案完全可以接受,但我想摆脱假的Typeable (T a)
类型。
答案 0 :(得分:4)
您尝试做的事情是不可能实现的。相反,你可以使用
type family T x :: *
newtype NT x = NT {fromNT :: T x}
gcastT :: (Typeable a, Typeable b) => NT a -> Maybe (NT b)
gcastT = gcast
在这种情况下,您不需要使用Eq
约束。
另一种选择是将可键入的词典重新定义为GADT
data Type x where
Typeable :: Typeable x => Type x
asT :: NT x -> Type x -> NT x
asT = const
gcastTD :: Type a -> Type b -> Type a -> Maybe (T b)
gcastTD t@Typeable Typeable x = fmap fromNT $ gcastT $ (NT x) `asT` t
(代码未经过测试,但应该几乎正确)
一旦你有了这个,你可以通过传递显式类型签名来使用它
type instance T Int = ()
justUnit = gcastTD (Typeable :: Type Int) (Typeable :: Type Int) ()