0 | ||| The content of this module is based on the
  1 | ||| tutorial post about [refined primitives](../../Doc/Primitives.md).
  2 | module Language.Reflection.Refined
  3 |
  4 | import public Language.Reflection.Refined.Util
  5 | import Language.Reflection.Util
  6 |
  7 | %default total
  8 |
  9 | --------------------------------------------------------------------------------
 10 | --          Elab Scripts
 11 | --------------------------------------------------------------------------------
 12 |
 13 | ||| Creates an `Eq` implementation for the given datatype
 14 | ||| by using the given accessor function.
 15 | |||
 16 | ||| This is hardly useful on its own but convenient when
 17 | ||| combined with other interface implementors.
 18 | |||
 19 | ||| In some occasions it is useful to have one or several
 20 | ||| phantom types for the refined type. Therefore, the exact
 21 | ||| type should be given as a `TTImp`.
 22 | |||
 23 | ||| ```idris example
 24 | ||| record AtomicNr a where
 25 | |||   constructor MkAtomicNr
 26 | |||   value : Int
 27 | |||   0 inBounds : So (1 <= value && value <= 118)
 28 | |||
 29 | ||| %runElab refinedEq "AtomicNr" `(AtomicNr a) `{{value}}
 30 | ||| ```
 31 | export
 32 | refinedEq : (type : String) -> (dt : TTImp) -> (accessor : Name) -> Elab ()
 33 | refinedEq dt t accessor =
 34 |   let eqFun := UN $ Basic $ "implEq" ++ dt
 35 |       acc   := var accessor
 36 |
 37 |    in declare
 38 |         [ interfaceHint Public eqFun `(Eq ~(t))
 39 |         , def eqFun [patClause (var eqFun) `(mkEq ((==) `on` ~(acc)))]
 40 |         ]
 41 |
 42 | ||| Creates an `Ord` implementation for the given datatype
 43 | ||| by using the given accessor function.
 44 | |||
 45 | ||| This is hardly useful on its own but convenient when
 46 | ||| combined with other interface implementors.
 47 | |||
 48 | ||| ```idris example
 49 | ||| record AtomicNr a where
 50 | |||   constructor MkAtomicNr
 51 | |||   value : Int
 52 | |||   0 inBounds : So (1 <= value && value <= 118)
 53 | |||
 54 | ||| %runElab refinedOrd "AtomicNr" `(AtomicNr a) `{{value}}
 55 | ||| ```
 56 | export
 57 | refinedOrd : (dataType : String) -> (dt : TTImp) -> (accessor : Name) -> Elab ()
 58 | refinedOrd dt t accessor =
 59 |   let ordFun := UN $ Basic $ "implOrd"  ++ dt
 60 |       acc    := var accessor
 61 |
 62 |    in declare
 63 |         [ interfaceHint Public ordFun `(Ord ~(t))
 64 |         , def ordFun [patClause (var ordFun) `(mkOrd (compare `on` ~(acc)))]
 65 |         ]
 66 |
 67 | ||| Creates a `Show` implementation for the given datatype
 68 | ||| by using the given accessor function.
 69 | |||
 70 | ||| This is hardly useful on its own but convenient when
 71 | ||| combined with other interface implementors.
 72 | |||
 73 | ||| ```idris example
 74 | ||| record AtomicNr a where
 75 | |||   constructor MkAtomicNr
 76 | |||   value : Int
 77 | |||   0 inBounds : So (1 <= value && value <= 118)
 78 | |||
 79 | ||| %runElab refinedShow "AtomicNr" `(AtomicNr a) `{{value}}
 80 | ||| ```
 81 | export
 82 | refinedShow : (type : String) -> (dt : TTImp) -> (accessor : Name) -> Elab ()
 83 | refinedShow dt t accessor =
 84 |   let showFun := UN $ Basic $ "implShow" ++ dt
 85 |       acc     := var accessor
 86 |
 87 |    in declare
 88 |         [ interfaceHint Public showFun `(Show ~(t))
 89 |         , def showFun
 90 |             [patClause (var showFun) `(mkShowPrec (\p => showPrec p . ~(acc)))]
 91 |         ]
 92 |
 93 | ||| Convenience function combining `refinedEq`, `refinedOrd`,
 94 | ||| and `refinedShow`.
 95 | export
 96 | refinedEqOrdShow :
 97 |      (type : String)
 98 |   -> (dt : TTImp)
 99 |   -> (accessor : Name)
