Skip to content

Request for comment: EventM state monad refactor #379

@jtdaugherty

Description

@jtdaugherty

This ticket is a place for discussing the impact of work that is happening on the refactor/event-state-monad branch. The objective of this branch is essentially to refactor EventM to make event handler code more modular, easier to write, and less verbose. This branch introduces several breaking API changes.

If you write/maintain brick applications, I'd love your thoughts on how this will impact your applications (positively and negatively) if this were to be merged and released. I'd also love feedback if you actually try using the branch as well. Thanks!

Examples of the impact of these changes in action can be found in the demo programs. Here are a few examples:

Here's a summary of the changes and their impacts:

  • The EventM type is changing from EventM n a to EventM n s a. EventM n s a is now a MonadState over s. (The state monad introduced in this change is strict in s.) This means that you can either use lenses or the mtl state monad API to manage your state. Therefore, event handler functions that previously took an explicit state argument and returned a Next are now state-monadic over that state type instead. For example, a function that was previously written as handleMyEvent :: s -> BrickEvent n e -> EventM n (Next s) will now be written handleMyEvent :: BrickEvent n e -> EventM n s ().
  • The Next type is going away from the user-facing API. This means EventM blocks in event handlers no longer need to end with continue, halt, etc. Instead, the behavior previously provided by the continue function is now the default behavior for any EventM block, and halt etc. need to be called explicitly to indicate what the library's main event loop should do once the event handler terminates. Functions like halt do not change control flow of EventM (i.e. do not cause early aborting).
  • continue itself was removed from the API. A practical consequence of this is that if an EventM block now calls halt, it cannot undo that by subsequently calling continue since continue no longer exists.
  • handleEventLensed went away and has been replaced with support for the zoom function from microlens-mtl. This works with both lenses and traversals. Brick.Types now re-exports zoom for convenience.
  • The library's event handler functions (e.g. for List, Edit, etc.) are now scoped to just the states they manage; e.g. handleEditorEvent :: BrickEvent n e -> EventM n (Editor t n) (). This means they are now used with zoom, i.e. zoom someLens $ handleEditorEvent e. This also impacted the Form type whose field event handlers correspondingly changed to be scoped to their fields' state types.
  • Brick now depends on mtl rather than transformers.

To me, the biggest practical consequences of these changes are that:

  • Positive: EventM code will be much cleaner, particularly if you like to use lenses to manage your state values. Event handlers can now use lens updates with the microlens-mtl API, e.g., someLens .= someValue etc.
  • Positive: Nested event handlers can now change state types, leading to more focused and more modular event handler implementations.
  • Positive: event handlers will no longer have continue noise since that is the default behavior of any EventM block unless otherwise specified by halt etc.
  • Positive: brick application authors who want to use lenses more often in their event handlers can now do so.
  • Positive: even brick application authors who don't want to use lenses more often can still benefit from the move to make EventM a state monad over the application state type.
  • Negative: Application authors will need to update their event handlers to use the new API. Depending on the application, this may be quite a bit of work. If this gets released, I'll provide upgrade guidance documentation.
  • Negative: Applications that previously used their own event handler wrapper monads around EventM will have to contend with these changes as well. (I suspect this will be rare.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions