Haskell: getting the static type of an expression

2019-02-01 13:10发布

问题:

I'm looking for a function that does what the GHCi :type command does.

Ideally, it would have a signature something like

getStaticType :: a -> String

a = getStaticType (1+2)
-- a = "(Num t) => t"

b = getStaticType zipWith
-- b = "(a -> b -> c) -> [a] -> [b] -> [c]"

(Note: this has nothing to do with Data.Dynamic. I just want the static type inferred from the compiler. In fact the function wouldn't need a runtime implementation at all, as all calls to it could be inlined as constants at compile time. I'm assuming it exists somewhere, since GHCi can do it)

回答1:

You can do it like this:

import Data.Typeable

getStaticType :: Typeable a => a -> String
getStaticType = show . typeOf

Note that the type must be an instance of Typeable. You can derive Typeable automatically using the DeriveDataTypeable Haskell language extension and ... deriving (Typeable, ...).

Also note that polymorphic types cannot be identified in this way; you must always call a function with a specific type, so you can never get that polymorphic type information that you get in GHCi with compiled Haskell code.

The way GHCi does it is that it uses the GHC API to analyse an intermediary Haskell abstract syntax tree (AST) that contains type information. GHCi does not have the same restricted environment that your typical compiled Haskell program does; it can do lots of stuff to find out more information about its environment.

With TemplateHaskell, you can do it like this; first, create this module:

module TypeOf where

import Control.Monad

import Language.Haskell.TH
import Language.Haskell.TH.Syntax

getStaticType :: Name -> Q Exp
getStaticType = lift <=< fmap pprint . reify

Then, in a different module (very important), you can do the following:

{-# LANGUAGE TemplateHaskell #-}

import TypeOf

main = putStrLn $(getStaticType 'zipWith)

This program outputs:

GHC.List.zipWith :: forall a_0 b_1 c_2 . (a_0 -> b_1 -> c_2) ->
                                         [a_0] -> [b_1] -> [c_2]

You can use a better pretty-printer than the pprint function; take a look at the Language.Haskell.TH.Ppr module.



回答2:

try http://www.haskell.org/haskellwiki/GHC/As_a_library

typed targetFile targetModule = do
 defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
  runGhc (Just libdir) $ do

   dflags <- getSessionDynFlags
   let dflags' = xopt_set dflags Opt_ImplicitPrelude
   setSessionDynFlags dflags'

   target <- guessTarget targetFile Nothing
   setTargets [target]
   load LoadAllTargets

   m <- getModSummary $ mkModuleName targetModule
   p <- parseModule m
   t <- typecheckModule p

   return $ typecheckedSource d