Haskell:获取表达式的静态类型

11
我是一位有用的助手,会进行文本翻译。

我正在寻找一个函数,它可以执行类似于GHCi :type 命令的操作。

理想情况下,它应该具有以下签名:

getStaticType :: a -> String

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

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

注意:这与Data.Dynamic无关。我只想从编译器中推断出静态类型。实际上,该函数根本不需要运行时实现,因为它的所有调用都可以在编译时作为常量内联。我假设它存在于某个地方,因为GHCi可以做到这一点。
2个回答

20

你可以这样做:

import Data.Typeable

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

请注意,类型必须是Typeable的实例。您可以使用DeriveDataTypeable Haskell语言扩展和 ... deriving (Typeable, ...)自动派生 Typeable

还要注意,无法通过这种方式识别多态类型;您必须始终使用特定的类型调用函数,因此在编译后的Haskell代码中永远无法获得在GHCi中获取的多态类型信息。

GHCi的方法是使用GHC API分析包含类型信息的中间Haskell抽象语法树(AST)。 GHCi没有通常编译后的Haskell程序所具有的受限环境;它可以执行许多操作以查找其环境的更多信息。

使用TemplateHaskell,您可以像这样操作:首先创建此模块:

module TypeOf where

import Control.Monad

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

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

然后,在一个不同的模块中(非常重要),您可以执行以下操作:

{-# LANGUAGE TemplateHaskell #-}

import TypeOf

main = putStrLn $(getStaticType 'zipWith)

这个程序输出:

GHC.List.zipWith :: forall a_0 b_1 c_2 . (a_0 -> b_1 -> c_2) ->
                                         [a_0] -> [b_1] -> [c_2]
你可以使用比pprint函数更好的漂亮打印器;看一下Language.Haskell.TH.Ppr模块。

使用它可以自我记录代码和日志。很遗憾,这似乎不可能实现为编译器中的“魔术函数”,因为编译器已经具有所有信息。 - drwowe
@drwowe 是正确的 - 这样的实现在编译器中相当容易。它将是一种类似于 C 语言中的 sizeof 的编译时函数。 - Ingo
它的使用想法可能是将其实现为函数的Show实例。 - VlatkoB

1

试试看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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接