断言类型类适用于类型族应用程序的所有结果

时间:2019-07-04 00:31:47

标签: haskell typeclass type-families type-level-computation sbv

我有一个定义如下的类型族:

type family Vec a (n :: Nat) where
  Vec a Z = a
  Vec a (S n) = (a, Vec a n)

我想断言应用此类型族的结果始终满足SBV软件包中的SymVal类约束:

forall a . (SymVal a) => SymVal (Vec a n)

SymVal个实例a,b,因此只要SymVal a成立,只要SymVal (Vec a n)成立,n就应该成立。我如何确保GHC看到始终为类型族应用程序的结果实现SymVal

但是,我不知道该如何表达。我要写一个实例吗?派生子句?我不是在创建新的类型,只是将数字映射到现有的数字。

还是我完全走错了轨道?我应该使用数据系列还是功能依赖项?

2 个答案:

答案 0 :(得分:5)

无法完成。您只需要在所有位置放置约束即可。这真是令人讨厌。

答案 1 :(得分:4)

我不知道您需要这些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

(请注意,使用模式中的类型应用程序可以省去这些令人讨厌的case语句。)

然后,您可以使用此功能允许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 ...