Agda:我的代码没有键入检查(如何获取隐式参数?)

时间:2012-08-23 17:57:22

标签: haskell agda dependent-type

“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)
-}

1 个答案:

答案 0 :(得分:8)

问题非常简单:sToStat的结果类型取决于其第一个参数(代码中为u : U)的值;当您稍后在sToStat内使用check时,您希望返回类型依赖于someU - 但check承诺其返回类型取决于隐式u : U代替!


现在,让我们想象一下,这会做出类型检查,我会告诉你几个问题。

如果u1nothing怎么办?那么,在这种情况下,我们也希望返回nothingnothing是什么类型的?您可能会说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 uChar


现在可能的解决方案。我们需要以某种方式使check的结果类型依赖于u1。如果我们有某种unJust函数,我们可以编写

check : (u1 : Maybe U) → Maybe (El (unJust u1))

您应该立即看到此代码的问题 - 没有任何事情可以保证u1just。即使我们要返回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₂现在可以进行类型检查!