dunno
dunno

Reputation: 65

Haskell: Get caller function name

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

Answers (2)

Ben
Ben

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

Daniel Wagner
Daniel Wagner

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:

  1. Take the given String, append "Raw", and create a Name for the corresponding thing in the current module.
  2. Use introspection to get the type of the raw thing, check that it starts out String -> ..., then strip the String -> bit and create a Decl that declares f1 :: ....
  3. Implement f1 = f1Raw "f1" as a second Decl.

Upvotes: 2

Related Questions