Haskell 如何使GHC识别此代码段中的SingI实例?

Haskell 如何使GHC识别此代码段中的SingI实例?,haskell,ghc,singleton-type,Haskell,Ghc,Singleton Type,有一个关于 我有一个提升的数据类型a,其中我适当地实例化了数据族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

有一个关于

我有一个提升的数据类型
a
,其中我适当地实例化了
数据族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
,让type家族完成这两项工作。除此之外,我也有很多类似的函数,我不希望每一个都需要这样做

我确实打开了ScopedTypeVariables

谢谢大家


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

instance SingI NZ where
  sing = SNZ

instance SingI n => SingI (NS n) where
  sing = SNS sing
不幸的是,GHC仍然告诉我,当我在上面的lambda中使用
sing
时,没有
SingI
的实例:/

我觉得我应该可以在这里使用singI,让类型 家庭负责这两项工作

这是办不到的。我们在
Singleton
中拥有许多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
、类型族和构造函数的失效符号,还可以为
Eq
Ord
键入电平类似物,以及。您可以点击模块上的
:bro
,然后点击
:i PNat
,以准确了解生成的内容

现在
nPlus
和类型系列
nPlus
可以按预期工作

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

鉴于此

(SingI (NPlus n m)) => Sing n -> Sing m -> Sing (NPlus n m)
相当尴尬,因为它相当于

Sing (NPlus n m) -> Sing n -> Sing m -> Sing (NPlus n m)
它可以实现为一个不做任何加法的常量函数

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

啊,谢谢:)这看起来确实是目前最好的方法,可以很好地利用单例的全部功能,而不需要大量的代码复制。。。。