Skip to content

Commit 5c47360

Browse files
authored
Wingman: Tactical support for deep recursion (#1944)
* Make `try` a tactic combinator instead of an operator * wip * with_arg and deep_of * Tactic arguments require parentheses * Add deep_of test * Fix MetaTry * Haddock * with_arg test * It was a stupid inside joke
1 parent b214231 commit 5c47360

File tree

12 files changed

+234
-30
lines changed

12 files changed

+234
-30
lines changed

plugins/hls-tactics-plugin/COMMANDS.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ running `application` will produce:
2323
```haskell
2424
f (_ :: a)
2525
```
26+
2627
## apply
2728

2829
arguments: single reference.
@@ -46,6 +47,7 @@ running `apply f` will produce:
4647
```haskell
4748
f (_ :: a)
4849
```
50+
4951
## assume
5052

5153
arguments: single reference.
@@ -69,6 +71,7 @@ running `assume some_a_val` will produce:
6971
```haskell
7072
some_a_val
7173
```
74+
7275
## assumption
7376

7477
arguments: none.
@@ -92,6 +95,7 @@ running `assumption` will produce:
9295
```haskell
9396
some_a_val
9497
```
98+
9599
## auto
96100

97101
arguments: none.
@@ -116,6 +120,7 @@ running `auto` will produce:
116120
```haskell
117121
g . f
118122
```
123+
119124
## binary
120125

121126
arguments: none.
@@ -139,6 +144,7 @@ running `binary` will produce:
139144
```haskell
140145
(_3 :: a -> a -> Int) (_1 :: a) (_2 :: a)
141146
```
147+
142148
## cata
143149

144150
arguments: single reference.
@@ -168,6 +174,7 @@ case x of
168174
a2_c = f a2
169175
in _
170176
```
177+
171178
## collapse
172179

173180
arguments: none.
@@ -193,6 +200,7 @@ running `collapse` will produce:
193200
```haskell
194201
(_ :: a -> a -> a -> a) a1 a2 a3
195202
```
203+
196204
## ctor
197205

198206
arguments: single reference.
@@ -214,6 +222,7 @@ running `ctor Just` will produce:
214222
```haskell
215223
Just (_ :: a)
216224
```
225+
217226
## destruct
218227

219228
arguments: single reference.
@@ -239,6 +248,7 @@ case a of
239248
False -> _
240249
True -> _
241250
```
251+
242252
## destruct_all
243253

244254
arguments: none.
@@ -271,6 +281,7 @@ case a of
271281
Nothing -> _
272282
Just i -> _
273283
```
284+
274285
## homo
275286

276287
arguments: single reference.
@@ -298,6 +309,7 @@ case e of
298309
Left a -> Left (_ :: x)
299310
Right b -> Right (_ :: y)
300311
```
312+
301313
## intro
302314

303315
arguments: single binding.
@@ -319,6 +331,7 @@ running `intro aye` will produce:
319331
```haskell
320332
\aye -> (_ :: b -> c -> d)
321333
```
334+
322335
## intros
323336

324337
arguments: varadic binding.
@@ -368,6 +381,29 @@ running `intros x y z w` will produce:
368381
```haskell
369382
\x y z -> (_ :: d)
370383
```
384+
385+
## nested
386+
387+
arguments: single reference.
388+
non-deterministic.
389+
390+
> Nest the given function (in module scope) with itself arbitrarily many times. NOTE: The resulting function is necessarily unsaturated, so you will likely need `with_arg` to use this tactic in a saturated context.
391+
392+
393+
### Example
394+
395+
Given:
396+
397+
```haskell
398+
_ :: [(Int, Either Bool a)] -> [(Int, Either Bool b)]
399+
```
400+
401+
running `nested fmap` will produce:
402+
403+
```haskell
404+
fmap (fmap (fmap _))
405+
```
406+
371407
## obvious
372408

373409
arguments: none.
@@ -389,6 +425,7 @@ running `obvious` will produce:
389425
```haskell
390426
[]
391427
```
428+
392429
## pointwise
393430

394431
arguments: tactic.
@@ -412,6 +449,7 @@ running `pointwise (use mappend)` will produce:
412449
```haskell
413450
mappend _ _
414451
```
452+
415453
## recursion
416454

417455
arguments: none.
@@ -435,6 +473,7 @@ running `recursion` will produce:
435473
```haskell
436474
foo (_ :: Int) (_ :: b)
437475
```
476+
438477
## sorry
439478

440479
arguments: none.
@@ -456,6 +495,7 @@ running `sorry` will produce:
456495
```haskell
457496
_ :: b
458497
```
498+
459499
## split
460500

461501
arguments: none.
@@ -477,6 +517,38 @@ running `split` will produce:
477517
```haskell
478518
Right (_ :: b)
479519
```
520+
521+
## try
522+
523+
arguments: tactic.
524+
non-deterministic.
525+
526+
> Simultaneously run and do not run a tactic. Subsequent tactics will bind on both states.
527+
528+
529+
### Example
530+
531+
Given:
532+
533+
```haskell
534+
f :: a -> b
535+
536+
_ :: b
537+
```
538+
539+
running `try (apply f)` will produce:
540+
541+
```haskell
542+
-- BOTH of:
543+
544+
f (_ :: a)
545+
546+
-- and
547+
548+
_ :: b
549+
550+
```
551+
480552
## unary
481553

482554
arguments: none.
@@ -500,6 +572,7 @@ running `unary` will produce:
500572
```haskell
501573
(_2 :: a -> Int) (_1 :: a)
502574
```
575+
503576
## use
504577

505578
arguments: single reference.
@@ -523,3 +596,28 @@ running `use isSpace` will produce:
523596
```haskell
524597
isSpace (_ :: Char)
525598
```
599+
600+
## with_arg
601+
602+
arguments: none.
603+
deterministic.
604+
605+
> Fill the current goal with a function application. This can be useful when you'd like to fill in the argument before the function, or when you'd like to use a non-saturated function in a saturated context.
606+
607+
608+
### Example
609+
610+
> Where `a` is a new unifiable type variable.
611+
612+
Given:
613+
614+
```haskell
615+
_ :: r
616+
```
617+
618+
running `with_arg` will produce:
619+
620+
```haskell
621+
(_2 :: a -> r) (_1 :: a)
622+
```
623+

plugins/hls-tactics-plugin/src/Wingman/GHC.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ freshTyvars t = do
113113
case M.lookup tv reps of
114114
Just tv' -> tv'
115115
Nothing -> tv
116-
) t
116+
) $ snd $ tcSplitForAllTys t
117117

