在下面的示例中,我试图让foo
返回其预期的"多态输出类型。我们的想法是foo
返回一个多态值和一个存在类型,然后bar
指定元组的类型为隐藏类型。 (当然,只有在bar
中的类型也是存在的时才有效,在我的情况下也是如此。)以下示例编译:
{-# LANGUAGE GADTs, ScopedTypeVariables #-}
module Foo where
import Data.Proxy
import Data.Typeable
data HiddenType where
Hidden :: (Typeable a) => Proxy a -> HiddenType
foo :: (i,HiddenType)
foo = (undefined, Hidden (Proxy::Proxy Int))
data Foo where
Foo :: i -> Foo
bar :: Foo
bar =
let (x,h) = foo
in case h of
(Hidden (p::Proxy i)) -> Foo (x :: i)
我确实需要Typeable
上的foo
约束:
foo :: (Typeable i) => (i,HiddenType)
当我添加该约束(没有其他更改)时,我会收到以下错误:
Foo.hs:20:15:
No instance for (Typeable t0) arising from a use of ‘foo’
The type variable ‘t0’ is ambiguous
Relevant bindings include x :: t0 (bound at Foo.hs:20:8)
Note: there are several potential instances:
instance [overlap ok] Typeable ()
-- Defined in ‘Data.Typeable.Internal’
instance [overlap ok] Typeable Bool
-- Defined in ‘Data.Typeable.Internal’
instance [overlap ok] Typeable Char
-- Defined in ‘Data.Typeable.Internal’
...plus 14 others
In the expression: foo
In a pattern binding: (x, h) = foo
In the expression:
let (x, h) = foo
in case h of { (Hidden (p :: Proxy i)) -> Foo (x :: i) }
Foo.hs:22:35:
Couldn't match expected type ‘a’ with actual type ‘t0’
because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor
Hidden :: forall a. Typeable a => Proxy a -> HiddenType,
in a case alternative
at Foo.hs:22:6-24
Relevant bindings include
p :: Proxy a (bound at Foo.hs:22:14)
x :: t0 (bound at Foo.hs:20:8)
In the first argument of ‘Foo’, namely ‘(x :: i)’
In the expression: Foo (x :: i)
Failed, modules loaded: none.
我理解约束在核心中变成了参数,所以在我看来,这里的问题是GHC不能处理GADT的模式绑定。如果可以的话,我可以用递归的方式来表达:
bar :: Foo
bar =
let (x :: i,h) = foo
(Hidden (p::Proxy i)) = h
in Foo x
这应该在范围内制定约束,为foo
提供额外的参数。我的意图是h
包含一些(隐藏的)具体类型i
,它应该用作多态函数的具体类型
GHC抱怨:
Foo.hs:19:8:
You cannot bind scoped type variable ‘i’
in a pattern binding signature
In the pattern: x :: i
In the pattern: (x :: i, h)
In a pattern binding:
(x :: i, h) = foo
Foo.hs:20:8:
My brain just exploded
I can't handle pattern bindings for existential or GADT data constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.
In the pattern: Hidden (p :: Proxy i)
In a pattern binding: (Hidden (p :: Proxy i)) = h
In the expression:
let
(x :: i, h) = foo
(Hidden (p :: Proxy i)) = h
in Foo x
我的用例的假设是
1. foo
同时计算i
和HiddenType
2.隐藏类型的值涉及(至少部分)第一元组元素的计算。这意味着我 not 想要在foo
中两次调用bar
(一次获取HiddenType
,一次使用该类型绑定第一个元组元素)。
是否有某种方法可以在bar
存在约束时使foo
的定义成为可能?
答案 0 :(得分:3)
我认为问题是foo
的返回值实际上并不是多态的。 foo
本身是多态的,但返回的值必须存在于特定类型。遗憾的是,由于循环引用,您要使用的类型尚不可用,并且无法在foo
呼叫站点进入范围。如果我们在伪核心中写出foo
的定义,问题很明显:
foo (@ iType) _ = (undefined @ iType, HiddenType...)
这里@ iType
是一个类型参数。我们需要先获取foo的类型应用程序(以及未使用的字典应用程序),然后才能获得HiddenType
,因此无法按原样使用此工作。
幸运的是,有一种方法可以说服ghc foo
应该返回一个实际的多态值:
{-# LANGUAGE GADTs, ScopedTypeVariables #-}
{-# LANGUAGE ImpredicativeTypes #-}
module Foo where
import Data.Proxy
import Data.Typeable
data HiddenType where
Hidden :: (Typeable a) => Proxy a -> HiddenType
foo :: (forall i. Typeable i => i,HiddenType)
foo = (undefined, Hidden (Proxy::Proxy Int))
data Foo where
Foo :: i -> Foo
bar =
let (x,h) = foo
in case h of
Hidden p -> Foo (x `asProxyTypeOf` p)
如果您熟悉较高排名类型(例如RankNTypes
扩展名),则可以将ImpredicativeTypes
视为类似的except for data structures instead of functions。例如,如果没有ImpredicativeTypes
,您可以写:
list1 :: forall t. Typeable t => [t]
对于包含t
约束的某些t
,包含Typeable
类型的所有值的列表类型。即使它是多态的,列表中的每个元素都是相同的类型!如果您希望移动列表中的forall
,以便每个元素可以是不同的类型t
,ImpredicativeTypes
将允许:
list2 :: [forall t. Typeable t => t]
这不是常用的扩展程序,但偶尔会有用。
foo
的impredicative版本的核心也有点不同:
foo = (\(@ iType) _ -> undefined @ iType, HiddenType...)
如果您向x
添加注释,您可以看到这允许let
根据需要变为多态:
bar :: Foo
bar =
let (x :: forall i. Typeable i => i,h) = foo
in case h of
Hidden p -> Foo (x `asProxyTypeOf` p)
这允许您延迟隐藏类型x
的实例化,直到稍后它可用。您仍然需要在Foo
或其他Hidden
内将其打包,因为ghc不允许该类型在第一个Hidden
模式匹配下逃脱。