如何在Haskell中获取函数类型?实现类似GHCi的:t命令
Great question! You're right that Haskell's static typing means type information is erased at runtime—so you can't directly write a getTypeTuple foo that returns (Int, Int, String) as a runtime value. But there are solid workarounds using compile-time tools or GHC's built-in features to replicate what GHCi's :t command does. Let's break down your options:
1. Compile-Time Type Generation with Template Haskell
Template Haskell lets us inspect types at compile time and generate code that represents those types as values (like string tuples). Here's how to implement this for your foo function:
First, enable the Template Haskell extension and import the necessary modules:
{-# LANGUAGE TemplateHaskell #-} import Language.Haskell.TH import Language.Haskell.TH.Syntax
Then write a Template Haskell function that parses a function's type and generates a tuple of type name strings:
-- Takes a function name and generates code for its type tuple getTypeTuple :: Name -> Q Exp getTypeTuple name = do -- Retrieve metadata about the function info <- reify name case info of VarI _ ty _ _ -> do -- Split the function type into arguments and return type let (args, result) = splitFunctionType ty -- Convert each type to a string expression argExps <- mapM typeToStringExp args resultExp <- typeToStringExp result -- Combine into a tuple expression return $ TupE (argExps ++ [resultExp]) _ -> fail "getTypeTuple only works with functions" where -- Recursively split function types (e.g., Int -> Int -> String becomes [Int, Int], String) splitFunctionType :: Type -> ([Type], Type) splitFunctionType (AppT (AppT ArrowT arg) rest) = let (args, res) = splitFunctionType rest in (arg : args, res) splitFunctionType finalType = ([], finalType) -- Convert a type to a string literal expression typeToStringExp :: Type -> Q Exp typeToStringExp (ConT typeName) = stringE $ nameBase typeName typeToStringExp otherType = stringE $ show otherType
Now use it with your foo function:
foo :: Int -> Int -> String foo a b = show a ++ show b ++ "hello" -- Generate the type tuple at compile time fooType :: (String, String, String) fooType = $(getTypeTuple 'foo) -- Evaluates to ("Int", "Int", "String")
When you compile this, Template Haskell runs during the build process, inspects foo's type, and generates code that directly returns the tuple of type names.
2. Replicate GHCi's :t with the GHC API
If you want to programmatically query types like GHCi does (instead of generating code at compile time), you can use the GHC API. This lets you load modules, typecheck them, and retrieve type information.
First, add the ghc package to your dependencies. Then implement this function:
import GHC import GHC.Paths (libdir) import DynFlags import OccName (mkVarOcc) import Module (mkModuleName) -- Get the string representation of a function's type (like :t in GHCi) getFunctionType :: String -> String -> IO String getFunctionType moduleName functionName = runGhc (Just libdir) $ do -- Initialize the GHC session dflags <- getSessionDynFlags setSessionDynFlags dflags -- Load the target module target <- guessTarget moduleName Nothing setTargets [target] load LoadAllTargets -- Retrieve module summary and typecheck it modSum <- getModSummary $ mkModuleName moduleName parsedMod <- parseModule modSum typecheckedMod <- typecheckModule parsedMod -- Look up the function's name let renamedSource = tm_renamed_source typecheckedMod case lookupName renamedSource (mkVarOcc functionName) of Just (AnId funcId) -> do funcType <- getType funcId return $ show funcType _ -> return "Function not found in module"
You can call this to get foo's type string:
main :: IO () main = do ty <- getFunctionType "MyModule" "foo" putStrLn ty -- Prints "Int -> Int -> String"
From here, you could parse this string into a tuple of type names if needed.
Key Notes
- You can't directly return
(Int, Int, String)as a runtime value becauseIntandStringare types, not values. The closest you can get is a tuple of their string representations (as shown above). - All solutions rely on compile-time or tooling access to type information, since runtime type erasure means types don't exist in compiled Haskell code.
内容的提问来源于stack exchange,提问作者Kivol

