“checkSimple”获取宇宙U的元素u,并检查是否 (nat 1)可以转换为给定的agda类型。返回转换结果。
现在我尝试编写一个控制台程序并从命令行获取“someU”。
因此,我将“checkSimple”的类型更改为包含(u:Maybe U)作为参数(可能因为来自控制台的输入可以是“无”)。但是我无法让代码进行类型检查。
module CheckMain where
open import Prelude
-- Install Prelude
---- clone this git repo:
---- https://github.com/fkettelhoit/agda-prelude
-- Configure Prelude
--- press Meta/Alt and the letter X together
--- type "customize-group" (i.e. in the mini buffer)
--- type "agda2"
--- expand the Entry "Agda2 Include Dirs:"
--- add the directory
data S : Set where
nat : (n : ℕ) → S
nil : S
sToℕ : S → Maybe ℕ
sToℕ (nat n) = just n
sToℕ _ = nothing
data U : Set where
nat : U
El : U → Set
El nat = ℕ
sToStat : (u : U) → S → Maybe (El u)
sToStat nat s = sToℕ s
-- Basic Test
test1 : Maybe ℕ
test1 = sToStat nat (nat 1)
{- THIS WORKS -}
checkSimple : (u : U) → Maybe (El u)
checkSimple someU = sToStat someU (nat 1)
{- HERE IS THE ERROR -}
-- in contrast to checkSimple we only get a (Maybe U) as a parameter
-- (e.g. from console input)
check : {u : U} (u1 : Maybe U) → Maybe (El u)
check (just someU) = sToStat someU (nat 1)
check _ = nothing
{- HER IS THE ERROR MESSAGE -}
{-
someU != .u of type U
when checking that the expression sToStat someU (nat 1) has type
Maybe (El .u)
-}
答案 0 :(得分:8)
问题非常简单:sToStat
的结果类型取决于其第一个参数(代码中为u : U
)的值;当您稍后在sToStat
内使用check
时,您希望返回类型依赖于someU
- 但check
承诺其返回类型取决于隐式u : U
代替!
现在,让我们想象一下,这会做出类型检查,我会告诉你几个问题。
如果u1
是nothing
怎么办?那么,在这种情况下,我们也希望返回nothing
。 nothing
是什么类型的?您可能会说Maybe (El u)
,但事情是这样的 - u
被标记为隐式参数,这意味着编译器将尝试从其他上下文中为我们推断它。但是没有其他背景可以降低u
的价值!
每当您尝试使用check
时,Agda很可能会抱怨未解决的元变量,这意味着您必须在u
的任何地方写入check
的值,从而打败了首先隐含标记u
。如果您不知道,Agda为我们提供了一种提供隐式参数的方法:
check {u = nat} {- ... -}
但我离题了。
如果使用更多构造函数扩展U
,则另一个问题变得明显:
data U : Set where
nat char : U
例如,。我们还必须在其他一些函数中考虑这个额外的情况,但是出于这个例子的目的,让我们只有:
El : U → Set
El nat = ℕ
El char = Char
现在,check {u = char} (just nat)
是什么? sToStat someU (nat 1)
为Maybe ℕ
,但El u
为Char
!
现在可能的解决方案。我们需要以某种方式使check
的结果类型依赖于u1
。如果我们有某种unJust
函数,我们可以编写
check : (u1 : Maybe U) → Maybe (El (unJust u1))
您应该立即看到此代码的问题 - 没有任何事情可以保证u1
是just
。即使我们要返回nothing
,我们仍然必须提供正确的类型!
首先,我们需要为nothing
案例选择一些类型。假设我想稍后延长U
,所以我需要选择一些中性的东西。 Maybe ⊤
听起来很合理(只是一个快速提醒,⊤
是Haskell中的()
- 单位类型。)
我们如何在某些情况下check
返回Maybe ℕ
,在其他情况下如何Maybe ⊤
?啊,我们可以使用一个函数!
Maybe-El : Maybe U → Set
Maybe-El nothing = Maybe ⊤
Maybe-El (just u) = Maybe (El u)
这正是我们所需要的!现在check
变为:
check : (u : Maybe U) → Maybe-El u
check (just someU) = sToStat someU (nat 1)
check nothing = nothing
此外,这是提及这些功能的减少行为的绝佳机会。 Maybe-El
在这方面非常不理想,让我们看看另一个实现并进行一些比较。
Maybe-El₂ : Maybe U → Set
Maybe-El₂ = Maybe ∘ helper
where
helper : Maybe U → Set
helper nothing = ⊤
helper (just u) = El u
或许我们可以为我们节省一些打字和写字:
Maybe-El₂ : Maybe U → Set
Maybe-El₂ = Maybe ∘ maybe El ⊤
好吧,之前的Maybe-El
和新的Maybe-El₂
是相同的,因为它们为相同的输入提供相同的答案。也就是∀ x → Maybe-El x ≡ Maybe-El₂ x
。但是有一个巨大的差异。如果不查看Maybe-El x
是什么,我们可以告诉x
什么?没错,我们什么都说不出来。在继续之前,两个功能案例都需要了解x
的内容。
但是Maybe-El₂
呢?让我们尝试相同:我们从Maybe-El₂ x
开始,但这一次,我们可以应用(唯一的)功能案例。展开一些定义,我们得出:
Maybe-El₂ x ⟶ (Maybe ∘ helper) x ⟶ Maybe (helper x)
现在我们陷入困境,因为为了减少helper x
,我们需要知道x
是什么。但是看,我们得到的远远超过Maybe-El
。它有所作为吗?
考虑这个非常愚蠢的功能:
discard : {A : Set} → Maybe A → Maybe ⊤
discard _ = nothing
当然,我们期望以下函数进行类型检查。
discard₂ : Maybe U → Maybe ⊤
discard₂ = discard ∘ check
check
正在为某些Maybe y
制作y
,对吗?啊,问题就出现了 - 我们知道check x : Maybe-El x
,但我们对x
一无所知,所以我们不能假设Maybe-El x
减少到Maybe y
!
在Maybe-El₂
方面,情况完全不同。我们知道 Maybe-El₂ x
缩减为Maybe y
,因此discard₂
现在可以进行类型检查!