with-abstraction.lagda.rst 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. ..
  2. ::
  3. {-# OPTIONS --allow-unsolved-metas --irrelevant-projections --guardedness #-}
  4. module language.with-abstraction where
  5. open import Agda.Builtin.Nat using (Nat; zero; suc; _<_)
  6. open import Agda.Builtin.Bool using (Bool; true; false)
  7. data Comparison : Set where
  8. equal greater less : Comparison
  9. data List (A : Set) : Set where
  10. [] : List A
  11. _∷_ : A → List A → List A
  12. open import Agda.Builtin.Equality using (_≡_; refl)
  13. data ⊥ : Set where
  14. .. _with-abstraction:
  15. ****************
  16. With-Abstraction
  17. ****************
  18. .. contents::
  19. :depth: 2
  20. :local:
  21. With-abstraction was first introduced by Conor McBride [McBride2004]_ and lets
  22. you pattern match on the result of an intermediate computation by effectively
  23. adding an extra argument to the left-hand side of your function.
  24. Usage
  25. -----
  26. In the simplest case the ``with`` construct can be used just to discriminate on
  27. the result of an intermediate computation. For instance
  28. ..
  29. ::
  30. module verbose-usage where
  31. ::
  32. filter : {A : Set} → (A → Bool) → List A → List A
  33. filter p [] = []
  34. filter p (x ∷ xs) with p x
  35. filter p (x ∷ xs) | true = x ∷ filter p xs
  36. filter p (x ∷ xs) | false = filter p xs
  37. The clause containing the with-abstraction has no right-hand side. Instead it
  38. is followed by a number of clauses with an extra argument on the left,
  39. separated from the original arguments by a vertical bar (``|``).
  40. When the original arguments are the same in the new clauses you can use the
  41. ``...`` syntax:
  42. ..
  43. ::
  44. module ellipsis-usage where
  45. ::
  46. filter : {A : Set} → (A → Bool) → List A → List A
  47. filter p [] = []
  48. filter p (x ∷ xs) with p x
  49. ... | true = x ∷ filter p xs
  50. ... | false = filter p xs
  51. In this case ``...`` expands to ``filter p (x ∷ xs)``. There are three cases
  52. where you have to spell out the left-hand side:
  53. - If you want to do further pattern matching on the original
  54. arguments.
  55. - When the pattern matching on the intermediate result refines some of
  56. the other arguments (see :ref:`dot-patterns`).
  57. - To disambiguate the clauses of nested with-abstractions (see
  58. :ref:`nested-with-abstractions` below).
  59. ..
  60. ::
  61. module generalisation where
  62. .. _generalisation:
  63. Generalisation
  64. ~~~~~~~~~~~~~~
  65. The power of with-abstraction comes from the fact that the goal type
  66. and the type of the original arguments are generalised over the value
  67. of the scrutinee. See :ref:`technical-details` below for the details.
  68. This generalisation is important when you have to prove properties
  69. about functions defined using ``with``. For instance, suppose we want
  70. to prove that the ``filter`` function above satisfies some property
  71. ``P``. Starting out by pattern matching of the list we get the
  72. following (with the goal types shown in the holes)
  73. ..
  74. ::
  75. open ellipsis-usage
  76. ::
  77. postulate P : ∀ {A} → List A → Set
  78. postulate p-nil : ∀ {A} → P {A} []
  79. postulate Q : Set
  80. postulate q-nil : Q
  81. ..
  82. ::
  83. module verbose-proof where
  84. ::
  85. proof : {A : Set} (p : A → Bool) (xs : List A) → P (filter p xs)
  86. proof p [] = {! P [] !}
  87. proof p (x ∷ xs) = {! P (filter p (x ∷ xs) | p x) !}
  88. ..
  89. ::
  90. module ellipsis-proof where
  91. In the cons case we have to prove that ``P`` holds for ``filter p (x ∷ xs) | p x``.
  92. This is the syntax for a stuck with-abstraction---\ ``filter`` cannot reduce
  93. since we don't know the value of ``p x``. This syntax is used for printing, but
  94. is not accepted as valid Agda code. Now if we with-abstract over ``p x``, but
  95. don't pattern match on the result we get::
  96. proof : {A : Set} (p : A → Bool) (xs : List A) → P (filter p xs)
  97. proof p [] = p-nil
  98. proof p (x ∷ xs) with p x
  99. ... | r = {! P (filter p (x ∷ xs) | r) !}
  100. ..
  101. ::
  102. module ellipsis-proof-step where
  103. Here the ``p x`` in the goal type has been replaced by the variable ``r``
  104. introduced for the result of ``p x``. If we pattern match on ``r`` the
  105. with-clauses can reduce, giving us::
  106. proof : {A : Set} (p : A → Bool) (xs : List A) → P (filter p xs)
  107. proof p [] = p-nil
  108. proof p (x ∷ xs) with p x
  109. ... | true = {! P (x ∷ filter p xs) !}
  110. ... | false = {! P (filter p xs) !}
  111. Both the goal type and the types of the other arguments are generalised, so it
  112. works just as well if we have an argument whose type contains ``filter p xs``.
  113. ::
  114. proof₂ : {A : Set} (p : A → Bool) (xs : List A) → P (filter p xs) → Q
  115. proof₂ p [] _ = q-nil
  116. proof₂ p (x ∷ xs) H with p x
  117. ... | true = {! H : P (x ∷ filter p xs) !}
  118. ... | false = {! H : P (filter p xs) !}
  119. The generalisation is not limited to scrutinees in other with-abstractions. All
  120. occurrences of the term in the goal type and argument types will be
  121. generalised.
  122. Note that this generalisation is not always type correct and may
  123. result in a (sometimes cryptic) type error. See
  124. :ref:`ill-typed-with-abstractions` below for more details.
  125. .. _nested-with-abstractions:
  126. Nested with-abstractions
  127. ~~~~~~~~~~~~~~~~~~~~~~~~
  128. ..
  129. ::
  130. module compare-verbose where
  131. With-abstractions can be nested arbitrarily. The only thing to keep in mind in
  132. this case is that the ``...`` syntax applies to the closest with-abstraction.
  133. For example, suppose you want to use ``...`` in the definition below.
  134. ::
  135. compare : Nat → Nat → Comparison
  136. compare x y with x < y
  137. compare x y | false with y < x
  138. compare x y | false | false = equal
  139. compare x y | false | true = greater
  140. compare x y | true = less
  141. You might be tempted to replace ``compare x y`` with ``...`` in all the
  142. with-clauses as follows.
  143. .. code-block:: agda
  144. compare : Nat → Nat → Comparison
  145. compare x y with x < y
  146. ... | false with y < x
  147. ... | false = equal
  148. ... | true = greater
  149. ... | true = less -- WRONG
  150. This, however, would be wrong. In the last clause the ``...`` is interpreted as
  151. belonging to the inner with-abstraction (the whitespace is not taken into
  152. account) and thus expands to ``compare x y | false | true``. In this case you
  153. have to spell out the left-hand side and write
  154. ..
  155. ::
  156. module compare-ellipsis where
  157. ::
  158. compare : Nat → Nat → Comparison
  159. compare x y with x < y
  160. ... | false with y < x
  161. ... | false = equal
  162. ... | true = greater
  163. compare x y | true = less
  164. ..
  165. ::
  166. module simultaneous-abstraction where
  167. open import Agda.Builtin.Nat using (_+_)
  168. .. _simultaneous-abstraction:
  169. Simultaneous abstraction
  170. ~~~~~~~~~~~~~~~~~~~~~~~~
  171. You can abstract over multiple terms in a single with-abstraction. To do this
  172. you separate the terms with vertical bars (``|``).
  173. ::
  174. compare : Nat → Nat → Comparison
  175. compare x y with x < y | y < x
  176. ... | true | _ = less
  177. ... | _ | true = greater
  178. ... | false | false = equal
  179. In this example the order of abstracted terms does not matter, but in general
  180. it does. Specifically, the types of later terms are generalised over the values
  181. of earlier terms. For instance
  182. ::
  183. postulate plus-commute : (a b : Nat) → a + b ≡ b + a
  184. postulate P : Nat → Set
  185. ..
  186. ::
  187. module simultaneous-thm-unmatched where
  188. ::
  189. thm : (a b : Nat) → P (a + b) → P (b + a)
  190. thm a b t with a + b | plus-commute a b
  191. thm a b t | ab | eq = {! t : P ab, eq : ab ≡ b + a !}
  192. Note that both the type of ``t`` and the type of the result ``eq`` of
  193. ``plus-commute a b`` have been generalised over ``a + b``. If the terms in the
  194. with-abstraction were flipped around, this would not be the case. If we now
  195. pattern match on ``eq`` we get
  196. ..
  197. ::
  198. module simultaneous-thm-refl where
  199. ::
  200. thm : (a b : Nat) → P (a + b) → P (b + a)
  201. thm a b t with a + b | plus-commute a b
  202. thm a b t | .(b + a) | refl = {! t : P (b + a) !}
  203. and can thus fill the hole with ``t``. In effect we used the
  204. commutativity proof to rewrite ``a + b`` to ``b + a`` in the type of
  205. ``t``. This is such a useful thing to do that there is special syntax
  206. for it. See :ref:`Rewrite <with-rewrite>` below.
  207. ..
  208. ::
  209. module with-on-lemma where
  210. .. _with-on-lemma:
  211. A limitation of generalisation is that only occurrences of the term that are
  212. visible at the time of the abstraction are generalised over, but more instances
  213. of the term may appear once you start filling in the right-hand side or do
  214. further matching on the left. For instance, consider the following contrived
  215. example where we need to match on the value of ``f n`` for the type of ``q`` to
  216. reduce, but we then want to apply ``q`` to a lemma that talks about ``f n``::
  217. postulate
  218. R : Set
  219. P : Nat → Set
  220. f : Nat → Nat
  221. lemma : ∀ n → P (f n) → R
  222. Q : Nat → Set
  223. Q zero = ⊥
  224. Q (suc n) = P (suc n)
  225. ..
  226. ::
  227. module proof-blocked where
  228. ::
  229. proof : (n : Nat) → Q (f n) → R
  230. proof n q with f n
  231. proof n () | zero
  232. proof n q | suc fn = {! q : P (suc fn) !}
  233. ..
  234. ::
  235. module proof-lemma where
  236. Once we have generalised over ``f n`` we can no longer apply the lemma, which
  237. needs an argument of type ``P (f n)``. To solve this problem we can add the
  238. lemma to the with-abstraction::
  239. proof : (n : Nat) → Q (f n) → R
  240. proof n q with f n | lemma n
  241. proof n () | zero | _
  242. proof n q | suc fn | lem = lem q
  243. In this case the type of ``lemma n`` (``P (f n) → R``) is generalised over ``f
  244. n`` so in the right-hand side of the last clause we have ``q : P (suc fn)`` and
  245. ``lem : P (suc fn) → R``.
  246. See :ref:`the-inspect-idiom` below for an alternative approach.
  247. ..
  248. ::
  249. module with-modalities where
  250. .. _with-modalities:
  251. Making with-abstractions hidden and/or irrelevant
  252. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  253. It is possible to add hiding and relevance annotations to `with`
  254. expressions. For example::
  255. module _ (A B : Set) (recompute : .B → .{{A}} → B) where
  256. _$_ : .(A → B) → .A → B
  257. f $ x with .{f} | .(f x) | .{{x}}
  258. ... | y = recompute y
  259. This can be useful for hiding with-abstractions that you do not need
  260. to match on but that need to be abstracted over for the result to be
  261. well-typed. It can also be used to abstract over the fields of a
  262. record type with irrelevant fields, for example::
  263. record EqualBools : Set₁ where
  264. field
  265. bool1 : Bool
  266. bool2 : Bool
  267. .same : bool1 ≡ bool2
  268. open EqualBools
  269. example : EqualBools → EqualBools
  270. example x with bool1 x | bool2 x | .(same x)
  271. ... | true | y′ | eq′ = record { bool1 = true; bool2 = y′; same = eq′ }
  272. ... | false | y′ | eq′ = record { bool1 = false; bool2 = y′; same = eq′ }
  273. ..
  274. ::
  275. module with-clause-underscore where
  276. .. _with-clause-underscore:
  277. Using underscores and variables in pattern repetition
  278. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  279. If an ellipsis `...` cannot be used, the with-clause has to repeat (or
  280. refine) the patterns of the parent clause. Since Agda 2.5.3, such
  281. patterns can be replaced by underscores `_` if the variables they bind
  282. are not needed. Here is a (slightly contrived) example::
  283. record R : Set where
  284. coinductive -- disallows matching
  285. field f : Bool
  286. n : Nat
  287. data P (r : R) : Nat → Set where
  288. fTrue : R.f r ≡ true → P r zero
  289. nSuc : P r (suc (R.n r))
  290. data Q : (b : Bool) (n : Nat) → Set where
  291. true! : Q true zero
  292. suc! : ∀{b n} → Q b (suc n)
  293. test : (r : R) {n : Nat} (p : P r n) → Q (R.f r) n
  294. test r nSuc = suc!
  295. test r (fTrue p) with R.f r
  296. test _ (fTrue ()) | false
  297. test _ _ | true = true! -- underscore instead of (isTrue _)
  298. Since Agda 2.5.4, patterns can also be replaced by a variable::
  299. f : List Nat → List Nat
  300. f [] = []
  301. f (x ∷ xs) with f xs
  302. f xs0 | r = ?
  303. The variable `xs0` is treated as a let-bound variable with value `.x ∷
  304. .xs` (where `.x : Nat` and `.xs : List Nat` are out of scope). Since
  305. with-abstraction may change the type of variables, the instantiation
  306. of such let-bound variables are type checked again after
  307. with-abstraction.
  308. ..
  309. ::
  310. module with-invert {a} {A : Set a} where
  311. open import Agda.Builtin.Nat
  312. open import Agda.Builtin.Sigma
  313. open import Agda.Builtin.Equality
  314. open import Agda.Builtin.Unit
  315. .. _with-invert:
  316. Irrefutable With
  317. ~~~~~~~~~~~~~~~~
  318. When a pattern is irrefutable, we can use a pattern-matching ``with``
  319. instead of a traditional ``with`` block. This gives us a lightweight
  320. syntax to make a lot of observations before using a "proper" ``with``
  321. block. For a basic example of such an irrefutable pattern, see this
  322. unfolding lemma for ``pred`` ::
  323. pred : Nat → Nat
  324. pred zero = zero
  325. pred (suc n) = n
  326. NotNull : Nat → Set
  327. NotNull zero = ⊥ -- false
  328. NotNull (suc n) = ⊤ -- trivially true
  329. pred-correct : ∀ n (pr : NotNull n) → suc (pred n) ≡ n
  330. pred-correct n pr with suc p ← n = refl
  331. In the above code snippet we do not need to entertain the idea that ``n``
  332. could be equal to ``zero``: Agda detects that the proof ``pr`` allows us
  333. to dismiss such a case entirely.
  334. The patterns used in such an inversion clause can be arbitrary. We can
  335. for instance have deep patterns, e.g. projecting out the second element
  336. of a vector whose length is neither 0 nor 1:
  337. ::
  338. infixr 5 _∷_
  339. data Vec {a} (A : Set a) : Nat → Set a where
  340. [] : Vec A zero
  341. _∷_ : ∀ {n} → A → Vec A n → Vec A (suc n)
  342. second : ∀ {n} {pr : NotNull (pred n)} → Vec A n → A
  343. second vs with (_ ∷ v ∷ _) ← vs = v
  344. Remember the example of :ref:`simultaneous
  345. abstraction <simultaneous-abstraction>` from above. A simultaneous
  346. rewrite / pattern-matching ``with`` is to be understood as being nested.
  347. That is to say that the type refinements introduced by the first
  348. case analysis may be necessary to type the following ones.
  349. In the following example, in ``focusAt`` we are only able to perform
  350. the ``splitAt`` we are interested in because we have massaged the type
  351. of the vector argument using ``suc-+`` first.
  352. ::
  353. suc-+ : ∀ m n → suc m + n ≡ m + suc n
  354. suc-+ zero n = refl
  355. suc-+ (suc m) n rewrite suc-+ m n = refl
  356. infixr 1 _×_
  357. _×_ : ∀ {a b} (A : Set a) (B : Set b) → Set ?
  358. A × B = Σ A (λ _ → B)
  359. splitAt : ∀ m {n} → Vec A (m + n) → Vec A m × Vec A n
  360. splitAt zero xs = ([] , xs)
  361. splitAt (suc m) (x ∷ xs) with (ys , zs) ← splitAt m xs = (x ∷ ys , zs)
  362. -- focusAt m (x₀ ∷ ⋯ ∷ xₘ₋₁ ∷ xₘ ∷ xₘ₊₁ ∷ ⋯ ∷ xₘ₊ₙ)
  363. -- returns ((x₀ ∷ ⋯ ∷ xₘ₋₁) , xₘ , (xₘ₊₁ ∷ ⋯ ∷ xₘ₊ₙ))
  364. focusAt : ∀ m {n} → Vec A (suc (m + n)) → Vec A m × A × Vec A n
  365. focusAt m {n} vs rewrite suc-+ m n
  366. with (before , focus ∷ after) ← splitAt m vs
  367. = (before , focus , after)
  368. You can alternate arbitrarily many ``rewrite`` and pattern-matching
  369. ``with`` clauses and still perform a ``with`` abstraction afterwards
  370. if necessary.
  371. ..
  372. ::
  373. module with-rewrite where
  374. open import Agda.Builtin.Nat using (_+_)
  375. .. _with-rewrite:
  376. Rewrite
  377. ~~~~~~~
  378. Remember example of :ref:`simultaneous
  379. abstraction <simultaneous-abstraction>` from above.
  380. ..
  381. ::
  382. module remember-simultaneous-abstraction where
  383. postulate P : Nat → Set
  384. ::
  385. postulate plus-commute : (a b : Nat) → a + b ≡ b + a
  386. thm : (a b : Nat) → P (a + b) → P (b + a)
  387. thm a b t with a + b | plus-commute a b
  388. thm a b t | .(b + a) | refl = t
  389. ..
  390. ::
  391. open simultaneous-abstraction
  392. This pattern of rewriting by an equation by with-abstracting over it and its
  393. left-hand side is common enough that there is special syntax for it::
  394. thm : (a b : Nat) → P (a + b) → P (b + a)
  395. thm a b t rewrite plus-commute a b = t
  396. The ``rewrite`` construction takes a term ``eq`` of type ``lhs ≡ rhs``, where ``_≡_``
  397. is the :ref:`built-in equality type <built-in-equality>`, and expands to a
  398. with-abstraction of ``lhs`` and ``eq`` followed by a match of the result of
  399. ``eq`` against ``refl``:
  400. .. code-block:: agda
  401. f ps rewrite eq = v
  402. -->
  403. f ps with lhs | eq
  404. ... | .rhs | refl = v
  405. One limitation of the ``rewrite`` construction is that you cannot do further
  406. pattern matching on the arguments *after* the rewrite, since everything happens
  407. in a single clause. You can however do with-abstractions after the rewrite. For
  408. instance,
  409. ::
  410. postulate T : Nat → Set
  411. isEven : Nat → Bool
  412. isEven zero = true
  413. isEven (suc zero) = false
  414. isEven (suc (suc n)) = isEven n
  415. thm₁ : (a b : Nat) → T (a + b) → T (b + a)
  416. thm₁ a b t rewrite plus-commute a b with isEven a
  417. thm₁ a b t | true = t
  418. thm₁ a b t | false = t
  419. Note that the with-abstracted arguments introduced by the rewrite (``lhs`` and
  420. ``eq``) are not visible in the code.
  421. ..
  422. ::
  423. module inspect-idiom where
  424. .. _the-inspect-idiom:
  425. With-abstraction equality
  426. ~~~~~~~~~~~~~~~~~~~~~~~~~
  427. When you with-abstract a term ``t`` you lose the connection between
  428. ``t`` and the new argument representing its value. That's fine as long
  429. as all instances of ``t`` that you care about get generalised by the
  430. abstraction, but as we saw :ref:`above <with-on-lemma>` this is not
  431. always the case. In that example we used simultaneous abstraction to
  432. make sure that we did capture all the instances we needed.
  433. An alternative to that is to get Agda to remember in an equality proof
  434. that the patterns in the with clauses come from the expression you abstracted
  435. over. This is possible using the ``in`` keyword.
  436. ..
  437. ::
  438. open import Agda.Builtin.Sigma using (Σ; _,_)
  439. open import Agda.Builtin.Nat using (_+_)
  440. In the following artificial example, we try to prove that there exists two
  441. numbers such that one equals the double of the other. We start by computing
  442. the double of our input ``m`` and call it ``n``. We can then return the nested
  443. pair containing ``m``, ``n``, and we now need a proof that ``m + m ≡ n``.
  444. Luckily we used ``in eq`` when computing ``n`` as ``m + m`` and this ``eq``
  445. is exactly the proof we need.
  446. ::
  447. double : Nat → Σ Nat (λ m → Σ Nat (λ n → m + m ≡ n))
  448. double m with n ← m + m in eq = m , n , eq
  449. For a more natural example, we prove that ``filter`` (defined at the top of this
  450. page) is idempotent. That is to say that applying it twice to an input list is
  451. the same as only applying it once.
  452. In the ``filter-filter p (x ∷ xs)`` case, abstracting over and then matching
  453. on the result of ``p x`` allows the first call to ``filter p (x ∷ xs)`` to
  454. reduce.
  455. In case the element ``x`` is kept (i.e. ``p x`` is ``true``), the second call
  456. to ``filter`` on the LHS goes on to performs the same ``p x`` test. Because we
  457. have retained the proof that ``p x ≡ true`` in ``eq``, we are able to rewrite
  458. by this equality and get it to reduce too.
  459. This leads to just enough computation that we can finish the proof with
  460. an appeal to congruence and the induction hypothesis.
  461. ..
  462. ::
  463. open ellipsis-usage
  464. cong : {A B : Set} (f : A → B) → ∀ {x y} → x ≡ y → f x ≡ f y
  465. cong f refl = refl
  466. ::
  467. filter-filter : ∀ {A} p (xs : List A) → filter p (filter p xs) ≡ filter p xs
  468. filter-filter p [] = refl
  469. filter-filter p (x ∷ xs) with p x in eq
  470. ... | false = filter-filter p xs -- easy
  471. ... | true -- second filter stuck on `p x`: rewrite by `eq`!
  472. rewrite eq = cong (x ∷_) (filter-filter p xs)
  473. Alternatives to with-abstraction
  474. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  475. Although with-abstraction is very powerful there are cases where you cannot or
  476. don't want to use it. For instance, you cannot use with-abstraction if you are
  477. inside an expression in a right-hand side. In that case there are a couple of
  478. alternatives.
  479. Pattern lambdas
  480. +++++++++++++++
  481. Agda does not have a primitive ``case`` construct, but one can be emulated
  482. using :ref:`pattern matching lambdas <pattern-lambda>`. First you define a
  483. function ``case_of_`` as follows::
  484. case_of_ : ∀ {a b} {A : Set a} {B : Set b} → A → (A → B) → B
  485. case x of f = f x
  486. You can then use this function with a pattern matching lambda as the second
  487. argument to get a Haskell-style case expression::
  488. filter : {A : Set} → (A → Bool) → List A → List A
  489. filter p [] = []
  490. filter p (x ∷ xs) =
  491. case p x of
  492. λ { true → x ∷ filter p xs
  493. ; false → filter p xs
  494. }
  495. This version of ``case_of_`` only works for non-dependent functions. For
  496. dependent functions the target type will in most cases not be inferrable, but
  497. you can use a variant with an explicit ``B`` for this case::
  498. case_return_of_ : ∀ {a b} {A : Set a} (x : A) (B : A → Set b) → (∀ x → B x) → B x
  499. case x return B of f = f x
  500. The dependent version will let you generalise over the scrutinee, just like a
  501. with-abstraction, but you have to do it manually. Two things that it will not let you do is
  502. - further pattern matching on arguments on the left-hand side, and
  503. - refine arguments on the left by the patterns in the case expression. For
  504. instance if you matched on a ``Vec A n`` the ``n`` would be refined by the
  505. nil and cons patterns.
  506. Helper functions
  507. ++++++++++++++++
  508. Internally with-abstractions are translated to auxiliary functions
  509. (see :ref:`technical-details` below) and you can always write these
  510. functions manually. The downside is that the type signature for the
  511. helper function needs to be written out explicitly, but fortunately
  512. the :ref:`emacs-mode` has a command (``C-c C-h``) to generate it using
  513. the same algorithm that generates the type of a with-function.
  514. Termination checking
  515. ~~~~~~~~~~~~~~~~~~~~
  516. ..
  517. ::
  518. module Termination where
  519. postulate
  520. some-stuff : Nat
  521. module _ where
  522. The termination checker runs on the translated auxiliary functions, which
  523. means that some code that looks like it should pass termination checking
  524. does not. Specifically this happens in call chains like ``c₁ (c₂ x) ⟶ c₁ x``
  525. where the recursive call is under a with-abstraction. The reason is that
  526. the auxiliary function only gets passed ``x``, so the call chain is actually
  527. ``c₁ (c₂ x) ⟶ x ⟶ c₁ x``, and the termination checker cannot see that this
  528. is terminating. For example::
  529. data D : Set where
  530. [_] : Nat → D
  531. ..
  532. ::
  533. module M₁ where
  534. {-# TERMINATING #-}
  535. ::
  536. fails : D → Nat
  537. fails [ zero ] = zero
  538. fails [ suc n ] with some-stuff
  539. ... | _ = fails [ n ]
  540. The easiest way to work around this problem is to perform a with-abstraction
  541. on the recursive call up front::
  542. fixed : D → Nat
  543. fixed [ zero ] = zero
  544. fixed [ suc n ] with fixed [ n ] | some-stuff
  545. ... | rec | _ = rec
  546. ..
  547. ::
  548. module M₂ where
  549. {-# TERMINATING #-}
  550. If the function takes more arguments you might need to abstract over a
  551. partial application to just the structurally recursive argument. For instance,
  552. ::
  553. fails : Nat → D → Nat
  554. fails _ [ zero ] = zero
  555. fails _ [ suc n ] with some-stuff
  556. ... | m = fails m [ n ]
  557. fixed : Nat → D → Nat
  558. fixed _ [ zero ] = zero
  559. fixed _ [ suc n ] with (λ m → fixed m [ n ]) | some-stuff
  560. ... | rec | m = rec m
  561. ..
  562. ::
  563. postulate
  564. A possible complication is that later with-abstractions might change the
  565. type of the abstracted recursive call::
  566. T : D → Set
  567. suc-T : ∀ {n} → T [ n ] → T [ suc n ]
  568. zero-T : T [ zero ]
  569. ..
  570. ::
  571. module M₃ where
  572. {-# TERMINATING #-}
  573. ::
  574. fails : (d : D) → T d
  575. fails [ zero ] = zero-T
  576. fails [ suc n ] with some-stuff
  577. ... | _ with [ n ]
  578. ... | z = suc-T (fails [ n ])
  579. Trying to abstract over the recursive call as before does not work in this case.
  580. .. code-block:: agda
  581. still-fails : (d : D) → T d
  582. still-fails [ zero ] = zero-T
  583. still-fails [ suc n ] with still-fails [ n ] | some-stuff
  584. ... | rec | _ with [ n ]
  585. ... | z = suc-T rec -- Type error because rec : T z
  586. To solve the problem you can add ``rec`` to the with-abstraction messing up
  587. its type. This will prevent it from having its type changed::
  588. fixed : (d : D) → T d
  589. fixed [ zero ] = zero-T
  590. fixed [ suc n ] with fixed [ n ] | some-stuff
  591. ... | rec | _ with rec | [ n ]
  592. ... | _ | z = suc-T rec
  593. Performance considerations
  594. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  595. The :ref:`generalisation step <generalisation>` of a with-abstraction
  596. needs to normalise the scrutinee and the goal and argument types to
  597. make sure that all instances of the scrutinee are generalised. The
  598. generalisation also needs to be type checked to make sure that it's
  599. not :ref:`ill-typed <ill-typed-with-abstractions>`. This makes it
  600. expensive to type check a with-abstraction if
  601. - the normalisation is expensive,
  602. - the normalised form of the goal and argument types are big, making finding
  603. the instances of the scrutinee expensive,
  604. - type checking the generalisation is expensive, because the types are big, or
  605. because checking them involves heavy computation.
  606. In these cases it is worth looking at the `alternatives to with-abstraction`_
  607. from above.
  608. .. _technical-details:
  609. Technical details
  610. -----------------
  611. Internally with-abstractions are translated to auxiliary functions---there are
  612. no with-abstractions in the :ref:`core-language`. This translation proceeds as
  613. follows. Given a with-abstraction
  614. .. math::
  615. :nowrap:
  616. \[\arraycolsep=1.4pt
  617. \begin{array}{lrllcll}
  618. \multicolumn{3}{l}{f : \Gamma \to B} \\
  619. f ~ ps & \mathbf{with} ~ & t_1 & | & \ldots & | ~ t_m \\
  620. f ~ ps_1 & | ~ & q_{11} & | & \ldots & | ~ q_{1m} &= v_1 \\
  621. \vdots \\
  622. f ~ ps_n & | ~ & q_{n1} & | & \ldots & | ~ q_{nm} &= v_n
  623. \end{array}\]
  624. where :math:`\Delta \vdash ps : \Gamma` (i.e. :math:`\Delta` types the
  625. variables bound in :math:`ps`), we
  626. - Infer the types of the scrutinees :math:`t_1 : A_1, \ldots, t_m : A_m`.
  627. - Partition the context :math:`\Delta` into :math:`\Delta_1` and
  628. :math:`\Delta_2` such that :math:`\Delta_1` is the smallest context where
  629. :math:`\Delta_1 \vdash t_i : A_i` for all :math:`i`, i.e., where the scrutinees are well-typed.
  630. Note that the partitioning is not required to be a split,
  631. :math:`\Delta_1\Delta_2` can be a (well-formed) reordering of :math:`\Delta`.
  632. - Generalise over the :math:`t_i` s, by computing
  633. .. math::
  634. C = (w_1 : A_1)(w_1 : A_2')\ldots(w_m : A_m') \to \Delta_2' \to B'
  635. such that the normal form of :math:`C` does not contain any :math:`t_i` and
  636. .. math::
  637. A_i'[w_1 := t_1 \ldots w_{i - 1} := t_{i - 1}] \simeq A_i
  638. (\Delta_2' \to B')[w_1 := t_1 \ldots w_m := t_m] \simeq \Delta_2 \to B
  639. where :math:`X \simeq Y` is equality of the normal forms of :math:`X` and
  640. :math:`Y`. The type of the auxiliary function is then :math:`\Delta_1 \to C`.
  641. - Check that :math:`\Delta_1 \to C` is type correct, which is not
  642. guaranteed (see :ref:`below <ill-typed-with-abstractions>`).
  643. - Add a function :math:`f_{aux}`, mutually recursive with :math:`f`, with the
  644. definition
  645. .. math::
  646. :nowrap:
  647. \[\arraycolsep=1.4pt
  648. \begin{array}{llll}
  649. \multicolumn{4}{l}{f_{aux} : \Delta_1 \to C} \\
  650. f_{aux} ~ ps_{11} & \mathit{qs}_1 & ps_{21} &= v_1 \\
  651. \vdots \\
  652. f_{aux} ~ ps_{1n} & \mathit{qs}_n & ps_{2n} &= v_n \\
  653. \end{array}\]
  654. where :math:`\mathit{qs}_i = q_{i1} \ldots q_{im}`, and :math:`ps_{1i} : \Delta_1` and
  655. :math:`ps_{2i} : \Delta_2` are the patterns from :math:`ps_i` corresponding to
  656. the variables of :math:`ps`. Note that due to the possible reordering of the
  657. partitioning of :math:`\Delta` into :math:`\Delta_1` and :math:`\Delta_2`,
  658. the patterns :math:`ps_{1i}` and :math:`ps_{2i}` can be in a different order
  659. from how they appear :math:`ps_i`.
  660. - Replace the with-abstraction by a call to :math:`f_{aux}` resulting in the
  661. final definition
  662. .. math::
  663. :nowrap:
  664. \[\arraycolsep=1.4pt
  665. \begin{array}{l}
  666. f : \Gamma \to B \\
  667. f ~ ps = f_{aux} ~ \mathit{xs}_1 ~ ts ~ \mathit{xs}_2
  668. \end{array}\]
  669. where :math:`ts = t_1 \ldots t_m` and :math:`\mathit{xs}_1` and
  670. :math:`\mathit{xs}_2` are the variables from :math:`\Delta` corresponding to
  671. :math:`\Delta_1` and :math:`\Delta_2` respectively.
  672. ..
  673. ::
  674. module examples where
  675. Examples
  676. ~~~~~~~~
  677. Below are some examples of with-abstractions and their translations.
  678. ::
  679. postulate
  680. A : Set
  681. _+_ : A → A → A
  682. T : A → Set
  683. mkT : ∀ x → T x
  684. P : ∀ x → T x → Set
  685. -- the type A of the with argument has no free variables, so the with
  686. -- argument will come first
  687. f₁ : (x y : A) (t : T (x + y)) → T (x + y)
  688. f₁ x y t with x + y
  689. f₁ x y t | w = {!!}
  690. -- Generated with function
  691. f-aux₁ : (w : A) (x y : A) (t : T w) → T w
  692. f-aux₁ w x y t = {!!}
  693. -- x and p are not needed to type the with argument, so the context
  694. -- is reordered with only y before the with argument
  695. f₂ : (x y : A) (p : P y (mkT y)) → P y (mkT y)
  696. f₂ x y p with mkT y
  697. f₂ x y p | w = {!!}
  698. f-aux₂ : (y : A) (w : T y) (x : A) (p : P y w) → P y w
  699. f-aux₂ y w x p = {!!}
  700. postulate
  701. H : ∀ x y → T (x + y) → Set
  702. -- Multiple with arguments are always inserted together, so in this case
  703. -- t ends up on the left since it’s needed to type h and thus x + y isn’t
  704. -- abstracted from the type of t
  705. f₃ : (x y : A) (t : T (x + y)) (h : H x y t) → T (x + y)
  706. f₃ x y t h with x + y | h
  707. f₃ x y t h | w₁ | w₂ = {! t : T (x + y), goal : T w₁ !}
  708. f-aux₃ : (x y : A) (t : T (x + y)) (h : H x y t) (w₁ : A) (w₂ : H x y t) → T w₁
  709. f-aux₃ x y t h w₁ w₂ = {!!}
  710. -- But earlier with arguments are abstracted from the types of later ones
  711. f₄ : (x y : A) (t : T (x + y)) → T (x + y)
  712. f₄ x y t with x + y | t
  713. f₄ x y t | w₁ | w₂ = {! t : T (x + y), w₂ : T w₁, goal : T w₁ !}
  714. f-aux₄ : (x y : A) (t : T (x + y)) (w₁ : A) (w₂ : T w₁) → T w₁
  715. f-aux₄ x y t w₁ w₂ = {!!}
  716. ..
  717. ::
  718. module ill-typed where
  719. .. _ill-typed-with-abstractions:
  720. Ill-typed with-abstractions
  721. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  722. As mentioned above, generalisation does not always produce well-typed results.
  723. This happens when you abstract over a term that appears in the *type* of a subterm
  724. of the goal or argument types. The simplest example is abstracting over the
  725. first component of a dependent pair. For instance,
  726. ::
  727. postulate
  728. A : Set
  729. B : A → Set
  730. H : (x : A) → B x → Set
  731. .. code-block:: agda
  732. bad-with : (p : Σ A B) → H (fst p) (snd p)
  733. bad-with p with fst p
  734. ... | _ = {!!}
  735. Here, generalising over ``fst p`` results in an ill-typed application ``H w
  736. (snd p)`` and you get the following type error:
  737. .. code-block:: none
  738. fst p != w of type A
  739. when checking that the type (p : Σ A B) (w : A) → H w (snd p) of
  740. the generated with function is well-formed
  741. This message can be a little difficult to interpret since it only prints the
  742. immediate problem (``fst p != w``) and the full type of the with-function. To
  743. get a more informative error, pointing to the location in the type where the
  744. error is, you can copy and paste the with-function type from the error message
  745. and try to type check it separately.
  746. .. [McBride2004] C. McBride and J. McKinna. **The view from the left**. Journal of Functional Programming, 2004.
  747. http://strictlypositive.org/vfl.pdf.
  748. .. _std-lib: https://github.com/agda/agda-stdlib
  749. .. _agda-prelude: https://github.com/UlfNorell/agda-prelude