【问题标题】:`forall {..}` in GHC 9GHC 9 中的`forall {..}`
【发布时间】:2022-02-03 21:33:09
【问题描述】:

这是 GHC 9 中的有效语法。{..} 是什么意思(与 GHC 8.10 在这里要求的 (..) 不同)?

ign :: forall {f :: Type -> Type} {p}. Applicative f => p -> f ()
ign _ = pure ()

【问题讨论】:

标签: haskell syntax forall


【解决方案1】:

6.4.14.1. Inferred vs. specified type variables:

从 9.0.1 版本开始,GHC 允许将用户编写的类型或种类变量标记为 inferred,这与默认的 specified 不同。通过将大括号中的类型变量绑定器写入{tyvar}{tyvar :: kind},新变量将被分类为推断,而不是指定

  • .. 是一个指定的类型
  • {..} 是一个推断类型

forall a.forall {a}. 是 GHC 将通过统一自动实例化的不可见量词。

const :: forall a b. a -> b -> a
const a _ = a

这意味着 const True EQ 在没有用户帮助的情况下实例化 a (@Bool) 和 b (@Ordering)。

如果用户想要显式实例化它们,他们可以使用可见类型应用程序“覆盖其可见性”。这就是为什么它们是指定类型。 (尽管“特定able”可能是更准确的术语)

>> :set -XTypeApplications
>> :t const @Bool @Ordering True EQ
const @Bool @Ordering True EQ :: Bool

如果出于某种原因我们只想指定 b(不召唤“蜗牛小队”:@_, umm "partial type signatures")我们可以制作 a 推断类型。然后第一种类型被丢弃

const2 :: forall {a} b. a -> b -> a
const2 a _ = a

>> :t const2 @Ordering True EQ
const2 @Ordering True EQ :: Bool

对于您的示例,这意味着 ghc 必须推断 fp 的类型。你不能写ign @IO @Int


当你有善良的多态性时,这会变得更有用。如果你定义

-- MkApply @Type @[] @Int :: [Int] -> Apply @Type [] Int
-- MkApply @_    @[] @Int :: [Int] -> Apply @Type [] Int
type    Apply :: forall (k :: Type). (k -> Type) -> (k -> Type)
newtype Apply f a where
  MkApply :: forall (k :: Type) (f :: k -> Type) (a :: k). f a -> Apply @k f a

在实例化MkApply @Type @[] @Int 时必须指定类型k,但[]Int 都隐含了这种类型。您可能更喜欢将 k 标记为 MkApply 中的推断,以便您可以编写 MkApply @[] @Int

-- MkApply @[] @Int :: [Int] -> Apply @Type [] Int
type    Apply :: forall (k :: Type). (k -> Type) -> (k -> Type)
newtype Apply f a where
  MkApply :: forall {k :: Type} (f :: k -> Type) (a :: k). f a -> Apply @k f a

【讨论】:

  • 从广义上讲,我认为应该尽可能推断类型变量。这与默认值相反,这是不幸的。例如,GHC.Generics.Generic1 采用指定的种类参数,因此 to1from1 也一样。糟糕的用户体验。
  • 我同意这一点,它阻止了我使用指定类型 (type Category :: forall ob. Cat ob -> Constraint) 使 Category 类型具有多态性。这会将方法中 ob 的量化从推断更改为指定,因此您必须编写 id @Type @(->) @Int :: Int -> Int。如果在种类中指定,则无法指定方法中推断的 {ob}。我不喜欢这种情况,当我添加 Generically1 时,我不得不使用 GADT 语法并量化 Generically1 :: forall {k} f a. f a -> Generically1 @k f a 构造函数中的所有变量
  • 我不确定最好的默认值是什么,但没有办法为这些方法覆盖它!我不知道这是否已经改变;有一个顶级方法签名的提议,您可以在其中编写id :: forall {ob :: Type} (cat :: Cat ob) (a :: ob) :: Category @ob cat => cat a a。我不喜欢在类声明之外复制签名,但至少它允许微调它!
  • 啊,现在我明白了。您想为该类指定一个不可推断的种类参数,但避免为该方法指定相同的、现在可推断的参数。有道理。
  • 是的,可见性可能会发生类似的事情(将来)。 sizeOf :: forall a. Sizeable a => a -> Int 实际上并没有使用a 参数,因此它可以与sizeOf (undefined :: A) 一起提供。如果参数被删除,我们可以使用类型应用程序sizeOf :: forall a. Sizeable a => Int 指定它,但使用可见量化更有意义:forall a -> Sizeable a => Int。但即使使用forall->,您如何明显地量化一种方法?类的类型参数已经在方法范围之外被隐式量化。身份证。
猜你喜欢
  • 2011-11-05
  • 1970-01-01
  • 2012-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多