为什么关联类型同义词不暗示约束

Why doesn't associated type synonym imply the constraint

为什么在签名中使用关联类型同义词不暗示相应的约束?

例如为什么不编译以下内容:

{-# LANGUAGE TypeApplications, ScopedTypeVariables, TypeFamilies, AllowAmbiguousTypes #-}

class MyClass a where
  type AssociatedType a 
  bar :: Int

foo :: forall a. AssociatedType a -> Int
foo _ = bar @a

ghc 8.6.5 出现以下错误:

error:
    * No instance for (MyClass a) arising from a use of `bar'
      Possible fix:
        add (MyClass a) to the context of
          the type signature for:
            foo :: forall a. AssociatedType a -> Int
    * In the expression: bar @a
      In an equation for `foo': foo _ = bar @a
  |
8 | foo _ = bar @a
  |         ^^^^^^

GHC 文档好像没有提到这方面。

因为只使用类型不需要类型约束。例如,编译:

foo :: forall a. AssociatedType a -> Int
foo _ = 42

在运行时,不需要将类型类字典传递给此函数,这与类型检查期间缺少约束一致。

在您的代码中,需要约束是因为您正在使用 bar,而不是因为您正在使用类型。

如果它暗示了约束,那么任何人以任何方式使用关联值的值都需要在范围内有约束。例如,

sort :: Ord a => [a] -> [a]

显然对MyClass一无所知,但如果您在调用中有Ord (AssociatedType a),应该可以将其用作sort :: [AssociatedType a] -> [AssociatedType a] ]范围。

要获得您似乎正在寻找的行为,您需要一个 包装约束 而不是关联类型。 -XTypeFamilies 无法做到这一点,但 -XGADTs:

可以做到
{-# LANGUAGE GADTs #-}

class MyClass a where
  bar :: Int

data MyClassWitness a where
  MyClassWitness :: MyClass a => MyClassWitness a

foo :: ∀ a. MyClassWitness a -> Int
foo MyClassWitness = bar @a

您还可以使用 constraints library:

中的包装器来代替 self-rolled 包装器
import Data.Constraint

foo :: ∀ a . Dict (MyClass a) -> Int
foo Dict = bar @a

在这两种情况下,重要的是您实际上 pattern-match 在 GADT 构造函数上,因为只有这样才能真正将约束带入范围。这 不会 起作用:

foo :: ∀ a . Dict (MyClass a) -> Int
foo _ = bar @a