我不知道您需要这些SymVal (Vec a n)
实例的确切上下文,但一般来说,如果您有一段需要该实例的代码,SymVal (Vec a n)
那么您应该将其添加为上下文:
foo :: forall (a :: Type) (n :: Nat). SymVal (Vec a n) => ...
当foo
使用特定调用时n
,约束求解器将减少类型族应用程序并使用实例
instance ( SymVal p, SymVal q ) => SymVal (p,q)
在该过程结束时,约束求解器将需要一个SymVal a
. 所以你可以打电话foo
:
- 如果您为 指定给定值
n
,则允许类型族应用程序完全减少,并使用a
具有以下实例的类型SymVal
:
bar :: forall (a :: Type). SymVal a => ...
bar = ... foo @a @(S (S (S Z))) ...
baz :: ...
baz = ... foo @Float @(S Z) ... -- Float has a SymVal instance
quux :: forall (a :: Type) (n :: Nat). SymVal (Vec a n) => ...
quux = ... foo @a @n ...
GHC 不能自动SymVal (Vec a n)
从推断SymVal a
,因为没有进一步的上下文它不能减少类型族应用程序,因此不知道选择哪个实例。如果您希望 GHC 能够执行此推论,则必须n
显式地作为参数传递。这可以用单例模拟:
deduceSymVal :: forall (a :: Type) (n :: Nat). Sing n -> Dict (SymVal a) -> Dict (SymVal (Vec a n))
deduceSymVal sz@SZ Dict =
case sz of
( _ :: Sing Z )
-> Dict
deduceSymVal ( ss@(SS sm) ) Dict
= case ss of
( _ :: Sing (S m) ) ->
case deduceSymVal @a @m sm Dict of
Dict -> Dict
(请注意,这些令人讨厌的案例陈述将被模式中的类型应用程序消除,mais c'est la vie。)
然后,您可以使用此函数来允许 GHC 从约束中推断出一个SymVal (Vec a n)
约束SymVal a
,只要您能够提供一个单例n
(这相当于n
显式传递而不是对其进行参数化):
flob :: forall (a :: Type) (n :: Nat). (SymVal a, SingI n) => ...
flob = case deduceSymVal @a (sing @n) Dict of
Dict -- matching on 'Dict' provides a `SymVal (Vec a n)` instance
-> ... foo @a @n ...