miso
Copyright(C) 2016-2026 David M. Johnson
LicenseBSD3-style (see the file LICENSE)
MaintainerDavid M. Johnson <code@dmj.io>
Stabilityexperimental
Portabilitynon-portable
Safe HaskellSafe-Inferred
LanguageHaskell2010

Miso.Effect

Description

Overview

Miso.Effect defines the three core abstractions used in the Model-View-Update loop:

  • Effect — the monad returned by every update handler. Combines a state update on model with a list of IO actions to schedule.
  • Sub — a long-running subscription (Sink action -> IO ()) that feeds actions into the event queue from threads, timers, WebSockets, etc.
  • Sink — a function (action -> IO ()) that enqueues a single action for processing by update.

The Effect monad

type Effect context props model action
     = RWS (ComponentInfo context props) [Schedule context action] model ()

The RWS decomposition:

Scheduling IO

By default all IO runs asynchronously in a separate thread after the VDOM has been patched. Use sync / sync_ to block the render thread:

update = \case
  Fetch    -> io   (fetchData >>= pure . GotData)  -- async
  LogIt    -> io_  (consoleLog "hi")               -- async, no action
  Urgent   -> sync (pure SomeSyncAction)           -- blocks render
  Many     -> batch [a1, a2, a3]                   -- multiple async
  Opt      -> for  (fetchMaybe >>= pure)           -- Maybe/Foldable

Subscriptions

A Sub is a function that receives a Sink and runs forever (typically on a forked thread). Register subscriptions in subs:

tickSub :: Sub Action
tickSub sink = forever $ do
  threadDelay 16667
  sink Tick

myComponent = (component model update view) { subs = [tickSub] }

Use mapSub to adapt a Sub a into a Sub b with a mapping function.

Component metadata

Within update, access the current component's runtime info through ask or the provided lenses:

update = \case
  Init -> do
    domRef <- view componentInfoDOMRef
    compId <- view componentInfoId
    myProps <- getProps
    io_ (initThirdParty domRef)

See also

Synopsis

Effect

Types

type Effect context props model action = RWS (ComponentInfo context props) [Schedule context action] model () Source #

A monad for succinctly expressing model transitions in the update function.

Effect is a RWS, where the State allows modification to model. It's also a Writer Monad, where the accumulator is a list of scheduled IO actions. Multiple actions can be scheduled using tell from the mtl library and a single asynchronous action can be scheduled using io_.

An Effect represents the results of an update action.

It consists of the updated model and a list of subscriptions. Each Sub is run in a new thread so there is no risk of accidentally blocking the application.

Tip: use the Effect monad in combination with the stateful Miso.Lens operators (all operators ending in "="). The following example assumes the lenses field1, counter and field2 are in scope and that the LambdaCase language extension is enabled:

myComponent = Component
  { update = \case
      MyAction1 -> do
        field1 .= value1
        counter += 1
      MyAction2 -> do
        field2 %= f
        io_ $ do
          consoleLog "Hello"
          consoleLog "World!"
  , ...
  }

type Sub action = Sink action -> IO () Source #

Type synonym for constructing subscriptions.

For example usage see Miso.Subscription

The Sink function is used to write to the global event queue.

type Sink action = action -> IO () Source #

Function to write to the global event queue for processing by the scheduler.

type DOMRef = JSVal Source #

Type to represent a DOM reference

data ComponentInfo context props Source #

This is the 'Reader r' in Effect. Accessible via ask. It holds a phantom type for context (the app-global React-style context, which is write-only from within update). It gives access to Component metadata such as the DOMRef the Component was mounted on and the ComponentId associated with it.

Constructors

ComponentInfo 

Fields

mkComponentInfo Source #

Arguments

:: ComponentId

ComponentId

-> ComponentId

parent ComponentId

-> DOMRef

DOMRef

-> props

props

-> context

context

-> ComponentInfo context props 

Smart constructor for ComponentInfo

IO

data Schedule context action Source #

Represents a scheduled Effect that is executed either synchronously or asynchronously.

All IO is by default asynchronous, use the sync function for synchronous execution. Beware sync can block the render thread for a specific Component.

N.B. During Component unmounting, all effects are evaluated synchronously.

The ContextModify constructor carries a pending mutation to the app-global React-style context. It is emitted by modifyContext / putContext and applied to the global context during the scheduler's commit phase, triggering a re-render of every Component with useContext enabled.

