-
Notifications
You must be signed in to change notification settings - Fork 14
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
Add support for visible forall
s in lambda, \case
, and \cases
expressions
#204
Comments
Another radical ideaInspired by this comment, we could replace data DExp
= ...
| DLamCasesE [DClause] Most of the challenges in the comment above arise due to attempting to avoid passing embedded type expressions to intermediate tuples. Using LambdasIn general, a lambda expression of the form: \pat_1 ... pat_n -> exp Desugars to: \cases pat_1 ... pat_n -> exp
|
Some local experimentation suggests that the Unfortunately, I am doubtful that we will be able to implement #210 in time for a GHC 9.10–compatible
The naïve approach, although limited, is perhaps not as bad as it sounds. Although you won't be able to write things like |
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.
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.
forall
s in the types of termsforall
s in lambda, \case
, and \cases
expressions
#211 implemented (1) and (2) from #204 (comment), so the only thing that remains is (3). I've retitled the issue to reflect this. |
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.
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.
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.
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.
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.
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.
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.
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.
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.
GHC 9.10 introduces the ability to use visible
forall
s in the types of terms. This means that the following can now be written:This issue aims to add support for this on the
th-desugar
side. This turns out to be surprisingly non-trivial, however, and we will need to think a bit about the design.Preliminaries:
template-haskell
–related changesFirst, we need to update the relevant parts of
th-desugar
that interface withtemplate-haskell
. On thetemplate-haskell
side, the following changes were introduced intemplate-haskell-2.22.0.0
(bundled with GHC 9.10.1):Naturally, we'll want to add
DTypeP
andDTypeE
toth-desugar
's ASTs as well. This proves relatively straightforward.Challenge (1): translating lambdas and
LambdaCase
Updating the
th-desugar
AST isn't enough to fully support embedded types in terms, however. First, consider this example:th-desugar
is capable of round-trippingf1
through desugaring and sweetening, as the round-tripped version will look identical to the original. However, let's change the implementation slightly:f2
is functionally the same asf1
, but the body off2
uses a lambda instead. This actually makes a big difference forth-desugar
, sinceth-desugar
is not capable of round-trippingf2
. If you attempted to do so, you'd end up with something like this:This simply won't work, however, as you aren't allowed to use
type a
(an embedded type expression) like that. GHC will complain thusly:This affects not just lambda expressions, but also uses of
\case
. For example, given this:th-desugar
would round-trip it to:But again, GHC doesn't like this:
Similarly for
\cases
. If you have this:th-desugar
will round-trip it to:Which fails for the same reasons as
f2
above.What should we do about this? One possibility is to detect embedded type patterns in lambda/
LambdaCase
expressions and exempt them from being included in the tuples thatth-desugar
generates when desugaring code. That is, round-trip this:To this:
And similarly for
f3
andf4
. This plan doesn't quite work out of the box, however, since the definition ofDLamE
(th-desugar
's AST for lambda expressions) is this:This means that
th-desugar
's lambdas can only bindName
s, not arbitrary patterns liketype a
. However, we are in luck, since we can binda
as a bareName
and it will still work:Challenge (2): Renaming
I did something very sneaky in my fix for
f2
above: I assumed that it was possible to lift out thetype a
pattern without needing to change the right-hand side of the lambda expression at all. That only worked in thef2
example through a fortunate coincidence, but we won't always be so lucky. Consider this more involved example:This time, we have a
\cases
expressions with multiple clauses, and each clause names the type variable something differenta1
anda2
. A naïve attempt to round-trip this might result in code that looks like this:But this is wrong, since the
a2
iny :: a2
no longer refers to the same type variable (a1
) that is bound in the embeddedtype a1
pattern. We'd have a similar problem if we bound\a2
instead of\a1
.The only way I can think of to solve this challenge would be to rename any occurrences of bound type variables to ensure that they all refer to the same variable names. That is, desugar
g
to this:Note that we now generate
y :: a1
instead ofy :: a2
.This is easier said than done, however, as we'd need to develop the necessary machinery for substituting into arbitrary expressions.
th-desugar
has some similar machinery here, but it's only suitable for substituting into types, not expressions. (See also #108.)Challenge (3): Distinguishing type from term variables
I did yet another sneaky thing in the fix for
f2
above: I assumed that it was straightforward to determine which bound variables were term variables or type variables. In the case off2
, this is obvious, since the type variablea
occurs underneath atype
marker. This isn't always so obvious in general, however. Consider:If you look at the expression
\a x -> a
in isolation, it's not at all obvious whethera
is a term-level or type-level variable. But we really do need to know this information, as we can't reliably desugar the expression unless we exempta
from being put into the generated tuple! Nor does thetemplate-haskell
AST tell us much: if we definedh
withinTemplateHaskellQuotes
:[d| h :: forall a -> a -> a h = \a x -> x |]
And then inspected
a
, we'd simply find aName
with aNameFlavour
ofNameU
, which simply tells us that it's a unique name. Nor can we callreify
ona
'sName
to see if it returnsVarI
(for term-levelName
s) orTyVarI
(for type-levelName
s).The only way I can think of solving this challenge is to look up the type signature of any lambda expression (or
LambdaCase
expression) that we are desugaring and use the type to determine if we are dealing with type variables or term variables. That's a pretty heavyweight solution, but I can't think of another way.Radical idea
Many of the challenges discussed above ultimately arise from the limitation that
DLamE
only supports bindingName
s, not full patterns. As an radical alternative, we could consider changing the definition ofDLamE
to:And abandoning the whole desugar-lambdas-to-case-of-tuples plan entirely. That would avoid all of the issues above, at the expense of making
DDec
slightly less minimal (and perhaps makingsingletons-th
's life harder). Is it worth it? I'm not sure.The text was updated successfully, but these errors were encountered: