在Haskell中,是否可以获取调用方函数名称?我当然可以对其进行硬编码,但这会带来维护负担:如果有人要重命名该功能,则没有什么迫使他们重命名硬编码的名称。
一个人为的例子:
f1 :: String
f1 = "f1" -- can this be automated?
答案 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
的行为大致如下:
String
,附加"Raw"
,并为当前模块中的对应内容创建Name
。String -> ...
开始,然后剥离String ->
位,并创建一个声明Decl
的{{1}}。 f1 :: ...
作为第二f1 = f1Raw "f1"
。