当前位置: 首页 > 知识库问答 >
问题:

带约束的循环类型

盖向荣
2023-03-14
{-# 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 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.
bar :: Foo
bar = 
  let (x :: i,h) = foo
      (Hidden (p::Proxy i)) = h
  in Foo x

这将使约束在范围内,为foo提供额外的参数。这里我的意图是h包含一些(隐藏的)具体类型i,它应该用作多态函数GHC compulins的具体类型:

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同时计算ihiddentype2。隐藏类型的值涉及(至少部分)第一个元组元素的计算。这意味着我不想在bar中调用foo两次(一次是获取hiddentype,一次是使用该类型绑定第一个元组元素)。在foo存在约束的情况下,是否有某种方法使bar的定义成为可能?

共有1个答案

焦博实
2023-03-14

我认为问题是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)
list1 :: forall t. Typeable t => [t]
list2 :: [forall t. Typeable t => t]

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

foo的非谓词版本的核心也有点不同:

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)
 类似资料:
  • 在表之间添加约束意味着使用 sequelize.sync 时必须在数据库中以一定顺序创建表. 如果 Task 具有对 User 的引用,则必须先创建 User 表,然后才能创建 Task 表. 有时这可能会导致循环引用,而 Sequelize 无法找到同步的顺序. 想象一下文档和版本的情况. 一个文档可以有多个版本,为方便起见,文档引用了其当前版本. const { Sequelize, Mode

  • 我在typescript中有以下泛型类 但是我不知道为什么得到这个错误Class'(匿名类)'不正确地扩展基类'列'。属性getValue的类型不兼容。类型'(值:数字)=

  • 我有一系列复杂的类型级别函数,它们的计算结果如下: 显然,在这种情况下,这个表达式是一个。更一般地说,我们可以说: 有没有办法教GHC推断这一点? 编辑:@chi指出,在某些情况下,GADT可以解决这一问题,但我的特殊情况是: 然后 不能被访问,但是也许GHC应该能够推断出

  • 销关节 cpPinJoint *cpPinJointAlloc(void) cpPinJoint *cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) cpConstraint *cpPinJointNew(cpBody *a, cpBody *b, cpVect anchr1,

  • 有效的解决方案路由的约束是: 考虑到第二个约束,路由中遍历的所有边的权重之和必须是图中可能的最高值。 所选路由中必须访问过N个顶点(包括起始和结束顶点)。 通常,图形将有大量的顶点和边,所以尝试所有的可能性是不可能的,需要相当有效的算法。

  • 泛型的类型约束 swapTwoValues(_:_:)函数和Stack类型可以用于任意类型. 但是, 有时在用于泛型函数的类型和泛型类型上, 强制其遵循特定的类型约束很有用. 类型约束指出一个类型形式参数必须继承自特定类, 或者遵循一个特定的协议、组合协议. 例如, Swift的Dictionary类型在可以用于字典中键的类型上设置了一个限制. 如字典中描述的一样,字典键的类型必须是可哈希的. 也