fjvallarino/monomer

Using sum types in Model

Closed this issue · 3 comments

I'm trying to make a text-based game using monomer, but I'm struggling a bit with making different views. For example, when the app is launched, I want the user to be in the main menu view. From there they can click on a button to move into the game view or the options view. The way I would implement this is to define the app model as a sum type like this:

data AppModel
  = MainMenuView MainMenuModel
  | GameView GameModel
  | OptionsView OptionsModel

Now when I want to add fields to the options view, the most convenient way to do this would be to use lenses, but since AppModel is a sum type, I only have access to a prism, which is not accepted by, say, numericField.

I also considered adding a data kind parameter to the app model so that I can keep track which view I'm in on the type level, but then I get stuck whenever I need to switch from one view to another, because the EventHandler can only switch to a new model if it has the exact same type.

So my question is, what's the monomer way to make apps with multiple views?

Hi @Soupstraw!

I think for your use case a good solution is splitting your views into separate Composites, and have a main function create an instance of the correct view passing a valid lens. Since you are using a sum type you have a prism, but you can convert it to a lens with singular (provided by the Lens package). Because of the need to transform the prism into a lens there is some sort of dispatch logic duplication, since you have to inspect your model, but the general idea is:

buildScreen :: AppModel -> WidgetNode AppModel AppEvt
buildScreen MainMenuView{} = mainMenuViewComposite (singular _MainMenuView)
buildScreen GameView{} = gameViewComposite (singular _GameView)

You may not need to create Composites for each view, but that way you make sure to only need to call singular once per view, instead of potentially needing it on each widget you use (that was my experience at least). With Composite you also have the option of using a different event type for each view.

In case you run into issues, if you provide a minimal example of what you currently have I can take a look (something I can checkout and build).

Ahh, I wasn't aware of singular, I'll try it out.

@Soupstraw I'll close the issue for now. Please re-open if needed or create a new one. Thanks!