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

Support promoting/singling invisible type patterns #583

Open
RyanGlScott opened this issue May 1, 2024 · 4 comments
Open

Support promoting/singling invisible type patterns #583

RyanGlScott opened this issue May 1, 2024 · 4 comments

Comments

@RyanGlScott
Copy link
Collaborator

GHC 9.10 introduces the ability to use invisible type patterns. As such, examples like this can now be written:

id :: a -> a
id @t x = x :: t

The question is: can we promote and single these definitions? It seems plausible, given that we can write the following:

type Id :: a -> a
type family Id x where
  Id @t x = x :: t

sId :: (forall (x :: a). Sing x -> Sing (Id x) :: Type)
sId @t (sX :: Sing x) = sX :: Sing (x :: t)

We should see if this is doable. See also #565.

RyanGlScott added a commit that referenced this issue May 1, 2024
This bumps the `th-desugar` commit in the `cabal.project` file's
`source-repository-package` to bring in the changes from `th-desugar-1.17`.
Among other things, this version of `th-desugar` adds support for:

* Namespace specifiers in fixity declarations
* Embedded type expressions and patterns
* Invisible type patterns

For now, `singletons-th` will error if it encounters any of these constructs.
Where appropriate, I have opened issues to track the idea of supporting these
language features in `singletons-th`:

* For namespace specifiers in fixity declarations, see
  #582.
* Supporting embedded type expressions and patterns seems quite difficult due to
  `singletons-th`'s policy of only promoting/singling vanilla type signatures
  (see the `README`), so I have not opened an issue for this.
* For invisible type patterns, see
  #583.

