无法推断前任 Nat 的 SingI

Posted

技术标签:

【中文标题】无法推断前任 Nat 的 SingI【英文标题】:Could not deduce SingI of predecessor Nat 【发布时间】:2017-01-10 14:39:06 【问题描述】:

我正在尝试为有限的整数集编写一个weaken 函数。我正在使用singletons 包。我已经定义并推广了加法、减法和前驱函数,并证明了它们的一些方程以帮助类型检查器。但我得到的错误与这一切完全无关。

weaken :: forall n m k . (SingI n, SingI m, SingI k, (Minus m n) ~ NatJust k) => Fin n -> Fin m
weaken ZF =  gcastWith (apply Refl $ plus_minus sm sn sk) ZF
  where sn = sing :: SNat n
        sm = sing :: SNat m
        sk = sing :: SNat k
weaken (SF n) = gcastWith (apply Refl $ succ_pred sm) (SF (weaken n))
  where sn = sing :: SNat n
        sm = sing :: SNat m
        sk = sing :: SNat k

我得到的错误是在weaken (SF (weaken n)) 的递归调用上,如下:Could not deduce (SingI n1),其中n1 被正确推断为类型n 的类型级前身.我可以添加一个SingI (Pred n) 约束,但这只会将问题向下移动一个级别(GHC 现在说它无法推断出(SingI (Pred (Pred n))) 的等价物)。

我如何让 GHC 相信 SingI (Pred n) 来自 SingI n(为什么 singletons 包没有这样做)?

【问题讨论】:

【参考方案1】:

GHC.TypeLits,最终为我们提供了类型级别的非负Integer-s,不导出任何可以让我们进行单例运行时操作的函数。例如,给定KnownNat aKnownNat b,没有标准函数可以在运行时生成KnownNat (a + b)

singletons 通过不安全地实现singleton Nat operations 解决了这个问题。

singletons 减法函数对负数结果抛出一个错误(并且不会像我们希望的那样截断为 0),所以我们不能为 pred 重用它,必须自己实现它:

-# language TypeApplications #- -- plus all the usual exts

import Data.Singletons.Prelude
import Data.Singletons.TypeLits
import Unsafe.Coerce

type family Pred (n :: Nat) :: Nat where
  Pred 0 = 0
  Pred n = n :- 1

sPred :: Sing n -> Sing (Pred n)
sPred sn = case fromSing sn of
  0 -> unsafeCoerce (sing @_ @0)
  n -> case toSing @Nat (n - 1) of
    SomeSing sn -> unsafeCoerce sn

您可以使用sPred 获取Sing (Pred n),然后使用withSingIsingInstance 将其转换为SingI (Pred n)

但是,您可能不应该在weaken 中使用SingISingI 的目的是在除了将它们转发给其他函数之外不将它们用于任何事情的上下文中自动插入单例参数。相反,只需使用Sing,您就可以避免sing 并键入注释噪音。以我的经验,Sing 在大约 90% 的时间用于 singletons 编程比 SingI 更可取。

【讨论】:

以上是关于无法推断前任 Nat 的 SingI的主要内容,如果未能解决你的问题,请参考以下文章

无法推断类型参数“T”

无法使用 SwiftUI 推断复杂的闭包返回类型

无法推断类型

RxDataSources`无法推断通用参数'Self'`

无法推断通用参数“D”。 [订购错误]

无法推断 COUNT 函数