Reputation: 65
In Haskell, is there a way to get the caller function name? Of course I could hard-code it, but that turns into a maintenance burden: If someone were to rename the function, nothing is forcing them to rename the hard-coded name.
A contrived example:
f1 :: String
f1 = "f1" -- can this be automated?
Upvotes: 4
Views: 433
Reputation: 71400
You can do this with GHC's CallStack functionality.
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"
Produces output This string is being passed to: foo
See my link above for a full description, but basically you can ask to have access to a (partial) call stack in a function by including a HasCallStack
constraint. Then callStack
gets a CallStack
, which is isomorphic to [(String, SrcLoc)]
, where the first element of each pair is a function name; getCallStack
converts the abstract CallStack
type into an actual list of pairs.
(The docs seem to claim that HasCallStack
constraints can be inferred, but in my very brief experiments that wasn't happening; if I used callStack
in a function with no signature I was just getting an empty call stack; probably best to explicitly write a signature with a HasCallStack
constraint)
Upvotes: 10
Reputation: 152682
A kernel of an idea: make f1
be a parameterized thing. This will make it possible to put the code name and the string representing the code name together, to reduce the chances that somebody renames one without the other. So:
f1Raw :: String -> String
f1Raw name = name
f1 :: String
f1 = f1Raw "f1"
With a bit of Template Haskell hackery, it is likely that you could make an interface withName :: String -> Q [Decl]
or so that let you write something like this as a shorthand for this pattern:
-- 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"
The behavior of withName
would be, roughly:
String
, append "Raw"
, and create a Name
for the corresponding thing in the current module.String -> ...
, then strip the String ->
bit and create a Decl
that declares f1 :: ...
.f1 = f1Raw "f1"
as a second Decl
.Upvotes: 2