Since: 1.9.0.0

Constructors

Schedule Synchronicity (Sink action -> IO ()) 
ContextModify (context -> context) 

data Synchronicity Source #

Type to indicate if effects should be handled asynchronously or synchronously.

Constructors

Async 
Sync 

Instances

Instances details
Show Synchronicity Source # 
Instance details

Defined in Miso.Effect

Eq Synchronicity Source # 
Instance details

Defined in Miso.Effect

Combinators

(<#) :: model -> IO action -> Effect context props model action infixl 0 Source #

Smart constructor for an Effect with exactly one action.

(#>) :: IO action -> model -> Effect context props model action infixr 0 Source #

Effect smart constructor, flipped

batch Source #

Arguments

:: [IO action]

Batch of IO actions to execute

-> Effect context props model action 

Smart constructor for an Effect with multiple IO actions.

Since: 1.9.0.0

batch_ :: [IO ()] -> Effect context props model action Source #

Like batch but actions are discarded

Since: 1.9.0.0

io Source #

Arguments

:: IO action

IO action to execute asynchronously

-> Effect context props model action 

Schedule a single IO action for later execution.

Note that multiple IO action can be scheduled using tell from the mtl library.

Since: 1.9.0.0

io_ Source #

Arguments

:: IO ()

IO action to execute asynchronously

-> Effect context props model action 

Like io but doesn't cause an action to be dispatched to the update function.

This is handy for scheduling IO computations where you don't care about their results or when they complete.

Note: The result of IO a is discarded.

Since: 1.9.0.0

sync Source #

Arguments

:: IO action

IO action to execute synchronously

-> Effect context props model action 

Schedule a single IO action, executed synchronously. For asynchronous execution, see io.

Please use this with caution because it will block the render thread.

Since: 1.9.0.0

sync_ Source #

Arguments

:: IO ()

IO action to execute synchronously

-> Effect context props model action 

Like sync, except discards the result.

Since: 1.9.0.0

for Source #

Arguments

:: Foldable f 
=> IO (f action)

actions executed in batch.

-> Effect context props model action 

Like io but generalized to any instance of Foldable

This is handy for scheduling IO computations that return a Maybe value

Since: 1.9.0.0

issue Source #

Arguments

:: action

action to raise

-> Effect context props model action 

Issue a new action to be processed by update.

data Action = HelloWorld
type Model  = Int

update :: Action -> Effect context props Model Action
update = \case
  Click -> issue HelloWorld

Since: 1.9.0.0

withSink Source #

Arguments

:: (Sink action -> IO ())

Callback function that provides access to the underlying Sink.

-> Effect context props model action 

withSink allows users to write to the global event queue. This is useful for introducing IO into the system. A synonym for tell, specialized to Effect.

A use-case is scheduling an IO computation which creates a 3rd-party JS widget which has an associated callback. The callback can then call the sink to turn events into actions.

update FetchJSON = withSink $ \sink -> getJSON (sink . ReceivedJSON) (sink . HandleError)

Since: 1.9.0.0

modifyContext Source #

Arguments

:: (context -> context)

Transformation to apply to the global context

-> Effect context props model action 

Mutate the app-global React-style context from within update.

The supplied function is scheduled as a ContextModify and folded over the current global context during the scheduler's commit phase. If the context value changes (per its Eq instance), every Component with useContext enabled is re-rendered.

Note that context is write-only inside update; to read it, use the context argument threaded into the view function.

update Toggle = modifyContext (\theme -> if theme == Light then Dark else Light)

Since: 1.13.0.0

modifyContext_ Source #

Arguments

:: State context ()

State computation describing the context mutation

-> Effect context props model action 

Mutate the app-global React-style context using a State computation.

A convenience wrapper around modifyContext that runs the supplied State context action over the current global context (via execState), scheduling the resulting context -> context transformation as a ContextModify. This lets you use put / modify and the lens operators from Miso.Lens to update context, mirroring how model is updated.

update Toggle = modifyContext_ $ theme .= Dark

Since: 1.13.0.0

putContext Source #

Arguments

:: context

New global context value

-> Effect context props model action 

Replace the app-global React-style context with a new value.

A convenience wrapper around modifyContext. See modifyContext for details of when re-renders are triggered.

Since: 1.13.0.0

mapSub Source #

Arguments

:: (a -> b)

Function to map actions produced by the subscription

-> Sub a

Source subscription delivering a actions

-> Sub b 

Turn a Sub that consumes actions of type a into a Sub that consumes actions of type b using the supplied function of type a -> b.

noop :: action -> Effect context props model action Source #

Helper for Component construction, when you want to ignore the update function temporarily, or permanently.

Since: 1.9.0.0

beforeAll Source #

Arguments

:: IO ()

IO action to prepend before all scheduled effects

-> Effect context props model action

Effect whose IO actions are modified

-> Effect context props model action 

Performs the given IO action before all IO actions collected by the given effect.

-- delays connecting a websocket by 100000 microseconds
beforeAll (liftIO $ threadDelay 100000) $ websocketConnectJSON OnConnect OnClose OnOpen OnError

Since: 1.9.0.0

afterAll Source #

Arguments

:: IO ()

IO action to append after all scheduled effects

-> Effect context props model action

Effect whose IO actions are modified

-> Effect context props model action 

Performs the given IO action after all IO actions collected by the given effect.

Example usage:

-- log that running the a websocket Effect completed
afterAll (consoleLog "Done running websocket effect") $ websocketConnectJSON OnConnect OnClose OnOpen OnError

modifyAllIO Source #

Arguments

:: (IO () -> IO ())

Transform to apply to every scheduled IO action in the effect

-> Effect context props model action

Effect whose IO actions are modified

-> Effect context props model action 

Modifies all IO collected by the given Effect.

All IO expressions collected by Effect can be evaluated either synchronously or asynchronously (the default).

This function can be used to adjoin additional actions to all IO expressions in an Effect. For examples see beforeAll and afterAll.

Lens

componentInfoDOMRef :: Lens (ComponentInfo context props) DOMRef Source #

Lens for accessing the underlying Component DOMRef.

  update = case
    SomeAction -> do
      domRef <- view componentDOMRef
      someAction domRef

Since: 1.9.0.0

componentInfoParentId :: Lens (ComponentInfo context props) ComponentId Source #

Lens for accessing the parents's ComponentId from ComponentInfo.

update = case
  SomeAction -> do
    compParentId <- view componentParentId
    someAction compParentId

Since: 1.9.0.0

componentInfoId :: Lens (ComponentInfo context props) ComponentId Source #

Lens for accessing the ComponentId from ComponentInfo.

  update = case
    SomeAction -> do
      compId <- view componentInfoId
      someAction compId

Since: 1.9.0.0

Internal

runEffect :: Effect context props model action -> ComponentInfo context props -> model -> (model, [Schedule context action]) Source #

Internal function used to unwrap an Effect

Props

componentInfoProps :: Lens (ComponentInfo context props) props Source #

Lens for accessing the underlying Component props.

  update = case
    SomeAction -> do
      props <- view componentInfoProps
      someAction props

Since: 1.9.0.0

props :: Lens (ComponentInfo context props) props Source #

Lens for accessing the underlying Component props.

This is a shorter convenience lens that is a synonynm for componentInfoProps. See getProps for usage in the Effect monad.

  update = case
    SomeAction ->
      someAction =<< view props

getProps :: MonadReader (ComponentInfo context props) m => m props Source #

props retrieval from within the Effect monad.

  update = case
    SomeAction -> do
      props <- getProps
      someAction props

Context

componentInfoContext :: Lens (ComponentInfo context props) context Source #

Lens for accessing the underlying Component context.

  update = case
    SomeAction -> do
      ctx <- view componentInfoContext
      someAction ctx

Since: 1.13.0.0

context :: Lens (ComponentInfo context props) context Source #

Lens for accessing the underlying Component context.

This is a shorter convenience lens that is a synonynm for componentInfoContext. See getContext for usage in the Effect monad.

  update = case
    SomeAction ->
      someAction =<< view context

Note: this lens is read-only within Effect. It targets the ComponentInfo reader environment, so setting through it (e.g. with set / .=) has no observable effect. To change the global context from update, use modifyContext, putContext, or modifyContext_ (the State-monad variant, which supports lens operators like .=) instead.

Since: 1.13.0.0

getContext :: MonadReader (ComponentInfo context props) m => m context Source #

Read-only context retrieval from within the Effect monad.

  update = case
    SomeAction -> do
      ctx <- getContext
      someAction ctx

Since: 1.13.0.0