我正在尝试编写一个函数(此处称为hide
),它可以在存在的包装器中应用足够多态的函数(或者提升函数来处理具有隐藏类型的包装器;因此“隐藏”):
{-# LANGUAGE GADTs
, RankNTypes
#-}
data Some f
where Some :: f a -> Some f
hide :: (forall a. f a -> g b) -> Some f -> Some g
hide f (Some x) = Some (f x)
data Phantom a = Phantom
cast :: Phantom a -> Phantom b
cast Phantom = Phantom
works :: Some Phantom -> Some Phantom
works = hide cast
doesn't :: Functor f => Some f -> Some f
doesn't = hide (fmap $ \x -> [x])
{-
foo.hs:23:17:
Couldn't match type ‘b0’ with ‘[a]’
because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: f a -> f b0
at foo.hs:23:11-33
Expected type: f a -> f b0
Actual type: f a -> f [a]
In the first argument of ‘hide’, namely ‘(fmap $ \ x -> [x])’
In the expression: hide (fmap $ \ x -> [x])
In an equation for ‘doesn't’: doesn't = hide (fmap $ \ x -> [x])
Failed, modules loaded: none.
-}
but :: Functor f => Some f -> Some f
but = hide' (fmap $ \x -> [x])
where hide' :: (forall a. f a -> g [a]) -> Some f -> Some g
hide' f (Some x) = Some (f x)
所以我非常理解为什么会这样; works
显示hide
确实在返回类型与输入类型完全无关时工作,但在doesn't
中我使用hide
类型的参数调用a -> [a]
{1}}。 hide
应该“选择”类型a
(RankNTypes
),但b
通常是多态的。当b
实际上取决于a
时,a
可能会泄漏。
但是在我实际调用它的上下文中,a
实际上并没有泄露出来;我立即将其包裹在Some
中。事实上,我可以编写一个备用hide'
来接受专门的a -> [a]
函数,并使用完全相同的实现工作,只是一个不同类型的签名。
我有什么方法可以输入实现hide f (Some x) = Some (f x)
以便它更常用吗?我真的对提升a -> q a
类型的函数感兴趣,其中q
是一些任意类型的函数;即我希望返回类型取决于a
,但我不关心它是如何做到的。可能存在q a
是常量的用例(即返回类型不依赖于a
),但我认为它们会更加罕见。
这个例子显然非常愚蠢。在我的实际用例中,我有一个GADT Schema a
,粗略地说是外部类型系统中的类型; phantom参数提供了一个Haskell类型,可用于表示外部类型系统中的值。我需要使用phantom参数来保证所有类型的安全,但有时我会根据运行时数据构造Schema
,在这种情况下我不知道该参数类型是什么。
所以我似乎需要另一种与type参数无关的类型。我希望使用像Some
之类的简单存在性包装器来构建Schema
,并且能够将forall a. Schema a -> Schema b
类型的函数提升为{{}}。 {1}}。因此,如果我遇到XY问题并且我更好地使用其他方法传递Some Schema -> Some Schema
来表示未知Schema a
,那么这也可以解决我的问题。
答案 0 :(得分:4)
正如 David Young 所说,你可以写
hide' :: (forall a. f a -> g (q a)) -> Some f -> Some g
hide' f (Some x) = Some (f x)
does :: Functor f => Some f -> Some f
does = hide' (fmap (:[]))
但不是像hide
fmap那样,你可以使它像绑定一样:
hide'' :: (forall a. f a -> Some g) -> Some f -> Some g
hide'' f (Some x) = f x
does :: Functor f => Some f -> Some f
does = hide'' (Some . fmap (:[]))
但这有点可以理解。
或者,更一般地说
elim :: (forall a. f a -> c) -> Some f -> c
elim f (Some x) = f x
答案 1 :(得分:2)
我不确定这对您的大型用例有多大用处,因为您必须重构所有现有操作以使用延续传递样式,但是可以使用continuation来实现有效的hide
对于这两个示例并保持b
完全通用。
hide :: (forall r a. f a -> (forall b. g b -> r g) -> r g) -> Some f -> Some g
hide f (Some x) = f x Some
cast :: Phantom a -> (forall b. Phantom b -> r Phantom) -> r Phantom
cast Phantom f = f Phantom
works :: Some Phantom -> Some Phantom
works = hide cast
alsoWorks :: Functor f => Some f -> Some f
alsoWorks = hide (\a f -> f $ fmap (\x -> [x]) a)
通过分析CPS转换可以让您更轻松地使用原始cast
之类的现有功能:
hide :: (forall r a. f a -> (forall b. g b -> r g) -> r g) -> Some f -> Some g
hide f (Some x) = f x Some
cps :: (f a -> g b) -> (f a -> (forall c. g c -> r) -> r)
cps f a c = c (f a)
cast :: Phantom a -> Phantom b
cast Phantom = Phantom
works :: Some Phantom -> Some Phantom
works = hide $ cps cast
alsoWorks :: Functor f => Some f -> Some f
alsoWorks = hide $ cps $ fmap (\x -> [x])