La question est assez simple dans la nature: le type résultant de sToStat
dépend de la valeur de son premier argument (u : U
dans votre code); lorsque vous utiliserez plus tard sToStat
à l'intérieur de check
, vous voulez que le type de retour dépende de someU
- mais check
promet que son type de retour dépend de l'implicite u : U
à la place! Maintenant, imaginons ceci, mais je vais vous montrer quelques problèmes qui pourraient survenir.
Que se passe-t-il si u1
est nothing
? Eh bien, dans ce cas, nous aimerions également retourner nothing
. nothing
de quel type? Maybe (El u)
vous pourriez dire, mais voici la chose - u
est marqué comme un argument implicite, ce qui signifie que le compilateur essaiera de l'inférer pour nous d'un autre contexte. Mais il n'y a pas d'autre contexte qui définirait la valeur de u
!
Agda sera très probablement se plaindre de métavariables non résolues à chaque fois que vous essayez d'utiliser check
, ce qui signifie que vous devez écrire la valeur de u
partout où vous utilisez check
, battant ainsi le point de marquer u
implicite en premier lieu. Au cas où vous ne le sauriez pas, Agda nous donne un moyen de fournir des arguments implicites:
check {u = nat} {- ... -}
mais je m'égare.
Une autre question devient évident si vous prolongez U
avec plus de constructeurs:
data U : Set where
nat char : U
par exemple. Nous devons aussi tenir compte de ce cas supplémentaire dans quelques autres fonctions, mais dans le but de cet exemple, nous allons avoir simplement:
El : U → Set
El nat = ℕ
El char = Char
Maintenant, ce qui est check {u = char} (just nat)
? sToStat someU (nat 1)
est Maybe ℕ
, mais El u
est Char
!
Et maintenant pour la solution possible. Nous devons faire en sorte que le type de résultat check
dépende de u1
. Si nous avions une sorte de fonction unJust
, nous pourrions écrire
check : (u1 : Maybe U) → Maybe (El (unJust u1))
Vous devriez voir le problème avec ce code tout de suite - rien ne nous garantit que u1
est just
.Même si nous allons retourner nothing
, nous devons toujours fournir un type correct!
Tout d'abord, nous devons choisir un certain type pour le cas nothing
. Disons que je voudrais étendre U
plus tard, donc je dois choisir quelque chose de neutre. Maybe ⊤
semble assez raisonnable (juste un rappel rapide, ⊤
est ce que ()
est dans Haskell - le type d'unité).
Comment pouvons-nous check
retourner dans certains cas Maybe ℕ
et Maybe ⊤
dans d'autres? Ah, nous pourrions utiliser une fonction!
Maybe-El : Maybe U → Set
Maybe-El nothing = Maybe ⊤
Maybe-El (just u) = Maybe (El u)
C'est exactement ce dont nous avions besoin! Maintenant check
devient simplement:
check : (u : Maybe U) → Maybe-El u
check (just someU) = sToStat someU (nat 1)
check nothing = nothing
En outre, c'est l'occasion idéale de mentionner le comportement de réduction de ces fonctions. Maybe-El
est très sous-optimale à cet égard, jetons un oeil à une autre mise en œuvre et faire un peu de comparaison.
Maybe-El₂ : Maybe U → Set
Maybe-El₂ = Maybe ∘ helper
where
helper : Maybe U → Set
helper nothing = ⊤
helper (just u) = El u
Ou peut-être que nous pourrions nous sauver et écrire quelques frappes:
Maybe-El₂ : Maybe U → Set
Maybe-El₂ = Maybe ∘ maybe El ⊤
D'accord, le précédent Maybe-El
et la nouvelle Maybe-El₂
sont équivalentes dans le sens où ils donnent des réponses aux mêmes mêmes entrées. C'est-à-dire ∀ x → Maybe-El x ≡ Maybe-El₂ x
. Mais il y a une énorme différence. Que pouvons-nous dire à propos de Maybe-El x
sans regarder ce que x
est? C'est vrai, nous ne pouvons rien dire. Les deux cas de fonction doivent savoir quelque chose à propos de x
avant de continuer.
Mais qu'en est-il de Maybe-El₂
? Essayons la même chose: nous commençons par Maybe-El₂ x
, mais cette fois, nous pouvons appliquer (le seul) un cas de fonction. Déroulage quelques définitions, nous arrivons à:
Maybe-El₂ x ⟶ (Maybe ∘ helper) x ⟶ Maybe (helper x)
Et maintenant nous sommes coincés, parce que pour réduire helper x
nous devons savoir ce que x
est. Mais regardez, nous avons beaucoup, beaucoup plus loin qu'avec Maybe-El
. Est-ce que cela fait une différence?
Tenir compte de cette fonction très stupide:
discard : {A : Set} → Maybe A → Maybe ⊤
discard _ = nothing
Naturellement, nous nous attendons à la fonction suivante à Typecheck.
discard₂ : Maybe U → Maybe ⊤
discard₂ = discard ∘ check
check
est la production Maybe y
pour certains y
, non? Ah, voici le problème - nous savons que check x : Maybe-El x
, mais nous ne savons rien sur , donc nous ne pouvons pas supposer Maybe-El x
réduit à Maybe y
non plus!
Du côté Maybe-El₂
, la situation est complètement différente. Nous sais que Maybe-El₂ x
réduit à Maybe y
, de sorte que le discard₂
maintenant typechecks!
Merci pour votre réponse.Encore une fois, une inspiration formidable! – mrsteve