🫤👉 [index]
🫤👉 [index]
About Me [About]
About Me [About]
Hi, This is Qu Zhuoyuan, you can call me KonKonjac online. I am currently a Master student at Nagoya University, Japan, supervised by Prof. Jacques Garrigue. My research interest is in type theory and programming language theory.
Contact
You can reach me via email at quzhuoyuan@gmail.com
Links
About this site
This site is a place where I write down my notes and blog on various topics. This website is generated with Kodama.
1. Blog [blog]
1. Blog [blog]
1.1. A Way to Implement Dependent Pattern Matching with Normalization by Evaluation [blog/dpm-nbe]
- Apr. 6. 2026
- Blog
1.1. A Way to Implement Dependent Pattern Matching with Normalization by Evaluation [blog/dpm-nbe]
- Apr. 6. 2026
- Blog
In my final year of college, I was working on a toy project named ShiTT (I rewrote it completely half a year ago), which is a dependent type checker in Agda style based on András Kovács’ elaboration-zoo, with dependent pattern matching (DPM) support. And I feel that I need to write a blog post to share some of the interesting ideas I came up.
One of the challenging problems I encountered was how to implement DPM in NbE settings. As we know, implementing DPM needs to unify the terms and when matching the pattern against the type . This process is to build a substitution and apply it to the body of the pattern matching. However, in NbE implementations, we usually have no simple way to substitute.
A Naive Solution
Take elaboration-zoo/02-typecheck-closures-debruijn for instance, there is an evaluation environment where contains the values of all variables. An intuitive way to do DPM is to modify the evaluation environment by changing the value of the unified variable. For example,
If the placeholder is a variable pattern , the type checking context of should be,
and the evaluation environment should be,
If is the constructor pattern , then the evaluation environment should be,
Then all the occurences of in RHS will directly evaluate to . But this approach comes with a problem: this kind of unification will break the wellformedness of the evaluation environment. The evaluation environment stores the values of all variables. And the type can be considered as a representation for the normal forms. We changed the value of from a variable to an other value, which might make some of other variables that depends on to be ill-formed. e.g. if there is a variable in the evaluation environment, after we change to , the value of will be , which is not a normal form, which will make other variables depend on also ill-formed, and so on.
A Better Solution
A workaround for this problem is to update the environment every time we need to unify two terms.
For example, we have a well-formed evaluation environment , such that . And we have a unification assignment where is a value. We have shown that directly substituting for in will fail. Instead, we introduce a new operation, I call it
Here implies that the environment need not to be well-formed. There might be a smarter way to implement without quoting it back, but let us just use this for now.
One can imagine as a substitution operation on . e.g. , here is the naive substitution of for in the environment (which, as we have seen, is not well-formed).
Then, we can map to every value in . We call this operation .
Thus we obtain . Is well-formed and is it the environment we want?
The answer is yes.
We can define the ill-formedness of an environment with a level parameter. For a value under an environment , if for any free variable in , , we say is well-formed or say it is -ill-formed. If there is a free variable in such that and is -ill-formed, then we say is -ill-formed. An environment is -ill-formed if it contains a value that is -ill-formed. For example,
We can prove that if is -ill-formed, then is -ill-formed.
If is well-formed, and and there are some other variables in depending on , then it is easy to see that is -ill-formed. And after applying on it we get a well-formed environment.
Let us rewrite the type of to make it more clear,
Where indicates the level of ill-formedness.
And also, don’t forget to using the updated environment to update all the types in type checking context as well.
A Limitation of Dependent Pattern Matching
This approach comes with an efficiency problem. Every time we need to unify two terms, we re-evaluate the whole environment, which is quite expensive. A workaround is to limit the length of the context. One can write the following Agda code,
module M (x : Nat) where
f : Id x 0 → ...
f refl = ...
A function has ability to unify the outer module variable . We can limit the ability of dependent pattern matching in ShiTT by only allowing it to unify the variables that are introduced in the current function (Well, I haven’t done it yet). So we rewrite the above code as,
module M (x : Nat) where
f' : (x : Nat) → Id x 0 → ...
f' x refl = ...
f = f' x
For a language with match expressions support, there is an extra reason to do this restriction. LightQuantum occurred this to me.
If we have the following data type,
data T : Set → Set where
C : T Bool
And we have a function with a match expression,
f : (A : Set) → T A → A
f A = λ t → match t with
| C => true -- here A = Bool
And we can define,
g1, g2 : T Nat → Nat
g1 = f Nat
g2 = λ t → match t with
| C => true
g1 is well-typed, while g2 is not, but one can find that g2 = f A [Nat / A] = g1. Substitution does not preserve typing!
The solution is as same as before, we limit the ability of dependent pattern matching by requiring all the unifying variables must in match expressions. So the above code will be rewritten as,
f A = λ t → match A , t with
| .Bool , C => true
After that, g2 ≠ f A [Nat / A] anymore, and it is not well-typed as expected.
All though Agda have no native support for match expressions, but it has lambda-case expressions which lead to the same problem. But seems they consider the lambda-case is a syntactic sugar for top-level pattern matching, which means the substitution is still type-preserving for Agda expressions (Since there is no pattern matching in Agda expressions).
Upon those two reasons, I think it might be a good idea to limit the ability of dependent pattern matching.
Update. Apr. 10. 2026
I just realized that the match expression example is wrong, the above code is also not well-typed under substitution. The final solution is to use lambda-case only (or equivalently, only allow matching on variables), and forbid unification on outer variables. So the above code will be rewritten as,
f = λ { A C -> true } : (A : Set) → T A → A
And lambda case only reduces when it is fully applied.
Update. Apr. 24. 2026
Turns out Rocq’s match-in-return-with expressions work just fine, no such problem as I mentioned above.
I so silly.
References
1.2. Old Blog Posts [blog/old]
1.2. Old Blog Posts [blog/old]
There are some old blog posts that I wrote before I started using Kodama. You can find most of them in my Zhihu profile.
2. Notes [notes]
2. Notes [notes]
3. A Naive Encoding of Russell’s Paradox in Type Theory [naive-encode-of-russell-paradox]
3. A Naive Encoding of Russell’s Paradox in Type Theory [naive-encode-of-russell-paradox]
Russell’s paradox is the most easily understandable way to illustrate the inconsistency of naïve set theory. This note proposes a direct encoding of Russell’s paradox with type-in-type universe, sigma types, and either extensional identity or intensional identity with the uniqueness of identity proofs (UIP).
See here for note in pdf.
See here for the formalization in Agda and Rocq.
4. 胡言乱语 [RandomWords]
4. 胡言乱语 [RandomWords]
2026/4/13 凌晨 名古屋
想家.
2026/4/12 名古屋
小学的时候想炸小学, 上了中学开始想念小学,想炸中学 上了大学爽了,啥也不想炸了。
现在只想回到大学,和朋友们再喝一杯酒
2026/4/11 名古屋
饥饿, 想食大便 贫穷, 想上班
2026/4/6 名古屋
我宣布, 人工智能是个笨蛋, 谁都不许害怕它.
2026/4/5 名古屋
我想家, 我和这里没有联系. 我用酒精麻痹自己.
我想家, 想回家.
为什么.
这辈子说的最多的就是,
为什么.
为什么.
不知道.
人群, 城市, 马路, 变形金刚.
饭店, 酒吧, 路牌, 一堆鞋子.
少年是什么
想念的那条街
确实如此.
晚安.
–
我明天要吃蛋炒饭.

