循环键入约束

在下面的例子中,我试图让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同时计算iHiddenType 2.隐藏类型的值涉及(至少部分)第一个元组元素的计算。 这意味着我不想在bar调用foo两次(一次获取HiddenType ,一次使用该类型绑定第一个元组元素)。 有没有办法在存在foo约束的情况下使bar的定义成为可能?


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

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

这里@ iType是一个类型参数。 在获取HiddenType之前,我们需要首先执行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看作类似的东西,除了数据结构而不是函数。 例如,没有ImpredicativeTypes你可以写:

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

这是包含t类型所有值的列表的类型,对于具有Typeable约束的某些t 。 即使它是多态的,列表中的每个元素都将是相同的类型! 相反,如果你要移动的forall名单内,让每一个元件可以是不同类型的tImpredicativeTypes将允许这样的:

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

这不是一个通常启用的扩展,但它偶尔有用。

foo的impandicative版本的核心也有点不同:

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

你可以看到,如果你给let添加一个注解,这就允许x是多态的:

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

链接地址: http://www.djcxy.com/p/7455.html

上一篇: Circular Typing with Constraints

下一篇: Apply polymorphic function in a GHC plugin