0 | ||| Utility types and functions for automatically deriving
  1 | ||| interface instances. So far, this module does not provide
  2 | ||| deriving functions for existing interfaces. See
  3 | ||| Doc.Generic4 for examples, how this could be done
  4 | ||| using the functionality provided here.
  5 | module Language.Reflection.Derive
  6 |
  7 | import Data.DPair
  8 | import Data.List1
  9 | import Data.Vect
 10 | import Decidable.Equality
 11 | import Language.Reflection
 12 | import Language.Reflection.Syntax
 13 | import Language.Reflection.Types
 14 |
 15 | %language ElabReflection
 16 |
 17 | %default total
 18 |
 19 | --------------------------------------------------------------------------------
 20 | --          Elaborateable
 21 | --------------------------------------------------------------------------------
 22 |
 23 | ||| Interface for named things that can be looked up by name using
 24 | ||| elaborator reflection.
 25 | public export
 26 | interface Named a => Elaborateable a where
 27 |   find_ : Elaboration m => Name -> m a
 28 |
 29 | ||| Utility version of `find_` taking an explicit type argument.
 30 | public export %inline
 31 | find : (0 a : Type) -> Elaborateable a => Elaboration m => Name -> m a
 32 | find _ = find_
 33 |
 34 | export %inline
 35 | Elaborateable TypeInfo where
 36 |   find_ = getInfo'
 37 |
 38 | public export %inline
 39 | Named a => Named (Subset a p) where
 40 |   (.getName) (Element v _) = v.getName
 41 |
 42 | --------------------------------------------------------------------------------
 43 | --          Names
 44 | --------------------------------------------------------------------------------
 45 |
 46 | ||| Generates the name of a function implementing some functionality
 47 | ||| for the given type.
 48 | export
 49 | funName : Named a => a -> String -> Name
 50 | funName n fun = UN $ Basic $ fun ++ n.nameStr
 51 |
 52 | ||| Generates the name of an interface's implementation function
 53 | export
 54 | implName : Named a => a -> String -> Name
 55 | implName n iface = UN $ Basic $ "impl" ++ iface ++ n.nameStr
 56 |
 57 | --------------------------------------------------------------------------------
 58 | --          Arguments
 59 | --------------------------------------------------------------------------------
 60 |
 61 | ||| A single constructor argument, for which we have `n` bound
 62 | ||| variables on the left hand side of a pattern match clause, and
 63 | ||| which is refined by predicate `p`.
 64 | public export
 65 | record BoundArg (n : Nat) (p : Arg -> Type) where
 66 |   constructor BA
 67 |   arg  : Arg
 68 |   vars : Vect n Name
 69 |   prf  : p arg
 70 |
 71 | public export
 72 | split : Vect k (Vect (S n) a) -> (Vect k a, Vect k (Vect n a))
 73 | split []                 = ([],[])
 74 | split ((x :: xs) :: yss) =
 75 |   let (zs,zss) := split yss
 76 |    in (x :: zs, xs :: zss)
 77 |
 78 | ||| Refine a list of constructor arguments with the given predicate
 79 | ||| and pair them with a number of bound variable names.
 80 | export
 81 | boundArgs :
 82 |      {0 p : Arg -> Type}
 83 |   -> (f : (a : Arg) -> Maybe (p a))
 84 |   -> Vect n Arg
 85 |   -> Vect k (Vect n Name)
 86 |   -> SnocList (BoundArg k p)
 87 | boundArgs f = go Lin
 88 |   where
 89 |     go :  SnocList (BoundArg k p)
 90 |        -> Vect m Arg
 91 |        -> Vect k (Vect m Name)
 92 |        -> SnocList (BoundArg k p)
 93 |     go sx (x :: xs) ns =
 94 |       let (y, ys) := split ns
 95 |        in case f x of
 96 |             Just prf => go (sx :< BA x y prf) xs ys
 97 |             Nothing  => go sx xs ys
 98 |     go sx []        _  = sx
 99 |
