-
Notifications
You must be signed in to change notification settings - Fork 37
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 generate defunctionalization symbols for "helper" families used in class defaults and instance methods #608
Labels
Comments
RyanGlScott
added a commit
that referenced
this issue
Jun 19, 2024
`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.
RyanGlScott
added a commit
that referenced
this issue
Jun 22, 2024
…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
added a commit
that referenced
this issue
Jun 22, 2024
`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.
RyanGlScott
added a commit
that referenced
this issue
Jun 22, 2024
…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.
Fixed in #609. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Consider this example:
Conceptually, this will:
Promote the class
C
toPC
with an associated type family namedM
.M
will have a type family default that looks like:Where
MHelper
is defined to be:What is interesting is that
singletons-th
defines defunctionalization symbols forMHelper
. As a result, compiling the example above will generate a surprising amount of code:I claim that these defunctionalization symbols are unnecessary, and we can omit them entirely. I oversimplified things a little bit earlier when I said that the type family default looks like
type M x y = MHelper x y
. In reality, it looks like this:Where
MHelperSym0
is a defunctionalization symbol. The thing is, there's not a good reason to involve defunctionalization symbols here. In the context of class defaults (and instance methods), we know that the helper type family will be fully applied to all of its arguments, so no partial application of defunctionalization symbols is required. We could just as well generatetype M x y = MHelper x y
and avoid all of the gobs of code that arise due to defunctionalization.The text was updated successfully, but these errors were encountered: