Skip to content

Get type information from the GHC API (for type class support) #269

@chrisdone

Description

@chrisdone

So, a while back when I first started Fay I mentioned that my first approach was to use the GHC API. Which is the first thing I wrote:

http://hpaste.org/75112

With the intention that I could get type information and such. But I couldn't see how to do that, and in the interest of expedience I gave up on type-classes and I switched to haskell-src-exts because it was better documented.

Now, looking at it again, I think it may be the answer to many problems, including:

  1. Type-classes.
  2. Name resolution.
  3. Smarter FFI.

At the start I had no idea what was required for a Haskell compiler. Now with experience it's a lot easier to pick out the key requirements.

Notice the definition of Var:

   λ> :i Id
    type Id = Var.Var   -- Defined in `Var'
    λ> :i Var.Var
    data Var.Var
      = Var.TyVar {Var.varName :: !Name,
                   Var.realUnique :: FastTypes.FastInt,
                   Var.varType :: Kind}
      | Var.TcTyVar {Var.varName :: !Name,
                     Var.realUnique :: FastTypes.FastInt,
                     Var.varType :: Kind,
                     Var.tc_tv_details :: TcType.TcTyVarDetails}
      | Var.Id {Var.varName :: !Name,
                Var.realUnique :: FastTypes.FastInt,
                Var.varType :: Type,
                Var.idScope :: Var.IdScope,
                Var.id_details :: IdInfo.IdDetails,
                Var.id_info :: IdInfo.IdInfo}
            -- Defined in `Var'

It appears to me that it contains a name, type and a guid, even scope info. That would be useful for getting name resolution for free but still outputting useful names (i.e. the original), maybe I imagine like foo_1234 or w/e.

Notice also that the typechecked source just gives you a list of binds, and if you want other stuff, it's elsewhere:

λ> :i ModuleInfo
data ModuleInfo
  = GHC.ModuleInfo {GHC.minf_type_env :: TypeEnv,
                    GHC.minf_exports :: NameSet,
                    GHC.minf_rdr_env :: Maybe RdrName.GlobalRdrEnv,
                    GHC.minf_instances :: [Instance],
                    GHC.minf_iface :: Maybe ModIface,
                    GHC.minf_modBreaks :: ModBreaks}
        -- Defined in `GHC'

That seems to be pretty much everything that we need.

I've also noticed in the ghc-mod package this code:

class HasType a where
    getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))

instance HasType (LHsExpr Id) where
    getType tcm e = do
        hs_env <- getSession
        (_, mbe) <- Gap.liftIO $ deSugarExpr hs_env modu rn_env ty_env e
        return $ (getLoc e, ) <$> CoreUtils.exprType <$> mbe
      where
        modu = ms_mod $ pm_mod_summary $ tm_parsed_module tcm
        rn_env = tcg_rdr_env $ fst $ tm_internals_ tcm
        ty_env = tcg_type_env $ fst $ tm_internals_ tcm

instance HasType (LHsBind Id) where
    getType _ (L spn FunBind{fun_matches = MatchGroup _ typ}) = return $ Just (spn, typ)
    getType _ _ = return Nothing

instance HasType (LPat Id) where
    getType _ (L spn pat) = return $ Just (spn, hsPatType pat)

And I've tested it and it does indeed give the type information.

The downside would be that we depend directly on GHC. The other downside would be that GHC's API is highly undocumented, and the API is kind of OTT overengineered, which is miserable to use, and subject to change, so version changes would need to be ifdef'd.

Maybe this approach is a different philosophy from Fay (which depends on GHC-independent libraries like haskell-src-exts and after haskell-type-exts) and belongs in a fork or something, I don't know. But it's an approach that I will be taking seriously.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions