Доступ к параметрам конфигурации через монаду? - PullRequest
8 голосов
/ 21 мая 2011

Цитата отсюда: http://www.haskell.org/haskellwiki/Global_variables

Если у вас есть глобальная среда, из которой читаются различные функции (и вы можете, например, инициализировать из файла конфигурации), то вам следуетв качестве параметра для ваших функций (после того, как вы, скорее всего, установите его в своем «основном» действии).Если явная передача параметров вас раздражает, то вы можете «спрятать» ее с помощью монады.

Теперь я пишу что-то, что требует доступа к параметрам конфигурации, и мне интересно, может ли кто-нибудь указать мне научебник или любой другой ресурс, который описывает, как монады могут быть использованы для этой цели.Извините, если этот вопрос глуп, я только начинаю ловить монады.Чтение учебника Майка Вайнера о них сейчас.

1 Ответ

5 голосов
/ 21 мая 2011

Основная идея заключается в том, что вы пишете код следующим образом:

main = do
   parameters <- readConfigurationParametersSomehow
   forever $ do
      myData <- readUserInput
      putStrLn $ bigComplicatedFunction myData parameters

bigComplicatedFunction d params = someFunction params x y z
   where x = function1 params d
         y = function2 params x d
         z = function3 params y

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

Проблема с этим стилем заключается в том, что блок параметров должен быть передан каждой маленькой функции, которая должна получить к нему доступ.Это неприятность.Вы обнаружите, что некоторые функции на десять уровней ниже в дереве вызовов теперь нуждаются в некотором параметре времени выполнения, и вам нужно добавить этот параметр времени выполнения в качестве аргумента для всех промежуточных функций.Это известно как tramp data .

Монада "решение" заключается в том, чтобы встроить параметр времени выполнения в Reader Monad и превратить все ваши функции в монадическиедействия.Это избавляет от явного параметра данных tramp, но заменяет его монадическим типом, и под капотом эта монада фактически выполняет вытеснение данных для вас.

Императивный мир решает эту проблему с помощью глобальной переменной.В Haskell вы можете делать то же самое, как это:

parameters = unsafePerformIO readConfigurationParametersSomehow

При первом использовании «параметров» выполняется «readConfigurationParametersSomehow», и с этого момента он ведет себя как постоянное значение, прихотя бы до тех пор, пока ваша программа работает.Это одно из немногих правомерных применений unsafePerformIO.

Однако, если вам понадобится такое решение, вам действительно нужно подумать о своем дизайне.Скорее всего, вы не задумываетесь над обобщением своих функций ниже;если какая-то ранее чистая функция внезапно нуждается в параметре времени выполнения, тогда посмотрите на причину и посмотрите, можете ли вы каким-либо образом использовать функции более высокого порядка.Например:

  • Передайте функцию, построенную с использованием параметра, а не самого параметра.
  • Пусть рабочая функция внизу возвращает функцию, которая передается вверхбыть составленным с помощью функции на основе параметров на более высоком уровне.
  • Выполнить рефакторинг стека вызовов таким образом, чтобы фундаментальные операции выполнялись примитивами более низкого уровня внизу, которые составлены зависимым от параметров способом вверху.

В любом случае будет задействовано

...