This is one step towards preparing a GHC 9.10–compatible release of
`singletons` and friends (see #569).
RyanGlScott added a commit that referenced this issue May 1, 2024
This bumps the `th-desugar` commit in the `cabal.project` file's
`source-repository-package` to bring in the changes from `th-desugar-1.17`.
Among other things, this version of `th-desugar` adds support for:

* Namespace specifiers in fixity declarations
* Embedded type expressions and patterns
* Invisible type patterns

For now, `singletons-th` will error if it encounters any of these constructs.
Where appropriate, I have opened issues to track the idea of supporting these
language features in `singletons-th`:

* For namespace specifiers in fixity declarations, see
  #582.
* Supporting embedded type expressions and patterns seems quite difficult due to
  `singletons-th`'s policy of only promoting/singling vanilla type signatures
  (see the `README`), so I have not opened an issue for this.
* For invisible type patterns, see
  #583.

This is one step towards preparing a GHC 9.10–compatible release of
`singletons` and friends (see #569).
RyanGlScott added a commit that referenced this issue May 1, 2024
This bumps the `th-desugar` commit in the `cabal.project` file's
`source-repository-package` to bring in the changes from `th-desugar-1.17`.
Among other things, this version of `th-desugar` adds support for:

* Namespace specifiers in fixity declarations
* Embedded type expressions and patterns
* Invisible type patterns

For now, `singletons-th` will error if it encounters any of these constructs.
Where appropriate, I have opened issues to track the idea of supporting these
language features in `singletons-th`:

* For namespace specifiers in fixity declarations, see
  #582.
* Supporting embedded type expressions and patterns seems quite difficult due to
  `singletons-th`'s policy of only promoting/singling vanilla type signatures
  (see the `README`), so I have not opened an issue for this.
* For invisible type patterns, see
  #583.

This is one step towards preparing a GHC 9.10–compatible release of
`singletons` and friends (see #569).
@RyanGlScott
Copy link
Collaborator Author

One wrinkle that I foresee is class method defaults. Consider this example:

class C a where
  m :: forall b. a -> b -> a
  m x _ = x

Currently, we promote this to:

class PC a where
  type M (x :: a) (y :: b) :: a
  type M x y = MDefault x y

type MDefault :: a -> b -> a
type family MDefault x y where
  MDefault x _ = x

So far, so good. Now consider this variation:

class C a where
  m :: forall b. a -> b -> a
  m @b x (_ :: b) = x

A naïve approach would lead to this promoted definition:

class PC a where
  type M (x :: a) (y :: b) :: a
  type M x y = MDefault x y

type MDefault :: a -> b -> a
type family MDefault x y where
  MDefault @b x (_ :: b) = x

But this is wrong: we really want to generate this instead:

type MDefault :: a -> b -> a
type family MDefault x y where
  MDefault @_ @b x (_ :: b) = x

In general, we'd need to insert a number of @_s equal to the number of type variables bound in the class header.

Actually, that's an oversimplification. Inspired by Note [Promoted class methods and kind variable ordering], we can make yet another variation of the example above:

class C b where
  m :: forall a. a -> b -> a
  m x _ = x

Note that b will be quantified before a in the type of m. In the promoted version of m, however, the type variables are quantified in the opposite order:

class PC b where
  type M (x :: a) (y :: b) :: a
  type M x y = MDefault x y

type MDefault :: a -> b -> a
type family MDefault x y where
  MDefault x (_ :: b) = x

This makes it less clear what to do when TypeAbstractions are involved:

class C b where
  m :: forall a. a -> b -> a
  m @a (x :: a) _ = x

Two possible options for promoting this to MDefault are:

  1. type MDefault :: a -> b -> a
    type family MDefault x y where
      MDefault @a x (_ :: b) = x
  2. type MDefault :: forall b a. a -> b -> a
    type family MDefault x y where
      MDefault @_ @a x (_ :: b) = x

Option (1) might appear simpler at a first glance, but generating that code would require us to ignore our earlier rule about inserting @_s. In fact, the more I think about it, I actually think that option (1) would be harder, since it would require swizzling around the invisible type variable arguments to match whatever order singletons-th happens to pick for MDefault's standalone kind signature.

Option (2), on the other hand, would be more consistent with the example above, since we'd continue to apply the insert-@_s rule. Note that the standalone kind signature that singletons-th generates for MDefault (as well as other "default" type families) doesn't yet have an explicit forall, but we could make it have one.

@RyanGlScott
Copy link
Collaborator Author

#591 takes one step towards option (2), as it ensures that type families like MDefault are given explicit foralls. The catch is that the order of the foralls isn't guaranteed to be in the expected order, but after #596 lands, we can guarantee that the order will be preserved for class methods whose parent class has a standalone kind signature. As such, we should land #591 and #596 first before making further progress on this.

@RyanGlScott
Copy link
Collaborator Author

After discovering a regression, I've reverted #596 (in #606). As such, we cannot guarantee the order of kind variables for promoted class methods after all. This means that if we wanted to support invisible type patterns in class method defaults, then we would have no choice but to go with option (1). This means that we would need to swizzle around the invisible type patterns to match whatever type variable order singletons-th happens to pick in the promoted kind.

Is this doable? Perhaps. But it would be somewhat tedious to do so, and I'm not exactly excited about implementing it. As such, I'm inclined to decide that singletons-th doesn't support invisible type patterns in class method defaults at all. If someone asks for this feature later, we could revisit this.

@RyanGlScott
Copy link
Collaborator Author

RyanGlScott commented Jun 17, 2024

We also can't reliably guarantee the order of type variables for let- or where-bound functions, as they aren't given standalone kind signatures (see #378 (comment)). As such, it would also be challenging to support the use of invisible type patterns in let- or where-bound functions.

Thinking about this more, this issue is really just a new twist on the old problem of not being able to guarantee the order of type variables, first described in #378. The last update on #378 was that we opted not to promote visible type applications because singletons-th cannot guarantee the order of type variables in enough places for it to work reliably. (I continue to be on the fence about this position, but that's where things left off.) For the sake of consistency, we should apply the same position to invisible type patterns as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant