Haskell: получить имя вызывающей функции - PullRequest
2 голосов
/ 19 февраля 2020

В Haskell есть ли способ получить имя функции вызывающей стороны? Конечно, я мог бы жестко закодировать его, но это обременительно для обслуживания: если кто-то переименует функцию, ничто не заставит его переименовать жестко закодированное имя.

Придуманный пример:

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

Ответы [ 2 ]

9 голосов
/ 19 февраля 2020

Вы можете сделать это с помощью GH C 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, который изоморфен c до [(String, SrcLoc)], где первый элемент каждой пары является именем функции; getCallStack преобразует абстрактный тип CallStack в фактический список пар.

(Документы, похоже, утверждают, что ограничения HasCallStack могут быть выведены, но в моих очень коротких экспериментах этого не произошло; если я использовал callStack в функции без подписи, я просто получал пустой стек вызовов; вероятно, лучше всего явно написать подпись с ограничением HasCallStack)

2 голосов
/ 19 февраля 2020

Суть идеи: сделать f1 параметризованной вещью. Это позволит объединить кодовое имя и строку, представляющую кодовое имя, чтобы уменьшить вероятность того, что кто-то переименует одно без другого. Итак:

f1Raw :: String -> String
f1Raw name = name

f1 :: String
f1 = f1Raw "f1"

С небольшим количеством взлома Template 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, который объявляет f1 :: ....
  3. Реализация f1 = f1Raw "f1" в секунду Decl.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...