Функция для вывода имени функции

Можно ли в Haskell реализовать функцию, которая возвращает собственное имя функции?

Возможный тип может быть (a -> b) -> String.


person Stuart Paton    schedule 07.03.2013    source источник
comment
Нет. Функция типа (a -> b) -> String не может проверить свой аргумент (кроме seq его обработки или применения его снизу) из-за своего полиморфного типа. Таким образом, у него нет средств для идентификации своего аргумента.   -  person Daniel Fischer    schedule 07.03.2013
comment
Если функция возвращает собственное имя функции, разве тип не будет () -> String (и реализация довольно тривиальна)?   -  person sepp2k    schedule 07.03.2013
comment
Большинство функций в Haskell даже не имеют имен. Что-то, что могло бы существовать, было бы функцией, которая показывает тип выражения, хотя я сомневаюсь, что это было бы очень полезно.   -  person Cubic    schedule 07.03.2013
comment
Если бы у вас была такая функция, для чего бы вы ее использовали? Вероятно, есть более простое решение вашей проблемы.   -  person amindfv    schedule 07.03.2013
comment
В дополнение к тому, что сказал Даниэль Фишер, это нарушит рассуждения об уравнениях: let f = id in findName f предположительно будет предназначено для производства "f", но findName id предположительно будет предназначено для производства "id". Это все связано с тем, что у функций просто нет имен; переменные могут быть в области видимости, которая имеет тип функции, но это все.   -  person Antal Spector-Zabusky    schedule 08.03.2013
comment
Я нашел этот вопрос, потому что искал способ отследить выполнение определенных функций без необходимости изменять каждую функцию. Не уверен, как это сделать в Haskelly, но я искал эквивалент того, как log4j использует отражение, чтобы дать контекст выполнения класса при ведении журнала.   -  person Adam Burke    schedule 27.11.2020


Ответы (5)


Вам нужна функция, которая принимает аргумент функции и возвращает имя переменной сайта определения, которое соответствует имени этой функции?

Это невозможно без метапрограммирования, которое обычно является признаком того, что вы делаете что-то не так :). Но если предположить, что это не так, один из способов добиться чего-то в правильном направлении — использовать Template Haskell, который может получить в уникальных именах (как компилятор называет вещи). Например.

Prelude Language.Haskell.TH> :set -XTemplateHaskell
Prelude Language.Haskell.TH> let f x y = x + y
Prelude Language.Haskell.TH> $( stringE . show =<< reify 'f )

     "VarI f_1627394057
                (ForallT [PlainTV a_1627394063]
                         [ClassP GHC.Num.Num [VarT a_1627394063]]
                              (AppT (AppT ArrowT (VarT a_1627394063)) 
                                    (AppT (AppT ArrowT (VarT a_1627394063)) 
                                         (VarT a_1627394063)))) 
                         Nothing (Fixity 9 InfixL)"

И теперь мы многое знаем о переменной. Таким образом, вы можете играть в игры, передавая имя функции (через 'f), а не сам f.

Вы, безусловно, находитесь в мире размышлений и метапрограммирования, поэтому было бы полезно узнать больше о том, что вы пытаетесь сделать.

person Don Stewart    schedule 07.03.2013

Чтобы уточнить что-то, упомянутое в посте Дона: в Haskell нет функций с именами. Существуют привязки, которые могут связывать функции, но если бы у меня была такая функция (назовите ее getName), которую вы ищете, то что бы вы ожидали, что она вернется:

let f x = x
    g   = f
    h   = f
in  getName g == getName h
person J. Abrahamson    schedule 07.03.2013

Я не знаю, для чего вам это нужно, но, может быть, достаточно упрощенного решения? Вот так:

data NamedFunction a b = NamedFunction { 
    name :: String,
    apply :: a -> b
}

timesTwo :: NamedFunction Int Int
timesTwo = NamedFunction "timesTwo" (\x -> 2 * x)

который вы можете использовать следующим образом:

ghci> timesTwo `apply` 7
14
ghci> name timesTwo
"timesTwo"

Затем вы можете написать свою собственную версию (.):

-- contrast (.)  ::    (b -> c) ->          (a -> b) ->         (a -> c)
compose :: NamedFunction b c -> NamedFunction a b -> NamedFunction a c
compose (NamedFunction n1 f1) (NamedFunction n2 f2) = 
     NamedFunction (n1++ " . " ++ n2) (f1 . f2)

В ГКИ:

ghci> let f = timesTwo `compose` timesTwo in (f `apply` 7, name f) 
(28,"timesTwo . timesTwo")

Вам придется заново реализовать свои собственные версии map, filter и так далее, и позже вы обязательно столкнетесь с другими проблемами, но, возможно, это все, что вам нужно...

person yatima2975    schedule 07.03.2013

Я что-то пропустил? Эта функция возвращает собственное имя функции.

Prelude> let myNameIs::(a->b) -> String; myNameIs f = "myNameIs"
Prelude> :type myNameIs
myNameIs :: (a -> b) -> String
Prelude> myNameIs myNameIs
"myNameIs"
person גלעד ברקן    schedule 07.03.2013

Вы можете предварительно обработать исходный код с помощью CPP. В СРР

#define _NAMEOF(name) #name

определяет макрос _NAMEOF для преобразования текста в строки (включая окружение его программистскими кавычками). Затем вы можете использовать его следующим образом:

head [] = error $ _NAMEOF(head) ++ ": empty list!"

который CPP должен переводиться в действительную строку исходного кода Haskell:

head [] = error $ "head" ++ ": empty list!"
person Bjartur Thorlacius    schedule 02.11.2020