100 | public export
101 | data Explicit : Arg -> Type where
102 |   IsExplicit : Explicit (MkArg c ExplicitArg n t)
103 |
104 | public export
105 | explicit : (a : Arg) -> Maybe (Explicit a)
106 | explicit (MkArg _ ExplicitArg _ _) = Just IsExplicit
107 | explicit _                         = Nothing
108 |
109 | public export
110 | data NamedArg : Arg -> Type where
111 |   IsNamed : NamedArg (MkArg c p (Just (UN $ Basic n)) t)
112 |
113 | public export
114 | named : (a : Arg) -> Maybe (NamedArg a)
115 | named (MkArg _ _ (Just $ UN $ Basic n) _) = Just IsNamed
116 | named _                                   = Nothing
117 |
118 | public export
119 | argName : (a : Arg) -> {auto 0 prf : NamedArg a} -> Name
120 | argName (MkArg _ _ (Just $ UN $ Basic n) _) {prf = IsNamed} = UN $ Basic n
121 |
122 | public export
123 | data Unerased : Arg -> Type where
124 |   U1 : Unerased (MkArg M1 p n t)
125 |   UW : Unerased (MkArg MW p n t)
126 |
127 | public export
128 | unerased : (a : Arg) -> Maybe (Unerased a)
129 | unerased (MkArg M0 _ _ _) = Nothing
130 | unerased (MkArg M1 _ _ _) = Just U1
131 | unerased (MkArg MW _ _ _) = Just UW
132 |
133 | public export
134 | data Erased : Arg -> Type where
135 |   IsErased : Erased (MkArg M0 p n t)
136 |
137 | public export
138 | erased : (a : Arg) -> Maybe (Erased a)
139 | erased (MkArg M0 _ _ _) = Just IsErased
140 | erased _                = Nothing
141 |
142 | public export
143 | 0 Regular : Arg -> Type
144 | Regular a = (Unerased a, Explicit a)
145 |
146 | public export
147 | regular : (a : Arg) -> Maybe (Regular a)
148 | regular a = [| MkPair (unerased a) (explicit a) |]
149 |
150 | public export
151 | 0 RegularNamed : Arg -> Type
152 | RegularNamed a = (NamedArg a, Regular a)
153 |
154 | public export
155 | regularNamed : (a : Arg) -> Maybe (RegularNamed a)
156 | regularNamed a = [| MkPair (named a) (regular a) |]
157 |
158 | public export
159 | 0 NamedExplicit : Arg -> Type
160 | NamedExplicit a = (NamedArg a, Explicit a)
161 |
162 | public export
163 | namedExplicit : (a : Arg) -> Maybe (NamedExplicit a)
164 | namedExplicit a = [| MkPair (named a) (explicit a) |]
165 |
166 | public export
167 | toNamed : BoundArg n p -> Maybe (BoundArg n (\a => (NamedArg a, p a)))
168 | toNamed (BA arg vars prf) = case named arg of
169 |   Just v  => Just (BA arg vars (v, prf))
170 |   Nothing => Nothing
171 |
172 | --------------------------------------------------------------------------------
173 | --          Utilities
174 | --------------------------------------------------------------------------------
175 |
176 | export
177 | failRecord : String -> Res a
178 | failRecord s =
179 |   Left "Interface \{s} can only be derived for single-constructor data types"
180 |
181 | ||| Generates a pattern clause for accumulating the arguments
182 | ||| of a singled data constructor.
183 | |||
184 | ||| This is used, for instance, to implement `showPrec`, when
185 | ||| deriving `Show` implementations.
186 | |||
187 | ||| @ p   : The predicate used to refine the constructor's arguments
188 | |||
189 | ||| @ f   : Refining function.
190 | |||
191 | ||| @ lhs : Adjusts the left-hand side of the clause.
192 | |||         The argument corresponds to the constructor with
193 | |||         all explicit arguments bound to variables named
194 | |||         `x0`, `x1` etc.
195 | |||
196 | ||| @ rhs : Adjusts the right-hand side of the clause.
197 | |||         The `SnocList` contains the arguments, as transformed
198 | |||         by `arg`.
199 | |||
200 | ||| @ arg : Processes a single argument
201 | |||
202 | ||| @ con : The constructor to process.
203 | export
204 | accumArgs :
205 |      {0 p : Arg -> Type}
206 |   -> (f : (a : Arg) -> Maybe (p a))
207 |   -> (lhs : TTImp -> TTImp)
208 |   -> (rhs : SnocList TTImp -> TTImp)
209 |   -> (arg : BoundArg 1 p -> TTImp)
210 |   -> (con : Con n vs)
211 |   -> Clause
212 | accumArgs f lhs rhs arg c =
213 |   let xs := freshNames "x" c.arty
214 |       cx := bindCon c xs
215 |       sx := map arg (boundArgs f c.args [xs])
216 |    in patClause (lhs cx) (rhs sx)
217 |
218 | ||| Generates a pattern clause for accumulating the arguments
219 | ||| of two equivalent data constructors.
220 | |||
221 | ||| This is used, for instance, to implement `(==)`, when
222 | ||| deriving `Eq` implementations.
223 | |||
224 | ||| @ p   : The predicate used to refine the constructor's arguments
225 | |||
226 | ||| @ f   : Refining function.
227 | |||
228 | ||| @ lhs : Adjusts the left-hand side of the clause.
229 | |||         The first argument corresponds to the constructor with
230 | |||         all explicit arguments bound to variables named
231 | |||         `x0`, `x1` etc., the second to the constructor
232 | |||         with bound explicit arguments namd `y0`, `y1` etc.
233 | |||
234 | ||| @ rhs : Adjusts the right-hand side of the clause.
235 | |||         The `SnocList` contains the arguments, as transformed
236 | |||         by `arg`.
237 | |||
238 | ||| @ arg : Processes a single pair of arguments
239 | |||
240 | ||| @ con : The constructor to process.
241 | export
242 | accumArgs2 :
243 |      {0 p : Arg -> Type}
244 |   -> (f : (a : Arg) -> Maybe (p a))
245 |   -> (lhs : TTImp -> TTImp -> TTImp)
246 |   -> (rhs : SnocList TTImp -> TTImp)
247 |   -> (arg : BoundArg 2 p -> TTImp)
248 |   -> (con : Con n vs)
249 |   -> Clause
250 | accumArgs2 f lhs rhs arg c =
251 |   let xs := freshNames "x" c.arty
252 |       ys := freshNames "y" c.arty
253 |       cx := bindCon c xs
254 |       cy := bindCon c ys
255 |       sx := map arg (boundArgs f c.args [xs,ys])
256 |    in patClause (lhs cx cy) (rhs sx)
257 |
258 | ||| Generates a pattern clause for mapping the arguments
259 | ||| of a data constructors over a unary function.
260 | |||
261 | ||| This is used, for instance, to implement `abs`, when
262 | ||| deriving `Abs` implementations.
263 | |||
264 | ||| @ p   : The predicate used to refine the constructor's arguments
265 | |||
266 | ||| @ f   : Refining function.
267 | |||
268 | ||| @ lhs : Adjusts the left-hand side of the clause.
269 | |||         The argument corresponds to the constructor with
270 | |||         all explicit arguments bound to variables named
271 | |||         `x0`, `x1` etc.
272 | |||
273 | ||| @ arg : Processes a single argument
274 | |||
275 | ||| @ con : The constructor to process.
276 | export
277 | mapArgs :
278 |      {0 p : Arg -> Type}
279 |   -> (f : (a : Arg) -> Maybe (p a))
280 |   -> (lhs : TTImp -> TTImp)
281 |   -> (arg : BoundArg 1 p -> TTImp)
282 |   -> (con : Con n vs)
283 |   -> Clause
284 | mapArgs f lhs arg c =
285 |   let xs := freshNames "x" c.arty
286 |       cx := bindCon c xs
287 |       sx := map arg (boundArgs f c.args [xs])
288 |    in patClause (lhs cx) (appAll c.name $ sx <>> [])
289 |
290 | ||| Generates a pattern clause for mapping the arguments
291 | ||| of two data constructors over a binary function.
292 | |||
293 | ||| This is used, for instance, to implement `(+)`, when
294 | ||| deriving `Num` implementations.
295 | |||
296 | ||| @ p   : The predicate used to refine the constructor's arguments
297 | |||
298 | ||| @ f   : Refining function.
299 | |||
300 | ||| @ lhs : Adjusts the left-hand side of the clause.
301 | |||         The first argument corresponds to the constructor with
302 | |||         all explicit arguments bound to variables named
303 | |||         `x0`, `x1` etc., the second to the constructor
304 | |||         with bound explicit arguments namd `y0`, `y1` etc.
305 | |||
306 | ||| @ arg : Processes a pair of arguments
307 | |||
308 | ||| @ con : The constructor to process.
309 | export
310 | mapArgs2 :
311 |      {0 p : Arg -> Type}
312 |   -> (f : (a : Arg) -> Maybe (p a))
313 |   -> (lhs : TTImp -> TTImp -> TTImp)
314 |   -> (arg : BoundArg 2 p -> TTImp)
315 |   -> (con : Con n vs)
316 |   -> Clause
317 | mapArgs2 f lhs arg c =
318 |   let xs := freshNames "x" c.arty
319 |       ys := freshNames "y" c.arty
320 |       cx := bindCon c xs
321 |       cy := bindCon c ys
322 |       sx := map arg (boundArgs f c.args [xs,ys])
323 |    in patClause (lhs cx cy) (appAll c.name $ sx <>> [])
324 |
325 | ||| Generates a pattern clause for creating and applying
326 | ||| constructor arguments.
327 | |||
328 | ||| This is used, for instance, to implement `fromInteger`, when
329 | ||| deriving `Num` implementations for record types.
330 | |||
331 | ||| @ p   : The predicate used to refine the constructor's arguments
332 | |||
333 | ||| @ f   : Refining function.
334 | |||
335 | ||| @ arg : Processes a single argument
336 | |||
337 | ||| @ con : The constructor to process.
338 | export
339 | injArgs :
340 |      {0 p : Arg -> Type}
341 |   -> (f : (a : Arg) -> Maybe (p a))
342 |   -> (arg : BoundArg 0 p -> TTImp)
343 |   -> (con : Con n vs)
344 |   -> TTImp
345 | injArgs f arg c =
346 |   let sx := map arg (boundArgs f c.args [])
347 |    in appAll c.name (sx <>> [])
348 |
349 | --------------------------------------------------------------------------------
350 | --          Deriving Implementations
351 | --------------------------------------------------------------------------------
352 |
353 | ||| A top-level declaration plus its definition.
354 | public export
355 | record TopLevel where
356 |   constructor TL
357 |   claim : Decl
358 |   defn  : Decl
359 |
360 | ||| Creates a function declaration with a `%hint` and `%inline`
361 | ||| annotation.
362 | |||
363 | ||| This is what you want if you use separate top-level function
364 | ||| for the interface's implementation and the actual implementation
365 | ||| just adds those functions to the interface constructor.
366 | public export %inline
367 | implClaimVis : Visibility -> Name -> TTImp -> Decl
368 | implClaimVis vis = interfaceHintOpts vis [Inline]
369 |
370 | ||| Creates a function declaration with a `%hint` and `%inline`
371 | ||| annotation.
372 | |||
373 | ||| This is what you want if you use separate top-level function
374 | ||| for the interface's implementation and the actual implementation
375 | ||| just adds those functions to the interface constructor.
376 | public export %inline
377 | implClaim : Name -> TTImp -> Decl
378 | implClaim = implClaimVis Public
379 |
380 | ||| Creates the function type for an interface implementation including
381 | ||| the required implicit and auto-implicit arguments.
382 | |||
383 | ||| For instance, if `Name` is `"Eq"` and the data type in question is
384 | ||| `Either` with parameter names `a` and `b`, the `TTImp` returned
385 | ||| corresponds to
386 | |||
387 | ||| ```idris
388 | ||| {0 a : _} -> {0 b : _} -> Eq a => Eq b => Eq (Either a b)`
389 | ||| ```
390 | export
391 | implType : Name -> ParamTypeInfo -> TTImp
392 | implType n p = piAll (var n `app` p.applied) (allImplicits p n)
393 |
394 | ||| Extracts the innermost target of a function application.
395 | ||| For instance, for `Foo @{bar} baz {n = 12}`, this will extract `Foo`.
396 | export
397 | unAppAny : TTImp -> TTImp
398 | unAppAny (IApp fc s t)         = unAppAny s
399 | unAppAny (INamedApp fc s nm t) = unAppAny s
400 | unAppAny (IAutoApp fc s t)     = unAppAny s
401 | unAppAny (IWithApp fc s t)     = unAppAny s
402 | unAppAny t                     = t
403 |
404 | ||| Tries to extract the result type from the current goal when
405 | ||| deriving custom interface implementations.
406 | |||
407 | ||| For instance, if the goal type is `Eq (Either a b)`, this
408 | ||| returns a `TTImp` corresponding to `Either a b` plus the
409 | ||| name of the data constructor `Either`.
410 | export
411 | extractResult : TTImp -> Maybe (TTImp, Name)
412 | extractResult (IApp _ _ tpe) = case unAppAny tpe of
413 |   IVar _ nm => Just (tpe, nm)
414 |   _         => Nothing
415 | extractResult _              = Nothing
416 |
417 | export %inline
418 | sequenceJoin : Applicative f => List (f $ List a) -> f (List a)
419 | sequenceJoin = map join . sequence
420 |
421 | public export
422 | record ParamInfo where
423 |   constructor PI
424 |   name     : Name
425 |   strategy : (: Nat) -> Maybe (Exists $ ParamPattern n)
426 |   goals    : List (List Name -> ParamTypeInfo -> Res (List TopLevel))
427 |
428 | fromParamInfo : List Name -> TypeInfo -> ParamInfo -> Res (List TopLevel)
429 | fromParamInfo nms ti (PI n f gs) = case f ti.arty of
430 |   Just (Evidence _ pat) => do
431 |     pti <- paramType ti pat
432 |     map join $ traverse (\g => g nms pti) gs
433 |
434 |   Nothing               => Left """
435 |     Parameter pattern does not match type constructor arity (\{show ti.arty}).
436 |     Note, that the arity includes implicit arguments, so those have to
437 |     be considered in the pattern, too.
438 |     """
439 |
440 | export
441 | deriveParam : Elaboration m => List ParamInfo -> m ()
442 | deriveParam is = do
443 |   ts <- traverse (find TypeInfo . name) is
444 |   let ns := map (.getName) ts
445 |
446 |   Right tls <- pure . map join . sequence $ zipWith (fromParamInfo ns) ts is
447 |     | Left err => fail err
448 |
449 |   let claims := map claim tls
450 |       defns  := map defn tls
451 |
452 |   logMsg "derive.claims" 1 $ unlines (map show claims)
453 |   logMsg "derive.definitions" 1 $ unlines (map show defns)
454 |   declare $ claims ++ defns
455 |
456 | export
457 | allParams : (n : Nat) -> Maybe (Exists $ ParamPattern n)
458 | allParams n = Just $ Evidence _ $ paramsOnly n
459 |
460 | export
461 | allIndices : (n : Nat) -> Maybe (Exists $ ParamPattern n)
462 | allIndices n = Just $ Evidence _ $ indicesOnly n
463 |
464 | export
465 | match : ParamPattern m k -> (n : Nat) -> Maybe (Exists $ ParamPattern n)
466 | match p n = map (\v => Evidence _ v) $ go n p
467 |
468 |   where
469 |     go : (z : Nat) -> ParamPattern x y -> Maybe (ParamPattern z y)
470 |     go 0 []           = Just []
471 |     go (S j) (a :: b) = (a ::) <$> go j b
472 |     go 0 (_ :: _)     = Nothing
473 |     go (S j) []       = Nothing
474 |
475 | export
476 | deriveMutual :
477 |      Elaboration m
478 |   => List Name
479 |   -> List (List Name -> ParamTypeInfo -> Res (List TopLevel))
480 |   -> m ()
481 | deriveMutual ns fs = deriveParam $ map (\n => PI n allParams fs) ns
482 |
483 | ||| Given a name of a parameterized data type plus a list of
484 | ||| interface generation functions, tries
485 | ||| to implement these interfaces automatically using
486 | ||| elaborator reflection.
487 | |||
488 | ||| Again, see Doc.Generic4 for a tutorial and examples how
489 | ||| to use this.
490 | export %inline
491 | derive :
492 |      Elaboration m
493 |   => Name
494 |   -> List (List Name -> ParamTypeInfo -> Res (List TopLevel))
495 |   -> m ()
496 | derive n = deriveMutual [n]
497 |
498 | export %inline
499 | deriveIndexed :
500 |      Elaboration m
501 |   => Name
502 |   -> List (List Name -> ParamTypeInfo -> Res (List TopLevel))
503 |   -> m ()
504 | deriveIndexed n fs = deriveParam [PI n allIndices fs]
505 |
506 | export %inline
507 | derivePattern :
508 |      Elaboration m
509 |   => Name
510 |   -> ParamPattern n k
511 |   -> List (List Name -> ParamTypeInfo -> Res (List TopLevel))
512 |   -> m ()
513 | derivePattern n pat fs = deriveParam [PI n (match pat) fs]
514 |
515 | --------------------------------------------------------------------------------
516 | --          Interface Factories
517 | --------------------------------------------------------------------------------
518 |
519 | ||| Like `MkEq` but generates (/=) from the passed `eq` function.
520 | public export %inline
521 | mkEq : (eq : a -> a -> Bool) -> Eq a
522 | mkEq eq = MkEq eq (\a,b => not $ eq a b)
523 |
524 | ||| Creates an `Ord` value from the passed comparison function
525 | ||| using default implementations based on `comp` for all
526 | ||| other function.
527 | public export
528 | mkOrd : Eq a => (comp : a -> a -> Ordering) -> Ord a
529 | mkOrd comp =
530 |   MkOrd comp
531 |     (\a,b => comp a b == LT)
532 |     (\a,b => comp a b == GT)
533 |     (\a,b => comp a b /= GT)
534 |     (\a,b => comp a b /= LT)
535 |     (\a,b => if comp a b == GT then a else b)
536 |     (\a,b => if comp a b == LT then a else b)
537 |
538 | ||| Creates a `Show` value from the passed `show` functions.
539 | public export
540 | mkShow : (show : a -> String) -> Show a
541 | mkShow show = MkShow show (\_ => show)
542 |
543 | ||| Creates a `Show` value from the passed `showPrec` functions.
544 | public export
545 | mkShowPrec : (showPrec : Prec -> a -> String) -> Show a
546 | mkShowPrec showPrec = MkShow (showPrec Open) showPrec
547 |
548 | ||| Creates a `DecEq` value from the passed implementation function
549 | ||| for `decEq`
550 | public export
551 | mkDecEq : (decEq : (x1 : a) -> (x2 : a) -> Dec (x1 = x2)) -> DecEq a
552 | mkDecEq = %runElab check (var $ singleCon "DecEq")
553 |