Haskell:获取调用者函数名称

时间:2020-02-18 23:12:06

标签: haskell reflection

在Haskell中,是否可以获取调用方函数名称?我当然可以对其进行硬编码,但这会带来维护负担:如果有人要重命名该功能,则没有什么迫使他们重命名硬编码的名称。

一个人为的例子:

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

2 个答案:

答案 0 :(得分:9)

您可以使用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)]同构,其中每对的第一个元素是函数名; getCallStack将抽象的CallStack类型转换为实际的配对列表。

(文档似乎声称可以推断出HasCallStack约束,但是在我的简短实验中却没有发生;如果我在没有签名的函数中使用callStack,我将得到空的调用栈;最好是使用HasCallStack约束来显式地编写签名)

答案 1 :(得分:2)

一个想法的核心:使f1成为参数化的事物。这将使将代码名称和代表代码名称的字符串放在一起成为可能,从而减少了有人重命名一个人而另一个没有重命名的机会。所以:

f1Raw :: String -> String
f1Raw name = name

f1 :: String
f1 = f1Raw "f1"

有了一些Haskell模板黑客,您很可能可以创建一个接口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 ->位,并创建一个声明Decl的{​​{1}}。
  3. f1 :: ...作为第二f1 = f1Raw "f1"