Как я могу использовать монаду Supply для создания функции, которая генерирует глобально уникальные имена?
Фон:
Я делаю проект перевода кода, который требует от меня генерировать имена переменных. Ни одно из имен, которые я генерирую, не должно дублировать друг друга.
Я очень расстроен, потому что это было бы глупо просто и элегантно с функцией генератора Python.
Что я пробовал:
Я делал это раньше, передавая переменную счетчика через рекурсивные вызовы в мой код перевода, и передавал (возможно, увеличенный) счетчик обратно в возвращаемое значение практически каждой функции.
Это было действительно грязно: добавлен дополнительный параметр для отслеживания каждой из этих функций; и что еще хуже, это заставило меня работать с беспорядочными возвращаемыми значениями кортежа, где в противном случае у меня было бы простое унарное возвращаемое значение.
За короткое время работы с Haskell я никогда по-настоящему не разбирался в монадах, но у меня было предположение, что я могу использовать обертку на State
монада для имитации глобальной переменной счетчика. После 3 дней бездельничающих попыток получить монады и создать свою собственную, а затем попытаться изменить чужие монады, чтобы получить нужные мне значения, я, наконец, смирился с тем, чтобы использовать чью-то монаду высокого уровня (возможно, с небольшими изменениями.)
Моя проблема сейчас:
Я определил модули MonadSupply и MonadUnique как пару, которые, вероятно, предоставляют простой вид интерфейса, который мне нужен. К сожалению, я не могу понять, как их использовать.
В частности MonadSupply
Документация модуля предоставляет этот хороший пример использования:
runSupplyVars x = runSupply x vars
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
Похоже, что я хочу! Как только я получил модуль для компиляции, я проверил тип этой функции в интерпретаторе:
> :t runSupplyVars
runSupplyVars :: Supply [Char] a -> Identity (a, [[Char]])
Я попытался передать много (многочасовых) разных вещей этой функции, но безуспешно. Я также попытался передать функцию некоторым другим функциям, чтобы посмотреть, будут ли они предоставлять параметры, которые мне неявно нужны. Пока не повезло.
Вопросы:
Может ли кто-нибудь предоставить пример использования этого runSupplyVars
функционировать?
Будет ли возможно сделать то, что я думаю с этим? Я хочу, чтобы у меня была функция, которую я могу вызывать из любой точки программы, которая будет давать мне разные имена переменных или целые числа при каждом вызове.
1 ответ
На самом деле использовать Supply
Монада, вы должны структурировать свой код с do
обозначение и вызов supply
функционировать, когда вам действительно нужно имя.
Например, это создаст новое имя переменной с префиксом var_
просто чтобы показать, как вы можете получить что-то из запаса и использовать его:
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var"_++name)
Вам нужно будет структурировать всю программу вокруг Supply
монада, а затем только вызов runSupplyVars
Однажды на верхнем уровне, в противном случае разные части программы будут иметь независимые поставки и поэтому могут использовать одно и то же имя переменной.
Наконец, вам нужно runIdentity
от Control.Monad.Identity
распаковать результат runSupplyVars
в основной кортеж типа (a, [[Char]])
, а затем отбросьте второе значение, которое является просто (бесконечным) списком неиспользуемых имен. Вам может быть лучше переопределить runSupplyVars
сделать это для вас:
import Control.Monad.Identity
[...]
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
Вот более полный пример, объединяющий все это. Обратите внимание на различные монады, с которыми do
нотация используется - IO
для main
функция и Supply [Char]
за realProgram
и, вероятно, большая часть кода в увеличенной версии:
import MonadSupply
import Control.Monad.Identity
main :: IO ()
main = do
let result = runSupplyVars realProgram
print result
realProgram :: Supply [Char] Int
realProgram = do
x <- newVar
return 0
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var_"++name)
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence