Haskell:获取调用方函数名



在Haskell中,有没有一种方法可以获取调用方函数名?当然,我可以硬编码它,但这会变成维护负担:如果有人要重命名函数,没有什么能强迫他们重命名硬编码的名称。

一个人为的例子:

f1 :: String
f1 = "f1" -- can this be automated?

您可以使用GHC的CallStack功能来实现这一点。

import GHC.Stack ( HasCallStack, getCallStack, callStack )
foo :: HasCallStack => String -> String
foo s = let ((name, _):_) = getCallStack callStack
in s <> ": " <> name
main :: HasCallStack => IO ()
main = putStrLn $ foo "This string is being passed to"

产生输出This string is being passed to: foo

请参阅我上面的链接以获得完整的描述,但基本上,您可以要求通过包含HasCallStack约束来访问函数中的(部分(调用堆栈。然后callStack得到一个CallStack,它同构于[(String, SrcLoc)],其中每对的第一个元素是一个函数名;CCD_ 6将抽象的CCD_。

(文档似乎声称HasCallStack约束是可以推断的,但在我非常简短的实验中,这并没有发生;如果我在没有签名的函数中使用callStack,我只会得到一个空的调用堆栈;可能最好显式地编写一个具有HasCallStack约束的签名(

一个想法的核心:使f1成为一个参数化的东西。这样就可以将代码名称和表示代码名称的字符串放在一起,以减少有人在没有另一个的情况下重命名其中一个的机会。因此:

f1Raw :: String -> String
f1Raw name = name
f1 :: String
f1 = f1Raw "f1"

有了一点TemplateHaskell的技巧,你很可能可以制作一个withName :: String -> Q [Decl]左右的接口,让你写这样的东西作为这个模式的简写:

-- f1Raw exactly as before
f1Raw :: String -> String
f1Raw name = name
-- this next line...
$(withName "f1")
-- ...would expand to these two:
-- f1 :: String
-- f1 = f1Raw "f1"

withName的行为大致为:

  1. 取给定的String,附加"Raw",并为当前模块中的相应事物创建一个Name
  2. 使用内省来获取原始事物的类型,检查它是否以String -> ...开头,然后剥离String ->位并创建声明f1 :: ...Decl
  3. f1 = f1Raw "f1"实现为第二个Decl

最新更新