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?
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)
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.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With