th-desugar icon indicating copy to clipboard operation
th-desugar copied to clipboard

Support as-patterns in pattern synonyms with view patterns

Open expipiplus1 opened this issue 3 years ago • 13 comments

I guess a bit of a WIP, Maybe needs neatening and tests

See https://github.com/goldfirere/th-desugar/issues/174

expipiplus1 avatar Jan 14 '23 10:01 expipiplus1

View patterns too, a tiny change on top of as-patterns.

expipiplus1 avatar Jan 14 '23 12:01 expipiplus1

ok, I think I've addressed the comments, as well as added view pattern support everywhere (it fell out very naturally)

I've keps dsPatX around for a deprecation cycle if we care about that.

expipiplus1 avatar Jan 15 '23 08:01 expipiplus1

err, actually I think there's a bug here I still need to sort!

expipiplus1 avatar Jan 15 '23 08:01 expipiplus1

The bug here is that now dsPatOverExp return a less fallible pattern, having moved most of the matching on as-patterns into the subsequent let expression. The consequence of this is apparent in the following:

do 
  let xs = [1,2]
  x@1 <- xs
  pure x

This used to desugar to the equivalent of:

do 
  let xs = [1,2]
  1 <- xs
  let x = 1
  pure x

But now it desugars to the equivalent of:

do
  let xs  = [1,2]
  x <- xs
  let 1 = x
  pure x

Apart from it being incorrect, I do prefer the second one as there isn't any pattern -> expression transmutation happening.

expipiplus1 avatar Jan 16 '23 03:01 expipiplus1

OK. I could take a look, but there is currently a build failure in the patch, as caught by CI:

[ 9 of 14] Compiling Language.Haskell.TH.Desugar.Core ( Language/Haskell/TH/Desugar/Core.hs, /__w/th-desugar/th-desugar/dist-newstyle/build/x86_64-linux/ghc-9.2.2/th-desugar-1.14/build/Language/Haskell/TH/Desugar/Core.o, /__w/th-desugar/th-desugar/dist-newstyle/build/x86_64-linux/ghc-9.2.2/th-desugar-1.14/build/Language/Haskell/TH/Desugar/Core.dyn_o )

Language/Haskell/TH/Desugar/Core.hs:806:22: error:
    Variable not in scope:
      dsPatWithViewPatterns :: Pat -> q (DPat, t0 (DPat, DExp))
    |
806 |   (pat', subPats) <- dsPatWithViewPatterns pat
    |                      ^^^^^^^^^^^^^^^^^^^^^

Language/Haskell/TH/Desugar/Core.hs:950:19: error:
    Variable not in scope:
      dsPatWithViewPatterns :: Pat -> q (DPat, [(DPat, DExp)])
    |
950 |   (pat', vars) <- dsPatWithViewPatterns pat
    |                   ^^^^^^^^^^^^^^^^^^^^^

RyanGlScott avatar Jan 16 '23 15:01 RyanGlScott

Ah, I didn't mean that I had uncovered an existing bug; I've introduced a new one. Before the "extra patterns" were designed/assumed to be infallible, but now they're not which leads to problems in do notation.

It shouldn't be the end of the world to fix, although it's tempting just to reinstate the old "no view patterns" behaviour.

expipiplus1 avatar Jan 17 '23 14:01 expipiplus1

It shouldn't be the end of the world to fix, although it's tempting just to reinstate the old "no view patterns" behaviour.

OK. For what it's worth, I would be fine with reinstating the old behavior, opening a separate issue to track the remaining tasks.

RyanGlScott avatar Jan 17 '23 14:01 RyanGlScott

Ok, I reverted to the previous behaviour and opened https://github.com/goldfirere/th-desugar/issues/177

expipiplus1 avatar Jan 18 '23 08:01 expipiplus1

Thank you for taking the time for a thorough review @RyanGlScott!

For the tests, it would be really nice to be able to describe them like so

test_t175 :: [Assertion]
test_t175 =
  fmap (uncurry (@=?))
  $(do
    Syn.lift =<< (traverse
      (\(a, e) -> (,) <$> (sweeten @_ @[DDec] <$> (desugar =<< a)) <*> e)
      [ ( [d| pattern P = (id -> ()) |]
        , [d| pattern P = (\x -> case id x of () -> () -> ()) |]
        )
      , ( [d| pattern P x <- x@3 |]
        , [d| pattern P x <- (\x -> case x of 3 -> x -> x) |]
        )
      , ([d| pattern P = ()|], [d| pattern P = ()|])
      ])
   )

But with something instead of @=? which checks up to alpha-equivalence. I couldn't find anything in th-desugar for this kind of test. Would this be something worth adding or is there a simpler test-setup you'd recommend?

expipiplus1 avatar Jan 19 '23 03:01 expipiplus1

Most of the unit tests are located here:

https://github.com/goldfirere/th-desugar/blob/92c07bd88b74efae0d1b6b7bb5d52049723a009a/Test/Run.hs#L77-L154

Where each test_* function is located here:

https://github.com/goldfirere/th-desugar/blob/92c07bd88b74efae0d1b6b7bb5d52049723a009a/Test/Splices.hs#L136-L352

The problem with this approach is that it tests things by splicing in expressions. However, it's not entirely straightforward to define an expression containing a pattern synonym definition. You can't do something like let { pattern P = (id -> ()) } in f P = 42, as pattern synonyms must be defined at the top level.

One way we could accomplish this would be to define the pattern synonym definitions elsewhere, and then define expressions in terms of those pattern synonyms. We would need two copies of each pattern synonym: one using the original pattern synonym syntax, and another using the desugared syntax. We could define them in separate modules (similar to how we have both Test/Decs.hs and Test/DsDecs.hs) and import them qualified to avoid name clashes.

Testing alpha-equivalence would be another way to accomplish this, but nothing in th-desugar quite implements that. The closest that we have is the eqTH function, which checks that two things are Equal to each other after removing the numeric suffixes from each type variable.

RyanGlScott avatar Jan 19 '23 13:01 RyanGlScott

How is this going, @expipiplus1? Do you need help with anything?

RyanGlScott avatar Feb 15 '23 12:02 RyanGlScott

Other life things have gotten in the way a bit! I would like to finalize this, but it's not at the top of my plate at the moment, sorry.

I think there's also a bug, along the lines of: (foo -> Left x) being transformed to (\a -> case foo a of Left x -> x) (something like that, where fallible matches on the rhs of a view pattern were transformed into infallible (runtime pattern match error) case analysis in the lhs. So very annoyingly we need to add a catch all case to these and handle everything in Maybe resultTuple (or some unboxed variant to avoid any allocation).

expipiplus1 avatar Feb 15 '23 13:02 expipiplus1

Thanks for the update! (I was worried that this was in a finished state and only being delayed due to figuring out how to add tests, in which case I could pick it up from there.)

RyanGlScott avatar Feb 15 '23 14:02 RyanGlScott