Как я могу использовать монаду 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
Другие вопросы по тегам