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

Don't defunctionalize helpers for class defaults or instance methods #609

Merged
merged 2 commits into from
Jun 22, 2024

Conversation

RyanGlScott
Copy link
Collaborator

singletons-th generates "helper" type families that contain the definitions of class method defaults or instance methods. For example, this:

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

Will be promoted to this:

class PC a where
  type M (x :: a) (y :: b) :: a
  type M x y = MHelperSym0 `Apply` x `Apply` y

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

type MHelperSym0 :: a ~> b ~> a
type MHelperSym1 :: a -> b ~> a
...

Generating defunctionalization symbols for MHelper is wasteful, however, as we never really need to partially apply MHelper. Instead, we can just generate this code:

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

This takes advantage of the fact that when we apply MHelper, we always fully apply it to all of its arguments. This means that we can avoid generating defunctionalization symbols for helper type families altogether, which is a nice optimization.

This patch implements that idea, fixing #608 in the process.

`singletons-th` generates "helper" type families that contain the definitions
of class method defaults or instance methods. For example, this:

```hs
class C a where
  m :: a -> b -> a
  m x _ = x
```

Will be promoted to this:

```hs
class PC a where
  type M (x :: a) (y :: b) :: a
  type M x y = MHelperSym0 `Apply` x `Apply` y

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

type MHelperSym0 :: a ~> b ~> a
type MHelperSym1 :: a -> b ~> a
...
```

Generating defunctionalization symbols for `MHelper` is wasteful, however, as
we never really _need_ to partially apply `MHelper`. Instead, we can just
generate this code:

```hs
class PC a where
  type M (x :: a) (y :: b) :: a
  type M x y = MHelper x y
```

This takes advantage of the fact that when we apply `MHelper`, we always fully
apply it to all of its arguments. This means that we can avoid generating
defunctionalization symbols for helper type families altogether, which is a
nice optimization.

This patch implements that idea, fixing #608 in the process.
…atures

As noted in #608, you don't need to involve defunctionalization when promoting
class method defaults, as we always know that the promoted versions of the
defaults will be fully applied to all their arguments. In fact, there is
another situation in which we always know that a promoted type family will be
fully applied: the return type of a singled type signature. That it, if you
single this definition:

```hs
f :: a -> b -> a
```

You currently get this:

```hs
sF :: forall a b (x :: a) (y :: b).
      Sing x -> Sing y -> Sing (FSym0 `Apply` x `Apply` y)
```

But using `FSym0` in the return type of `sF` is silly, however, as we always
know that it will be fully applied to its arguments (`x` and `y`). As such, it
would be far more direct to instead generate the following:

```hs
sF :: forall a b (x :: a) (y :: b).
      Sing x -> Sing y -> Sing (F x y)
```

No defunctionalization required at all. This doesn't change the behavior of the
singled code at all, but it is more direct and will likely require less time to
typecheck due to fewer type family indirections being involved.

To accomplish this, the `lde_proms` field now maps term-level names to
`LetDecProm`s, where a `LetDecProm` consists of the promoted version of the
name and the local variables that it closes over. In this context, "promoted
version" means `F`, _not_ `FSym0`. Storing the non-defunctionalized name allows
us to fully apply `F` whenever it is convenient to do so, and in situations
where partial application may be required, we can always convert `F` to `FSym0`
before generating the partial application.
@RyanGlScott RyanGlScott merged commit a351105 into master Jun 22, 2024
24 checks passed
@RyanGlScott RyanGlScott deleted the T608-no-class-default-defun branch June 22, 2024 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant