2016-08-12 6 views
1

такого рода вопрос к моему предыдущему: State and IO MonadsКак сбрасывать блок трансформатора монады из основного?

Моя цель - создать простой текстовый редактор для файлов. У меня уже есть компонент Editor, который прекрасно инкапсулирует все действия по редактированию в базовой структуре данных.

Благодаря ответы на мой предыдущий вопрос, я был в состоянии реорганизовать свою программу, так что теперь у меня есть хороший стек монады трансформатора:

type Session = StateT AppState (StateT Editor IO) 

AppState держит глобальное состояние приложения (в настоящее время открытого файла , и т. д.), а Editor - это внутреннее состояние редактирующего компонента приложения (где находится каретка и т. д.). У меня есть функция, которая является основным драйвером приложения:

eventLoop :: Session() 

До сих пор так хорошо, однако сейчас я не знаю, как я могу на самом деле ножной стартер моего стек трансформатора от моей main функции? Главная должна вернуть что-то в монаде IO, которая находится в самой базе моего стека. Я думаю, что я должен инициализировать моей AppState, а затем сделать что-то вроде:

main = do 
    let initialAppState = ... 
    return $ runStateT eventLoop initialAppState 

Но где я инициализировать мой Editor сейчас?

Главное заблуждение меня является то, что перед рефакторинга, Editor был просто членом AppState:

data AppState = { editor :: Editor , ... } 

, но теперь она переместилась из AppState и стал своего родного брата в стеке трансформатора. Должно ли Editor быть частью AppState, потому что его изменение означает изменение общего состояния?

Как правильно инициализировать мой Session как с AppState и Editor, а затем запустить его из моей main?

+0

Для обхода 'StateT' внутри' StateT' потребуется неудобная комбинация лифтов для доступа к правильному состоянию. Лучше было бы использовать только «StateT (AppState, EditorState)» (или собственный тип данных), чтобы вы могли использовать 'get' и такие как' get' MonadState, и без подъема. –

ответ

2

Как я могу на самом деле запустить мой блок трансформатора из своей основной функции?

main = 
    flip evalStateT initialAppState $ 
    flip evalStateT initialEditorState $ 
    eventLoop 
    where 
    initialAppState = 
     error "Define me" 
    initialEditorState = 
     error "Define me" 

не должен редактор все еще быть частью AppState, потому что изменение означает изменение общего состояния?

Это зависит.

Помните, что целью Monad Transformer является расширение функциональности специальным образом? Под ad-hoc я имею в виду, не переписывая существующую кодовую базу, а добавляя к ней. Итак, если у вас уже есть изолированные API-интерфейсы Editor и AppState, проще просто объединить их в другой модуль «купола», используя стек трансформатора.

OTOH, с точки зрения начальной архитектуры, совершенно очевидно, что AppState представляет собой структуру данных, которая включает в себя Редактор (я бы назвал его EditorState) между прочим. В таком случае API AppState должен инкапсулировать API Редактора.Библиотека «линз» поможет вам много работать с такими составными структурами данных (хотя я должен упомянуть, что она имеет крутую кривую обучения).

+0

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

+0

Добро пожаловать) –