У меня небольшая часть архитектурной проблемы, для которой я хотел бы увидеть, есть ли общий шаблон или абстракция, которые могут мне помочь. Я писал игровой движок, где пользователь может указать игровой цикл как монадическое вычисление формы:
gameLoop :: TimeStep -> a -> Game a
где монада Game
имеет кучу точек доступа для рисования, преобразования и взаимодействия с движком в целом. Затем я также предоставляю функцию, которую пользователь вызывает для запуска моделирования
runGame :: (TimeStep -> a -> Game a) -> a -> IO a
Одной из основных целей дизайна библиотеки было не сделать Game
экземпляр класса MonadIO
. Это делается для того, чтобы пользователь не стрелял в ногу, изменяя состояние основных графических вызовов или загружая вещи, когда они не ожидаются. Однако часто используются случаи, когда результат IO a
полезен после того, как игровой цикл уже начался. В частности, приходит в голову нерест врагов с процедурно генерируемыми графическими элементами.
В результате я хочу разрешить пользователю запрашивать ресурсы, используя что-то похожее на следующий интерфейс:
data ResourceRequestResult a
= NotLoaded
| Loaded a
newtype ResourceRequest a = ResourceRequest {
getRequestResult :: Game (ResourceRequestResult a)
}
requestResource :: IO a -> Game (ResourceRequest a)
При этом я хотел бы разветкить поток для загрузки ресурса и передать результат в контексте монады Game
и обратно пользователю. Основная цель будет заключаться в том, что я решаю, когда происходит действие IO - где-то, что я ожидаю, а не в середине игрового цикла.
Одна из идей, которую я имел в виду, заключалась в том, чтобы разместить другой пользовательский трансформатор монады поверх монады Game
... что-то вроде
newtype ResourceT r m a = ResourceT (StateT [ResourceRequest r] m a)
Однако я считаю, что тогда указание вещей в терминах f :: ResourceT r Game a
становится кошмаром API, так как мне придется поддерживать любую возможную комбинацию стеков трансформаторов монады. В идеале я также хотел бы избежать создания Game
полиморфного в r
, поскольку это увеличило бы многословие и переносимость базовых функций Game
.
Есть ли у Haskell какие-либо абстракции или идиомы для чего-то вроде этого шаблона программирования? Я не хочу, чтобы это было невозможно?