100 |   -> Elab ()
101 | refinedEqOrdShow dt t acc = do
102 |   refinedEq dt t acc
103 |   refinedOrd dt t acc
104 |   refinedShow dt t acc
105 |
106 | ||| This creates `Eq`, `Ord`, and `Show` implementations as
107 | ||| well as conversion functions for a refined integral number.
108 | |||
109 | ||| Conversion functions are called `refine` and `fromInteger`
110 | ||| and are put in their own namespace, named after the
111 | ||| data type's name.
112 | |||
113 | ||| ```idris example
114 | ||| record AtomicNr a where
115 | |||   constructor MkAtomicNr
116 | |||   value : Int
117 | |||   0 inBounds : So (1 <= value && value <= 118)
118 | |||
119 | ||| %runElab refinedIntegral "AtomicNr"
120 | |||                          `(AtomicNr a)
121 | |||                          `{{MkAtomicNr}}
122 | |||                          `{{value}}
123 | |||                          `(Int)
124 | ||| ```
125 | |||
126 | ||| The above will result in the following declarations being generated:
127 | |||
128 | ||| ```idris example
129 | ||| Eq AtomicNr where
130 | |||   (==) = (==) `on` value
131 | |||
132 | ||| Ord AtomicNr where
133 | |||   compare = compare `on` value
134 | |||
135 | ||| Show AtomicNr where
136 | |||   showPrec p = showPrec p . value
137 | |||
138 | ||| namespace AtomicNr
139 | |||   refine : Int -> Maybe AtomicNr
140 | |||   refine = refineSo MkAtomicNr
141 | |||
142 | |||   fromInteger :
143 | |||        (v : Integer)
144 | |||     -> {auto 0 _: IsJust (refine $ fromInteger v)}
145 | |||     -> AtomicNr
146 | |||   fromInteger v = fromJust (refine $ fromInteger v)
147 | ||| ```
148 | export
149 | refinedIntegral :
150 |      (type : String)
151 |   -> (dt       : TTImp)
152 |   -> (con      : Name)
153 |   -> (accessor : Name)
154 |   -> (tpe      : TTImp)
155 |   -> Elab ()
156 | refinedIntegral dt t con acc tpe =
157 |   let ns := MkNS [dt]
158 |
159 |       -- this has to be namespaced
160 |       -- to avoid disambiguities when being used
161 |       -- in fromInteger
162 |       refineNS := var $ NS ns (UN $ Basic "refine")
163 |
164 |    in refinedEqOrdShow dt t acc >>
165 |       declare
166 |         [ INamespace EmptyFC ns
167 |           `[ public export
168 |              refine : ~(tpe) -> Maybe ~(t)
169 |              refine = refineSo ~(var con)
170 |
171 |              public export
172 |              fromInteger :
173 |                   (n : Integer)
174 |                -> {auto 0 _: IsJust (~(refineNS) $ fromInteger n)}
175 |                -> ~(t)
176 |              fromInteger n = fromJust (refine $ fromInteger n)
177 |            ]
178 |         ]
179 |
180 | export
181 | refinedIntegralDflt : (type : String) -> (tpe : TTImp) -> Elab ()
182 | refinedIntegralDflt dt tpe =
183 |   refinedIntegral dt (varStr dt) (UN $ Basic $ "Mk" ++ dt) `{value} tpe
184 |
185 | ||| Specialized version of `refinedIntegral` for data types,
186 | ||| which adhere to the following conventions:
187 | |||
188 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
189 | |||  * The field accessor of the wrapped Int is named `value`.
190 | |||  * The proof of validity consists of a single zero quantity `So`.
191 | |||
192 | ||| ```idris example
193 | ||| record AtomicNr where
194 | |||   constructor MkAtomicNr
195 | |||   value : Int
196 | |||   0 inBounds : So (1 <= value && value <= 118)
197 | |||
198 | ||| %runElab refinedInt "AtomicNr"
199 | ||| ```
200 | export
201 | refinedInt : (type : String) -> Elab ()
202 | refinedInt dt = refinedIntegralDflt dt `(Int)
203 |
204 | ||| Specialized version of `refinedIntegral` for data types,
205 | ||| which adhere to the following conventions:
206 | |||
207 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
208 | |||  * The field accessor of the wrapped Int is named `value`.
209 | |||  * The proof of validity consists of a single zero quantity `So`.
210 | export
211 | refinedBits8 : (type : String) -> Elab ()
212 | refinedBits8 dt = refinedIntegralDflt dt `(Bits8)
213 |
214 | ||| Specialized version of `refinedIntegral` for data types,
215 | ||| which adhere to the following conventions:
216 | |||
217 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
218 | |||  * The field accessor of the wrapped Int is named `value`.
219 | |||  * The proof of validity consists of a single zero quantity `So`.
220 | export
221 | refinedBits16 : (type : String) -> Elab ()
222 | refinedBits16 dt = refinedIntegralDflt dt `(Bits16)
223 |
224 | ||| Specialized version of `refinedIntegral` for data types,
225 | ||| which adhere to the following conventions:
226 | |||
227 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
228 | |||  * The field accessor of the wrapped Int is named `value`.
229 | |||  * The proof of validity consists of a single zero quantity `So`.
230 | export
231 | refinedBits32 : (type : String) -> Elab ()
232 | refinedBits32 dt = refinedIntegralDflt dt `(Bits32)
233 |
234 | ||| Specialized version of `refinedIntegral` for data types,
235 | ||| which adhere to the following conventions:
236 | |||
237 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
238 | |||  * The field accessor of the wrapped Int is named `value`.
239 | |||  * The proof of validity consists of a single zero quantity `So`.
240 | export
241 | refinedBits64 : (type : String) -> Elab ()
242 | refinedBits64 dt = refinedIntegralDflt dt `(Bits64)
243 |
244 | ||| Specialized version of `refinedIntegral` for data types,
245 | ||| which adhere to the following conventions:
246 | |||
247 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
248 | |||  * The field accessor of the wrapped Int is named `value`.
249 | |||  * The proof of validity consists of a single zero quantity `So`.
250 | export
251 | refinedInt8 : (type : String) -> Elab ()
252 | refinedInt8 dt = refinedIntegralDflt dt `(Int8)
253 |
254 | ||| Specialized version of `refinedIntegral` for data types,
255 | ||| which adhere to the following conventions:
256 | |||
257 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
258 | |||  * The field accessor of the wrapped Int is named `value`.
259 | |||  * The proof of validity consists of a single zero quantity `So`.
260 | export
261 | refinedInt16 : (type : String) -> Elab ()
262 | refinedInt16 dt = refinedIntegralDflt dt `(Int16)
263 |
264 | ||| Specialized version of `refinedIntegral` for data types,
265 | ||| which adhere to the following conventions:
266 | |||
267 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
268 | |||  * The field accessor of the wrapped Int is named `value`.
269 | |||  * The proof of validity consists of a single zero quantity `So`.
270 | export
271 | refinedInt32 : (type : String) -> Elab ()
272 | refinedInt32 dt = refinedIntegralDflt dt `(Int32)
273 |
274 | ||| Specialized version of `refinedIntegral` for data types,
275 | ||| which adhere to the following conventions:
276 | |||
277 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
278 | |||  * The field accessor of the wrapped Int is named `value`.
279 | |||  * The proof of validity consists of a single zero quantity `So`.
280 | export
281 | refinedInt64 : (type : String) -> Elab ()
282 | refinedInt64 dt = refinedIntegralDflt dt `(Int64)
283 |
284 | ||| This creates `Eq`, `Ord`, and `Show` implementations as
285 | ||| well as conversion functions for a refined floating point
286 | ||| number.
287 | |||
288 | ||| Conversion functions are called `refine` and `fromDouble`
289 | ||| and are put in their own namespace, named after the
290 | ||| data type's name.
291 | |||
292 | ||| ```idris example
293 | ||| record Abundance a where
294 | |||   constructor MkAbundance
295 | |||   value : Double
296 | |||   0 inBounds : So (0 < value && value <= 1)
297 | |||
298 | ||| %runElab refinedFloating "Abundance" `(Abundance a) `{{MkAbundance}} `{{value}}
299 | ||| ```
300 | |||
301 | ||| The above will result in the following declarations being generated:
302 | |||
303 | ||| ```idris example
304 | ||| Eq Abundance where
305 | |||   (==) = (==) `on` value
306 | |||
307 | ||| Ord Abundance where
308 | |||   compare = compare `on` value
309 | |||
310 | ||| Show Abundance where
311 | |||   showPrec p = showPrec p . value
312 | |||
313 | ||| namespace Abundance
314 | |||   refine : Double -> Maybe Abundance
315 | |||   refine = refineSo MkAbundance
316 | |||
317 | |||   fromDouble :  (v : Double)
318 | |||              -> {auto 0 _: IsJust (refine v)}
319 | |||              -> Abundance
320 | |||   fromDouble v = fromJust (refine v)
321 | ||| ```
322 | export
323 | refinedFloating :
324 |      (type : String)
325 |   -> (dt       : TTImp)
326 |   -> (con      : Name)
327 |   -> (accessor : Name)
328 |   -> Elab ()
329 | refinedFloating dt t con acc =
330 |   let ns := MkNS [dt]
331 |
332 |       -- this has to be namespaced
333 |       -- to avoid disambiguities when being used
334 |       -- in fromInteger
335 |       refineNS := var $ NS ns (UN $ Basic "refine")
336 |
337 |    in refinedEqOrdShow dt t acc >>
338 |       declare
339 |         [ INamespace EmptyFC ns
340 |           `[ public export
341 |              refine : Double -> Maybe ~(t)
342 |              refine = refineSo ~(var con)
343 |
344 |              public export
345 |              fromDouble :
346 |                   (n : Double)
347 |                -> {auto 0 _: IsJust (~(refineNS) n)}
348 |                -> ~(t)
349 |              fromDouble n = fromJust (refine n)
350 |            ]
351 |         ]
352 |
353 | ||| Convenience version of `refinedFloating` for data types,
354 | ||| which adhere to the following conventions:
355 | |||
356 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
357 | |||  * The field accessor of the wrapped Int is named `value`.
358 | |||  * The proof of validity consists of a single zero quantity `So`.
359 | export
360 | refinedDouble : (type : String) -> Elab ()
361 | refinedDouble dt =
362 |   refinedFloating dt (varStr dt) (UN $ Basic $ "Mk" ++ dt) `{value}
363 |
364 | ||| This creates `Eq`, `Ord`, and `Show` implementations as
365 | ||| well as conversion functions for a refined string value.
366 | |||
367 | ||| Conversion functions are called `refine` and `fromString`
368 | ||| and are put in their own namespace, named after the
369 | ||| data type's name.
370 | |||
371 | ||| ```idris example
372 | ||| record Html a where
373 | |||   constructor MkHtml
374 | |||   value : String
375 | |||   0 inBounds : So (isValidHtml value)
376 | |||
377 | ||| %runElab refinedText "Html" `(Html a) `{{MkHtml}} `{{value}}
378 | ||| ```
379 | |||
380 | ||| The above will result in the following declarations being generated:
381 | |||
382 | ||| ```idris example
383 | ||| Eq Html where
384 | |||   (==) = (==) `on` value
385 | |||
386 | ||| Ord Html where
387 | |||   compare = compare `on` value
388 | |||
389 | ||| Show Html where
390 | |||   showPrec p = showPrec p . value
391 | |||
392 | ||| namespace Html
393 | |||   refine : String -> Maybe Html
394 | |||   refine = refineSo MkHtml
395 | |||
396 | |||   fromString :  (v : String)
397 | |||              -> {auto 0 _: IsJust (refine v)}
398 | |||              -> Html
399 | |||   fromString v = fromJust (refine v)
400 | ||| ```
401 | export
402 | refinedText :
403 |      (type : String)
404 |   -> (dt       : TTImp)
405 |   -> (con      : Name)
406 |   -> (accessor : Name)
407 |   -> Elab ()
408 | refinedText dt t con acc =
409 |   let ns := MkNS [dt]
410 |
411 |       -- this has to be namespaced
412 |       -- to avoid disambiguities when being used
413 |       -- in fromInteger
414 |       refineNS := var $ NS ns (UN $ Basic "refine")
415 |
416 |    in refinedEqOrdShow dt t acc >>
417 |       declare
418 |         [ INamespace EmptyFC ns
419 |           `[ public export
420 |              refine : String -> Maybe ~(t)
421 |              refine = refineSo ~(var con)
422 |
423 |              public export
424 |              fromString :
425 |                   (n : String)
426 |                -> {auto 0 _: IsJust (~(refineNS) n)}
427 |                -> ~(t)
428 |              fromString n = fromJust (refine n)
429 |            ]
430 |         ]
431 |
432 | ||| Convenience version of `refinedText` for data types,
433 | ||| which adhere to the following conventions:
434 | |||
435 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
436 | |||  * The field accessor of the wrapped Int is named `value`.
437 | |||  * The proof of validity consists of a single zero quantity `So`.
438 | export
439 | refinedString : (type : String) -> Elab ()
440 | refinedString dt = refinedText dt (varStr dt) (UN $ Basic $ "Mk" ++ dt) `{value}
441 |
442 | ||| This creates `Eq`, `Ord`, and `Show` implementations as
443 | ||| well as conversion functions for a refined charater.
444 | |||
445 | ||| Conversion functions are called `refine` and `fromChar`
446 | ||| and are put in their own namespace, named after the
447 | ||| data type's name.
448 | |||
449 | ||| ```idris example
450 | ||| record Digit a where
451 | |||   constructor MkDigit
452 | |||   value : Char
453 | |||   0 inBounds : So (isDigit value)
454 | |||
455 | ||| %runElab refinedText "Digit" `(Digit a) `{{MkDigit}} `{{value}}
456 | ||| ```
457 | |||
458 | ||| The above will result in the following declarations being generated:
459 | |||
460 | ||| ```idris example
461 | ||| Eq Digit where
462 | |||   (==) = (==) `on` value
463 | |||
464 | ||| Ord Digit where
465 | |||   compare = compare `on` value
466 | |||
467 | ||| Show Digit where
468 | |||   showPrec p = showPrec p . value
469 | |||
470 | ||| namespace Digit
471 | |||   refine : Char -> Maybe Digit
472 | |||   refine = refineSo MkDigit
473 | |||
474 | |||   fromChar :  (v : Char)
475 | |||            -> {auto 0 _: IsJust (refine v)}
476 | |||            -> Digit
477 | |||   fromChar v = fromJust (refine v)
478 | ||| ```
479 | export
480 | refinedCharacter :
481 |      (type : String)
482 |   -> (dt       : TTImp)
483 |   -> (con      : Name)
484 |   -> (accessor : Name)
485 |   -> Elab ()
486 | refinedCharacter dt t con acc =
487 |   let ns := MkNS [dt]
488 |
489 |       -- this has to be namespaced
490 |       -- to avoid disambiguities when being used
491 |       -- in fromInteger
492 |       refineNS := var $ NS ns (UN $ Basic "refine")
493 |
494 |    in refinedEqOrdShow dt t acc >>
495 |       declare
496 |         [ INamespace EmptyFC ns
497 |           `[ public export
498 |              refine : Char -> Maybe ~(t)
499 |              refine = refineSo ~(var con)
500 |
501 |              public export
502 |              fromChar :
503 |                   (n : Char)
504 |                -> {auto 0 _: IsJust (~(refineNS) n)}
505 |                -> ~(t)
506 |              fromChar n = fromJust (refine n)
507 |            ]
508 |         ]
509 |
510 | ||| Convenience version of `refinedCharacter` for data types,
511 | ||| which adhere to the following conventions:
512 | |||
513 | |||  * If a data type's name is `Foo` its constructor is named `MkFoo`.
514 | |||  * The field accessor of the wrapped Char is named `value`.
515 | |||  * The proof of validity consists of a single zero quantity `So`.
516 | export
517 | refinedChar : (type : String) -> Elab ()
518 | refinedChar dt =
519 |   refinedCharacter dt (varStr dt) (UN $ Basic $ "Mk" ++ dt) `{value}
520 |