2026/5/13 名古屋 凌晨
时间过去的越久, 越觉得有必要写些文字记录一下自己的大学生活, 但出于自己的社交焦虑本性, 总是写了又删, 删了又写, 像是露阴癖一般半遮半掩地展示自己的过往.
大概记得本科毕业前夕写过一篇, 酒醒之后再看到就删了.
我的确是一个不断地否定过去的自己的人.
但是我总是想, 总是想念着过去, 总是怀念着, 听着过去熟悉的音乐, 熟悉的旋律, 害怕着未来的自己会否定现在的我. 每当这种想法出现的时候, 我都会看着我的手, 问自己到底在干什么. 但是我自己显然也没有一个能够说服未来自己的答案.
想问未来, 问他有没有过上想要的生活, 有没有原谅现在的我.
再过一个月就是我本科毕业一年的日子, 每次想起过去总会怀念, 但是我真的站在那条承载我青春的街道上的时候, 我总是茫然, 我会想问我的朋友们呢, 我的青春呢, 我的过去呢, 我的回忆呢, 我当时喝到早上七点的馄饨铺呢.
但是正因如此, 我过去的一切都已经消失了 – 我草你妈了个臭逼, 我的时间平移对称性呢 – 这也许正是她美丽之处, 回忆美丽之处, 没有忧愁, 没有焦虑, 不必担心未来会发生什么, 因为老子他妈现在就在他妈的未来.
所以我现在来写一些文字, 并且祈祷明天的自己不会删除.
…
我第一次喝醉是在一个路边饭店, 青岛, 大二下学期, 应该是, 被疫情憋疯了, 和徐同学以及另外一个人去的, 在旁边超市买了三斤白酒, 一人喝了一瓶, 太他妈恶心了, 喝吐了, 吐在饭店路边, 打车回学校, 吐在学校路边.
但是开始发现, 喝了酒之后, 我能开始说话了, 我能自由地表达情感了, 我能丫的想说什么就说什么了, 我不会再在说话之前思考说了这句话之后我的未来会遭遇什么了, 我终于可以不去思考未来的我会怎么样地恨我了, 我像是自由了一样.
我的酒精生涯一直在追寻这样的自由.
我喝酒成瘾的原因是我终于能在饮酒后忽略别人的一切评价, 自己的一切考虑. 未来的一切焦虑.
社交焦虑或者说一切的焦虑本质, 在我看来, 都是过度思考, 而酒精能直接压制我的过度思考.
这叫我怎么能不上瘾.
所以我喜欢这个分子, 她能把我从未来的枷锁中解救出来.
下次喝醉再写吧, 晚安\