copatterns.lagda.rst 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. ..
  2. ::
  3. module language.copatterns where
  4. data _≡_ {A : Set} : A → A → Set where
  5. refl : {a : A} → a ≡ a
  6. data Nat : Set where
  7. zero : Nat
  8. suc : Nat → Nat
  9. data Bool : Set where
  10. true false : Bool
  11. {-# BUILTIN NATURAL Nat #-}
  12. .. _copatterns:
  13. **********
  14. Copatterns
  15. **********
  16. Consider the following record:
  17. ::
  18. record Enumeration (A : Set) : Set where
  19. constructor enumeration
  20. field
  21. start : A
  22. forward : A → A
  23. backward : A → A
  24. This gives an interface that allows us to move along the elements of a
  25. data type ``A``.
  26. For example, we can get the “third” element of a type ``A``:
  27. ..
  28. ::
  29. module _ where
  30. ::
  31. open Enumeration
  32. 3rd : {A : Set} → Enumeration A → A
  33. 3rd e = forward e (forward e (forward e (start e)))
  34. Or we can go back 2 positions starting from a given ``a``:
  35. ::
  36. backward-2 : {A : Set} → Enumeration A → A → A
  37. backward-2 e a = backward (backward a)
  38. where
  39. open Enumeration e
  40. Now, we want to use these methods on natural numbers. For this, we need
  41. a record of type ``Enumeration Nat``. Without copatterns, we would
  42. specify all the fields in a single expression:
  43. ..
  44. ::
  45. module Without-Copatterns where
  46. ::
  47. open Enumeration
  48. enum-Nat : Enumeration Nat
  49. enum-Nat = record {
  50. start = 0
  51. ; forward = suc
  52. ; backward = pred
  53. }
  54. where
  55. pred : Nat → Nat
  56. pred zero = zero
  57. pred (suc x) = x
  58. test₁ : 3rd enum-Nat ≡ 3
  59. test₁ = refl
  60. test₂ : backward-2 enum-Nat 5 ≡ 3
  61. test₂ = refl
  62. Note that if we want to use automated case-splitting and pattern
  63. matching to implement one of the fields, we need to do so in a separate
  64. definition.
  65. With *copatterns*, we can define the fields of a record as separate declarations,
  66. in the same way that we would give different cases for a function:
  67. ..
  68. ::
  69. module With-Copatterns where
  70. ::
  71. open Enumeration
  72. enum-Nat : Enumeration Nat
  73. start enum-Nat = 0
  74. forward enum-Nat n = suc n
  75. backward enum-Nat zero = zero
  76. backward enum-Nat (suc n) = n
  77. The resulting behaviour is the same in both cases:
  78. ::
  79. test₁ : 3rd enum-Nat ≡ 3
  80. test₁ = refl
  81. test₂ : backward-2 enum-Nat 5 ≡ 3
  82. test₂ = refl
  83. Copatterns in function definitions
  84. ----------------------------------
  85. In fact, we do not need to start at ``0``. We can allow the user to
  86. specify the starting element.
  87. Without copatterns, we just add the extra argument to the function declaration:
  88. ..
  89. ::
  90. module Without-Copatterns-2 where
  91. ::
  92. open Enumeration
  93. enum-Nat : Nat → Enumeration Nat
  94. enum-Nat initial = record {
  95. start = initial
  96. ; forward = suc
  97. ; backward = pred
  98. }
  99. where
  100. pred : Nat → Nat
  101. pred zero = zero
  102. pred (suc x) = x
  103. test₁ : 3rd (enum-Nat 10) ≡ 13
  104. test₁ = refl
  105. With copatterns, the function argument must be repeated once for each
  106. field in the record:
  107. ..
  108. ::
  109. module With-Copatterns-2 where
  110. ::
  111. open Enumeration
  112. enum-Nat : Nat → Enumeration Nat
  113. start (enum-Nat initial) = initial
  114. forward (enum-Nat _) n = suc n
  115. backward (enum-Nat _) zero = zero
  116. backward (enum-Nat _) (suc n) = n
  117. Mixing patterns and co-patterns
  118. -------------------------------
  119. Instead of allowing an arbitrary value, we want to limit the user to
  120. two choices: ``0`` or ``42``.
  121. Without copatterns, we would need an auxiliary definition to choose which
  122. value to start with based on the user-provided flag:
  123. ..
  124. ::
  125. module Without-Copatterns-3 where
  126. ::
  127. open Enumeration
  128. if_then_else_ : {A : Set} → Bool → A → A → A
  129. if true then x else _ = x
  130. if false then _ else y = y
  131. enum-Nat : Bool → Enumeration Nat
  132. enum-Nat ahead = record {
  133. start = if ahead then 42 else 0
  134. ; forward = suc
  135. ; backward = pred
  136. }
  137. where
  138. pred : Nat → Nat
  139. pred zero = zero
  140. pred (suc x) = x
  141. With copatterns, we can do the case analysis directly by pattern matching:
  142. ..
  143. ::
  144. module With-Copatterns-3 where
  145. ::
  146. open Enumeration
  147. enum-Nat : Bool → Enumeration Nat
  148. start (enum-Nat true) = 42
  149. start (enum-Nat false) = 0
  150. forward (enum-Nat _) n = suc n
  151. backward (enum-Nat _) zero = zero
  152. backward (enum-Nat _) (suc n) = n
  153. ..
  154. ::
  155. module Tip where
  156. .. tip:: When using copatterns to define an element of a record type,
  157. the fields of the record must be in scope. In the examples above,
  158. we use ``open Enumeration`` to bring the fields of the record into
  159. scope.
  160. Consider the first example:
  161. .. code-block:: agda
  162. enum-Nat : Enumeration Nat
  163. start enum-Nat = 0
  164. forward enum-Nat n = suc n
  165. backward enum-Nat zero = zero
  166. backward enum-Nat (suc n) = n
  167. If the fields of the ``Enumeration`` record are not in scope (in
  168. particular, the ``start`` field), then Agda will not be able to
  169. figure out what the first copattern means:
  170. .. code-block:: agda
  171. Could not parse the left-hand side start enum-Nat
  172. Operators used in the grammar:
  173. None
  174. when scope checking the left-hand side start enum-Nat in the
  175. definition of enum-Nat
  176. The solution is to open the record before using its fields:
  177. ..
  178. ::
  179. module Opened where
  180. ::
  181. open Enumeration
  182. enum-Nat : Enumeration Nat
  183. start enum-Nat = 0
  184. forward enum-Nat n = suc n
  185. backward enum-Nat zero = zero
  186. backward enum-Nat (suc n) = n