Используя систему типов Haskell для осуществления модульного принципа

Я думаю о способах использовать систему типов Haskell для осуществления модульного принципа в программе. Например, если у меня есть веб-приложение, мне любопытно, если существует способ разделить весь код базы данных от кода CGI из кода файловой системы из чистого кода.

Например, я предполагаю монаду DB, таким образом, я мог записать функции как:

countOfUsers :: DB Int
countOfUsers = select "count(*) from users"

Я хотел бы, чтобы это было невозможно использовать побочные эффекты кроме поддерживаемых монадой DB. Я изображаю высокоуровневую монаду, которая была бы ограничена прямыми обработчиками URL и сможет составить вызовы к монаде DB и монаде IO.

Действительно ли это возможно? Действительно ли это мудро?

Обновление: Я закончил тем, что достиг этого с Scala вместо Haskell: http://moreindirection.blogspot.com/2011/08/implicit-environment-pattern.html

15
задан Bill 4 December 2011 в 22:38
поделиться

2 ответа

Я представляю себе монаду более высокого уровня, которая будет ограничена прямыми обработчиками URL и сможет компоновать вызовы монады DB и монады IO.

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

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

Таким образом, вы сможете выполнять код CGI только в контексте CGI, а код DB - только в контексте DB. На Hackage есть много примеров.

Другой способ - создать интерпретатор для действий, а затем использовать конструкторы данных для описания каждой примитивной операции, которую вы хотите. Операции по-прежнему должны образовывать монаду, и вы можете использовать do-notation, но вместо этого вы будете создавать структуру данных, описывающую действия, которые нужно выполнить, и которые вы затем выполняете контролируемым образом через интерпретатор.

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

13
ответ дан 1 December 2019 в 03:24
поделиться

Спасибо за этот вопрос!

Я работал над клиент-серверным веб-фреймворком, который использовал монады для различения различных сред выполнения. Очевидными были client-side и server-side, но это также позволяло писать both-side код (который мог работать как на клиенте, так и на сервере, поскольку не содержал никаких специальных возможностей), а также asynchronous client-side, который использовался для написания неблокирующего кода на клиенте (по сути, монада продолжения на стороне клиента). Это звучит довольно близко к вашей идее различать код CGI и код DB.

Вот некоторые ресурсы о моем проекте:

Я думаю, это интересный подход, и он может дать вам интересные гарантии о коде. Однако есть несколько непростых вопросов. Если у вас есть серверная функция, которая принимает int и возвращает int, то каким должен быть тип этой функции? В своем проекте я использовал int -> int server (но можно также использовать server (int -> int)).

Если у вас есть несколько функций, подобных этой, то компоновать их не так просто. Вместо того, чтобы написать goo (foo (bar 1)), вам нужно написать следующий код:

do b <- bar 1
   f <- foo b
   return goo f

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

4
ответ дан 1 December 2019 в 03:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: