|Version 2 (modified by guest, 9 years ago) (diff)|
Representing closure-converted types as indexed types
The idea is to use a class
class CC a where data CConv a -- closure converted 'a' to :: a -> CConv a fr :: CConv a -> a
The most interesting instance is that for functions, which reads
data Clo a b = forall e. Clo (c -> a -> b) e class (CC a, CC b) => CC (a -> b) where data CConv (a -> b) = CCArrow (Clo a b) to f = Clo (\_ -> f) () fr (Clo f e) = f e
For all basic types, we want something like the following:
instance CC Int where newtype CConv Int = CCInt Int to = CCInt fr (CCInt x) = x
In fact, we want something like that for all types that are defined in modules that are not closure-converted. Unfortunately, associated data types cannot have defaults. (Also note that we use here a newtype instance for a data family. Something, which should be easy to add to GHC's implementation of associated types, but isn't allowed right now.)
When we encounter a data type definition for T a1 .. an in a closure converted module, we need to generate a CC (T a1 .. an) instance of CC. The right hand side of CConv (T a1 .. an) will have the same form as that of T, but with all data constructors renamed and with all types t replaced by CConv t. If no arrows occur directly or indirectly in the definition of T, we could instead use the same default instance as outlined for basic types above, as an optimisation.
Import and export
To export and import the data constructors of CConv instances, we can just export and import CConv(..).
During our brain storming, we thought that we might need CConv on higher-kinds, too - i.e., if we have CConv (T a1 .. an), we also want CConv T. The idea was that if we have
data U t = forall a. U a (a -> t a) data T a = ...
the translation of U T requires CConv T.
However, I am not so convinced anymore that we really need it (at least as long as disregarding the interaction with non-closure-converted code). After all, we need CConv essentially in two places:
- If we have f :: tau, we produce f' :: CConv tau.
- If we have data T = MkT tau, we produce data CConv T = CCMkT (CConv tau).
In both cases, we only apply CConv to tau types. (In GHC's sense of tau types.)
The only place, where that's not the case that I can see at the moment is when the closure-converted module defines (or uses?) constructor classes. Then, we need a corresponding constructor class for CConv C (where the C is the type constructor). This doesn't seem to be a feature that we need to cover in the near future.
In any case, we might be able to use a family of classes (such as with Typeable and we also discussed some tricks we might be able to play with associated synonyms).
Contexts: 1. dealing with signature contexts
Something we didn't think about much so far is how to closure convert functions that have a class context; so, if we have
inc :: Num a => a -> a inc = (+1)
what is the signature of the closure converted version? We can't use
inc :: CConv (Num a => a -> a)
as associated types don't support that. We probably want
inc :: Num a => CConv (a -> a) -- OR inc :: Num (CConv a) => CConv (a -> a)
As closure conversion happens on Core code, we need to be careful, as the context is not visible as such in the core view of types.