2014-11-24 57 views
2

在下面的示例中,我试图使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) 

我真的需要foo一个Typeable约束:

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 2.同时计算iHiddenType 2.隐藏类型的值涉及(至少部分)第一个元组元素的计算。这意味着我做而不是想在bar中拨打foo两次(一次获得HiddenType,并且一次使用该类型绑定第一个元组元素)。 在foo上存在约束时,是否有某种方法可以使bar的定义成为可能?

+2

你究竟想要做什么?这很少有意义。特别是最后一种情况,你试图用''Hidden'内部的其他类型来统一类型变量 - 恰恰与你可以用存在类型做什么相反。第一种情况不起作用,因为您需要选择一个实例,以便可以解析'Typeable i'约束,例如'let(x,h)= foo'必须是'let(x,h)= foo: :(Int,HiddenType)',但这不起作用,因为你不能从'Hidden'内部神奇地产生一个进入。 – user2407038 2014-11-24 05:54:05

+2

你可能试图实现'Data.Dynamic'吗? – user2407038 2014-11-24 05:54:23

+0

@ user2407038听起来很像'Data.Dynamic'。这个例子*真的被切断了,但是它的核心问题是关于AST中的变量类型。 'foo'就像一个'lambda',其中隐藏类型是变量的(预期)类型。 'bar'就像'let',它需要lambda变量的类型,但是隐藏了那个类型。 – crockeea 2014-11-24 14:20:49

回答

3

我想问题是foo的返回值实际上并不是多态。 foo本身是多态的,但返回值必须存在于特定类型中。不幸的是,您要使用的类型尚不可用,并且由于有循环参考,因此无法在foo的呼叫站点处列入范围。如果我们写出来的伪核心的foo定义,这个问题是清楚的:

foo (@ iType) _ = (undefined @ iType, HiddenType...) 

这里@ iType是一种说法。在获取HiddenType之前,我们需要首先完成foo的类型应用程序(和字典应用程序,这是未使用的),因此无法按原样执行此操作。

幸运的是,有办法说服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类型的所有值,对于一些tTypeable约束列表的类型。即使它是多态的,列表中的每个元素都将是相同的类型!相反,如果你要移动的forall名单内,让每一个元件可以是不同类型的tImpredicativeTypes将允许这样的:

list2 :: [forall t. Typeable t => t] 

这不是一个支持常用扩展,但它是偶尔有用。

foo的impredicative版本的核心是有点不同,以及:

foo = (\(@ iType) _ -> undefined @ iType, HiddenType...) 

你可以看到,这允许您添加注释到letx是为所需多:

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模式匹配下退出。

+1

'forall i。可打字的i => i'只有'undefined'和'list2 :: [forall t。只能用[]键入t => t]',所以我看不出这是有用的。你当然不应该将ImpredictiveTypes与RankNTypes类似 - 后者不能以任何方式破坏工作程序,而前者几乎肯定会这样做。 ImpredictiveTypes是邪恶的,应该避免。 – user2407038 2014-11-24 05:59:06

+0

@ user2407038:当然,这对于'Typeable'并没有什么用处,但我想这是一个严格削减的OP例子。除非必要,否则我不会启用“ImpredicativeTypes”,但它似乎正是OP在此处询问的内容。另外,您能否提供一个打破工作计划的扩展示例? GHC本身不会推断它,所以只要启用“ImpredicativeTypes”似乎不会导致许多问题,除非是人为的例子。 – 2014-11-24 06:20:51

+0

此外,impredicative多态只是一个更高级别类型的概括。想想隐含类型的最好方法是明确地操作量词(并且这是在Haskell中使用它们的唯一方法),此时这种关系是显而易见的。 – 2014-11-24 10:05:24