118118

119119
------------------------------------------------------------------------------

plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ deriveFmap = do
3535
try intros
3636
overAlgebraicTerms homo
3737
choice
38-
[ overFunctions apply >> auto' 2
38+
[ overFunctions (apply Saturated) >> auto' 2
3939
, assumption
4040
, recursion
4141
]

plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser.hs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ commands =
188188

189189
, command "apply" Deterministic (Ref One)
190190
"Apply the given function from *local* scope."
191-
(pure . useNameFromHypothesis apply)
191+
(pure . useNameFromHypothesis (apply Saturated))
192192
[ Example
193193
Nothing
194194
["f"]
@@ -295,7 +295,7 @@ commands =
295295
"Fill the current hole with a call to the defining function."
296296
( pure $
297297
fmap listToMaybe getCurrentDefinitions >>= \case
298-
Just (self, _) -> useNameFromContext apply self
298+
Just (self, _) -> useNameFromContext (apply Saturated) self
299299
Nothing -> E.throwError $ TacticPanic "no defining function"
300300
)
301301
[ Example
@@ -308,13 +308,7 @@ commands =
308308

309309
, command "use" Deterministic (Ref One)
310310
"Apply the given function from *module* scope."
311-
( \occ -> pure $ do
312-
ctx <- ask
313-
ty <- case lookupNameInContext occ ctx of
314-
Just ty -> pure ty
315-
Nothing -> CType <$> getOccNameType occ
316-
apply $ createImportedHyInfo occ ty
317-
)
311+
(pure . use Saturated)
318312
[ Example
319313
(Just "`import Data.Char (isSpace)`")
320314
["isSpace"]
@@ -354,6 +348,44 @@ commands =
354348
"(_ :: a -> a -> a -> a) a1 a2 a3"
355349
]
356350

351+
, command "try" Nondeterministic Tactic
352+
"Simultaneously run and do not run a tactic. Subsequent tactics will bind on both states."
353+
(pure . R.try)
354+
[ Example
355+
Nothing
356+
["(apply f)"]
357+
[ EHI "f" "a -> b"
358+
]
359+
(Just "b")
360+
$ T.pack $ unlines
361+
[ "-- BOTH of:\n"
362+
, "f (_ :: a)"
363+
, "\n-- and\n"
364+
, "_ :: b"
365+
]
366+
]
367+
368+
, command "nested" Nondeterministic (Ref One)
369+
"Nest the given function (in module scope) with itself arbitrarily many times. NOTE: The resulting function is necessarily unsaturated, so you will likely need `with_arg` to use this tactic in a saturated context."
370+
(pure . nested)
371+
[ Example
372+
Nothing
373+
["fmap"]
374+
[]
375+
(Just "[(Int, Either Bool a)] -> [(Int, Either Bool b)]")
376+
"fmap (fmap (fmap _))"
377+
]
378+
379+
, command "with_arg" Deterministic Nullary
380+
"Fill the current goal with a function application. This can be useful when you'd like to fill in the argument before the function, or when you'd like to use a non-saturated function in a saturated context."
381+
(pure with_arg)
382+
[ Example
383+
(Just "Where `a` is a new unifiable type variable.")
384+
[]
385+
[]
386+
(Just "r")
387+
"(_2 :: a -> r) (_1 :: a)"
388+
]
357389
]
358390

359391

@@ -369,14 +401,9 @@ oneTactic =
369401
tactic :: Parser (TacticsM ())
370402
tactic = flip P.makeExprParser operators oneTactic
371403

372-
bindOne :: TacticsM a -> TacticsM a -> TacticsM a
373-
bindOne t t1 = t R.<@> [t1]
374-
375404
operators :: [[P.Operator Parser (TacticsM ())]]
376405
operators =
377-
[ [ P.Prefix (symbol "*" $> R.many_) ]
378-
, [ P.Prefix (symbol "try" $> R.try) ]
379-
, [ P.InfixR (symbol "|" $> (R.<%>) )]
406+
[ [ P.InfixR (symbol "|" $> (R.<%>) )]
380407
, [ P.InfixL (symbol ";" $> (>>))
381408
, P.InfixL (symbol "," $> bindOne)
382409
]
@@ -426,7 +453,7 @@ parseMetaprogram
426453
-- | Automatically generate the metaprogram command reference.
427454
writeDocumentation :: IO ()
428455
writeDocumentation =
429-
writeFile "plugins/hls-tactics-plugin/COMMANDS.md" $
456+
writeFile "COMMANDS.md" $
430457
unlines
431458
[ "# Wingman Metaprogram Command Reference"
432459
, ""

plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ prettyCommand (MC name syn det desc _ exs) = vsep
150150
, ">" <+> align (pretty desc)
151151
, mempty
152152
, vsep $ fmap (prettyExample name) exs
153+
, mempty
153154
]
154155

155156

0 commit comments

Comments
 (0)