我如何让 GHC 识别此代码段中的 SingI 实例?

How do I make the GHC recognize a SingI instance in this snippet?

singletons

有疑问

我有一个提升的数据类型 a,我在其中适当地实例化了 data family Sing (a :: Foo)

我也有类型家族Bar (a :: Foo) (b :: Foo) :: Foo

在繁忙的功能中,我有:

(\(x :: Sing n) (y :: Sing m) -> doThingsWith (sing :: Sing (Bar n m)))

但我不确定我应该把 SingI 约束放在哪里。

更多详情,我的类型是

data PNat = NZ | NS PNat

data instance Sing (n :: PNat) where
  SNZ :: Sing 'NZ
  SNS :: Sing (m :: PNat) -> Sing ('NS m)

我的类型是

type family NPlus (n :: PNat) (m :: PNat) :: PNat where
  NPlus NZ y = y
  NPlus (NS x) y = NS (NPlus x y)

现在,我实际上能够手动写出显式单例改组:

nPlus :: forall n m nm. (NPlus n m ~ nm) => Sing n -> Sing m -> Sing nm
nPlus SNZ y = y
nPlus (SNS x) y = SNS (nPlus x y)

而且这个编译很好...我可以做到

\x y -> nPlus x y

但我觉得我应该可以在这里使用 singI 并让类型族完成这两项工作。此外,我也有很多这样的功能,并且希望不必每个都这样做。

我打开了 ScopedTypeVariables。

谢谢大家


编辑:啊,我刚刚意识到 SingI 实例不是自动派生的;我掀起了一个:

instance SingI NZ where
  sing = SNZ

instance SingI n => SingI (NS n) where
  sing = SNS sing

不幸的是,当我在上面的 lambda 中使用 sing 时,GHC 仍然告诉我没有 SingI 的实例:/

I feel like I should be able to just use singI here and let the type family do both of the work.

这是不可能的。我们在 singletons 中拥有许多 TH 设施的原因是数据和函数定义必须在当前状态下重复。

惯用的用法是在术语级别定义一次所有内容,然后用 TH 派生其余部分。

-- LANGUAGE KitchenSink
import Data.Singletons.TH
import Data.Singletons.Prelude

$(singletons [d|
  data PNat = NZ | NS PNat deriving (Eq, Show, Ord)

  nPlus :: PNat -> PNat -> PNat
  nPlus NZ y = y
  nPlus (NS x) y = NS (nPlus x y)  |])

这会创建 Sing 定义,nPlus 的类型族,SingI 实例,SingKind 实例,种类受限的 SPNat 类型同义词对于 Sing,类型族和构造函数的去功能化符号,以及 EqOrd 以及 decidable equality 的类型级类似物。您可以点击模块上的 :bro:i PNat 以准确找出生成的内容。

现在 nPlus 和类型族 NPlus 按预期工作。

提供一些关于 SingI 的解释:SingI a => t 等同于 Sing a -> t。他们甚至编译成完全相同的核心代码。它们之间的唯一区别是 Sing-s 是显式传递的,而 SingI-s 是隐式传递的。 sing 提供从 SingISing 的转换,而 singInstance 向后转换。

鉴于此,类似

(SingI (NPlus n m)) => Sing n -> Sing m -> Sing (NPlus n m)

很尴尬,因为它等同于

Sing (NPlus n m) -> Sing n -> Sing m -> Sing (NPlus n m)

可以实现为不做任何加法的常量函数。

那么我们什么时候应该使用SingISing呢?最方便的方法是在 Sing-s 上执行计算,因为我们可以对它们进行模式匹配,并且仅当我们只需要在某处插入或传递一个值时才使用 SingI,但不要不需要模式匹配或递归。