简体   繁体   English

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

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

had a question about singletons有一个关于单身人士的问题

I have a promoted data type a where I instanced data family Sing (a :: Foo) appropriately.我有一个提升的数据类型a ,我a其中适当地实例化了data family Sing (a :: Foo)

I also have a type family Bar (a :: Foo) (b :: Foo) :: Foo我也有一个类型家族Bar (a :: Foo) (b :: Foo) :: Foo

In the middle of a busy function, I have:在繁忙的功能中,我有:

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

But I'm not sure where I should put my SingI constraint.但我不确定应该把SingI约束放在SingI

For more details, my type is有关更多详细信息,我的类型是

data PNat = NZ | NS PNat

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

and my type family is我的字体家族是

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

Now, I'm actually able to manually write out explicit singleton shuffling:现在,我实际上能够手动写出显式单例改组:

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)

And this compiles fine...I can do这编译得很好......我可以做到

\x y -> nPlus x y

but I feel like I should be able to just use singI here and let the type family do both of the work.但我觉得我应该能够在这里使用singI并让singI系列完成这两项工作。 Besides, I have a lot of functions like this, too, and would prefer to not have to do it for each one.此外,我也有很多这样的功能,我宁愿不必为每个功能都做。

I do have ScopedTypeVariables turned on.我确实打开了 ScopedTypeVariables。

thank you all谢谢你们


Edit: Ah, I just realized that SingI instances aren't automatically derived;编辑:啊,我刚刚意识到SingI实例不是自动派生的; I whipped one up:我挑了一个:

instance SingI NZ where
  sing = SNZ

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

Unfortunately, GHC is still telling me that there is no instance of SingI when I use sing in the lambda above :/不幸的是,当我在上面的 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.我觉得我应该能够在这里使用 singI 并让字体系列完成这两项工作。

This can't be done.这是做不到的。 The reason why we have the many TH facilities in singletons is that data and function definitions must be duplicated in the current state of affairs.我们在singletons有许多 TH 设施的原因是数据和函数定义必须在当前状态下重复。

The idiomatic usage would be defining everything on the term level once, and deriving the rest with 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)  |])

This creates the Sing definition,the type family for nPlus , SingI instances, SingKind instances, the kind-restricted SPNat type synonym for Sing , defunctionalization symbols for type families and constructors, and also type-level analogues for Eq and Ord , and also decidable equality .这将创建Sing定义、 nPlus的类型族、 SingI实例、 SingKind实例、 Sing的种类限制SPNat类型同义词、类型族和构造函数的去功能化符号,以及EqOrd类型级类似物,以及可判定的平等 You can hit :bro on the module and :i PNat to find out exactly what got generated.您可以点击模块上的:bro:i PNat以准确找出生成的内容。

Now nPlus and the type family NPlus work as intended.现在nPlus和类型系列NPlus按预期工作。

To provide a bit of explanation regarding SingI : SingI a => t is equivalent to Sing a -> t .提供一些关于SingI的解释: SingI a => t等价于Sing a -> t They even compile to the exact same Core code.他们甚至编译成完全相同的核心代码。 The only difference between them is that Sing -s are passed explicitly and SingI -s implicitly.它们之间的唯一区别是Sing -s 是显式传递的,而SingI -s 是隐式传递的。 sing provides conversion from SingI to Sing , and singInstance converts backwards. sing提供从SingISing转换,而singInstance向后转换。

In the light of this, something like鉴于此,类似

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

is pretty awkward, since it's equivalent to很尴尬,因为它相当于

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

which can be implemented as a constant function that doesn't do any addition.它可以实现为一个不做任何加法的常量函数。

So when should we use SingI or Sing ?那么我们什么时候应该使用SingISing呢? The most convenient way is to perform computation on Sing -s, since we can pattern match on them, and use SingI only when we just need to plug in or pass along a value somewhere, but don't need to pattern match or recurse.最方便的方法是在Sing -s 上执行计算,因为我们可以对它们进行模式匹配,并且仅当我们只需要在某处插入或传递值时才使用SingI ,但不需要模式匹配或递归。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM