为什么关联类型同义词不暗示约束
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
为什么在签名中使用关联类型同义词不暗示相应的约束?
例如为什么不编译以下内容:
{-# 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:
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