Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace DLamE/DCaseE with lambda cases (\cases) #210

Open
RyanGlScott opened this issue Apr 29, 2024 · 3 comments · May be fixed by #218
Open

Replace DLamE/DCaseE with lambda cases (\cases) #210

RyanGlScott opened this issue Apr 29, 2024 · 3 comments · May be fixed by #218

Comments

@RyanGlScott
Copy link
Collaborator

RyanGlScott commented Apr 29, 2024

Currently, the th-desugar AST has a DLamE construct for binding variables in expressions, and a DCaseE construct for scrutinizing expressions. I propose that we remove both of these in favor of a single DLamCasesE construct, as proposed in #174 (comment):

data DExp
  = ...
  | DLamCasesE [DClause]

Advantages

  • Doing so would make the th-desugar AST more minimal, as we can desugar lambda expressions, case expressions, \case expressions, and \cases expressions to a single language construct (DLamCasesE). (See Add support for visible foralls in lambda, \case, and \cases expressions #204 (comment) for examples of how each of these desugarings would work.) We could also get rid of DMatch in favor of DClause.

  • Doing so would avoid the need to desugar expressions like this:

    \(Foo x) (Bar y) -> f x y

    Into this:

    \fooX barY ->
      case (# fooX, barY #) of
        (# Foo x, Bar y #) -> f x y

    That is, it would avoid the need to "pack" arguments into an unboxed tuple just for the sake of pattern-matching on them in a single clause. Instead, we can avoid all of this tuple-packing business by simply using \cases' built-in pattern-matching capabilities.

  • By not performing tuple-packing, we would make it much, much simpler to desugar expressions that bind embedded type patterns or invisible type patterns (Add support for visible foralls in lambda, \case, and \cases expressions #204).

Potential downsides

  • This would be a pretty big breaking API change, even by th-desugar standards. It may not be entirely straightforward to migrate all existing th-desugar clients over to the DLamCasesE approach, so we may even want to consider a deprecation period for one release before removing DLamE and DCaseE entirely.
  • I haven't attempted to port singletons-th (by far the most sophisticated th-desugar client I know of) over to this new approach. We should make sure that this is viable before committing to this design.
RyanGlScott added a commit that referenced this issue Apr 30, 2024
This is a partial fix for #204 which adds `DTypeE` and `DTypeP` constructs to
the `th-desugar` AST, which behave exactly like their `TypeE` and `TypeP`
counterparts in the `template-haskell` AST. This is only a partial fix,
however, because `DTypeP` is currently only supported in the clauses of
function declarations. In particular, `DTypeP` is _not_ supported in lambda
expressions, `\case` expressions, or `\cases` expressions. See the "Known
limitations" section of the `README` for more.

In order to add full support for `DTypeP`, we would first need to implement the
design proposed in #210. Until then, this is good enough.
RyanGlScott added a commit that referenced this issue Apr 30, 2024
This is a partial fix for #205 which adds a `DInvisP` data constructor to
`DPat`, which behaves exactly like `InvisP` in the `template-haskell` AST. This
is only a partial fix, however, because `DInvisP` is currently only supported
in the clauses of function declarations. In particular, `DInvisP` is _not_
supported in lambda expressions or `\cases` expressions. See the "Known
limitations" section of the `README` for more.

In order to add full support for `DInvisP`, we would first need to implement
the design proposed in #210. Until then, this is good enough.
RyanGlScott added a commit that referenced this issue May 1, 2024
This is a partial fix for #204 which adds `DTypeE` and `DTypeP` constructs to
the `th-desugar` AST, which behave exactly like their `TypeE` and `TypeP`
counterparts in the `template-haskell` AST. This is only a partial fix,
however, because `DTypeP` is currently only supported in the clauses of
function declarations. In particular, `DTypeP` is _not_ supported in lambda
expressions, `\case` expressions, or `\cases` expressions. See the "Known
limitations" section of the `README` for more.

In order to add full support for `DTypeP`, we would first need to implement the
design proposed in #210. Until then, this is good enough.
RyanGlScott added a commit that referenced this issue May 1, 2024
This is a partial fix for #205 which adds a `DInvisP` data constructor to
`DPat`, which behaves exactly like `InvisP` in the `template-haskell` AST. This
is only a partial fix, however, because `DInvisP` is currently only supported
in the clauses of function declarations. In particular, `DInvisP` is _not_
supported in lambda expressions or `\cases` expressions. See the "Known
limitations" section of the `README` for more.

In order to add full support for `DInvisP`, we would first need to implement
the design proposed in #210. Until then, this is good enough.
@RyanGlScott
Copy link
Collaborator Author

One obstacle to making this work is that \cases (as well as its template-haskell counterpart, LamCasesE) has only been around since GHC 9.4. When sweetening a DLamCasesE expression, this means we have to think carefully about what to do on pre-9.4 versions of GHC, as we can't simply convert DLamCasesE to LamCasesE. In many cases, we can convert simple DLamCasesE expressions to LamE (when there is only a single \cases clause) or LamCaseE (when each \cases clause only has a single pattern), which provides at least partial backwards compatibility. This wouldn't be quite as straightforward for \cases expressions like this this one, however:

\cases
  True  (Just x) -> x
  False Nothing  -> 2
  _     _        -> 3

Note that you wouldn't actually be able to write such an expression directly with a pre-9.4 version of GHC—you'd only be able to construct one by splicing in a DLamCasesE. Still, it's conceivable that a user might try this, so we should have a story for how to handle it. Some options:

  1. Throw an error. We can say that sweetening \cases expressions like the one above are simply unsupported on pre-9.4 versions of GHC, and you'd need to upgrade your GHC if you want to do this.

  2. We could cleverly sweeten this \cases expression to something like:

    \case
      True ->
        \case
          Just x -> x
          _      -> 3
      False ->
        \case
          Nothing -> 2
          _       -> 3

    Note how we have inlined the 3 case in certain spots. While we could do this, this would require a significant amount of additional complexity in the sweetening pass. This feels a bit wrong, as th-desugar's usual approach is to put all of the complicated logic in desugaring, which then makes sweetening back to the template-haskell AST nearly trivial.

  3. We could take a page from how \cases expressions are currently desugared and sweeten the expression above to something like this:

    \arg1 arg2 ->
      case (arg1, arg2) of
        (True,  Just x)  -> x
        (False, Nothing) -> 2
        (_,     _)       -> 3

    This is still a bit involved, but not nearly as complicated as option (2).

    This option comes with a more severe drawback, however. In order to ensure that arg1 and arg2 don't shadow anything currently in scope (and run the risk of emitting -Wname-shadowing warnings), we need to use newName to ensure that arg1 and arg2 are fresh names. newName is a monadic operation, however, and sweetening is a completely pure operation. This means that if we wanted to call newName during sweetening, we would need to change the type signatures of all sweetening-related operations to be monadic. This is doable, but this it at odds with th-desugar's current approach of making sweetening as simple as possible.

For now, I am inclined to pick option (1). We can revisit if someone specifically asks for the ability to sweeten DLamCasesE expressions with pre-9.4 versions of GHC.

@goldfirere
Copy link
Owner

Reading the options, I was more excited about (3). But actually I agree that (1) is most expedient.

@RyanGlScott RyanGlScott changed the title Replace LamE/CaseE with lambda cases (\cases) Replace DLamE/DCaseE with lambda cases (\cases) May 23, 2024
RyanGlScott added a commit that referenced this issue May 23, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
RyanGlScott added a commit that referenced this issue May 23, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
RyanGlScott added a commit that referenced this issue May 24, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
RyanGlScott added a commit that referenced this issue May 24, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
@RyanGlScott RyanGlScott linked a pull request May 24, 2024 that will close this issue
RyanGlScott added a commit that referenced this issue May 24, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
@RyanGlScott
Copy link
Collaborator Author

See #218 for the changes on the th-desugar side, as well as goldfirere/singletons#595 for the changes on the singletons-th side. Happily, migrating singletons-th over to DLamCasesE proved extremely straightforward, and it even simplified some tricky parts of how singling works.

RyanGlScott added a commit that referenced this issue Jun 1, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
RyanGlScott added a commit that referenced this issue Jun 1, 2024
This patch deprecates the `DLamE` and `DCaseE` data constructors of `DExp` in
favor of a new `DLamCasesE` data constructor, which represents `\cases`
expressions. Moreover, `th-desugar` now desugars all lambda, `case`, `\case`,
and `\cases` expressions to `DLamCasesE`. There are several reasons why this is
desirable, but an especially important motivation for switching is to support
desugaring expressions that use embedded type patterns (see #204) or invisible
type patterns (see #205) in lambda, `case`, `\case`, and `\cases` expressions.

This is a pretty big change, even by `th-desugar` standards. As such, I have
made an effort to avoid some of the more extreme breaking changes for now. For
example, I have refrained from removing `DLamE` and `DCaseE` outright, instead
converting them to deprecated pattern synonyms. I have also introduced
combinators such as `dLamE` and `dCaseE`, which construct lambda-like and
`case`-like expressions in terms of `DLamCasesE`. For the full details on how
to migrate your code over to use `DLamCaseE`, see the new
`doc/LambdaCaseMigration.md` document.

This patch:

* Fixes #210 (by replacing `DLamE`/`DCaseE` with `DLamCasesE`)
* Fixes #204 (by supporting higher-order uses of embedded type patterns)
* Fixes #205 (for supporting higher-order uses of invisible type patterns)

This also adds regression tests for #204 and #205.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants