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 parent props model action
     = RWS (ComponentInfo parent props) [Schedule 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 parent props model action = RWS (ComponentInfo parent props) [Schedule 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 parent props Source #

This is the 'Reader r' in Effect. Accessible via ask. It holds a phantom type for parent. This is used as a witness when calling the parent function. 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 
-> ComponentInfo parent props 

Smart constructor for ComponentInfo

IO

data Schedule 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.

Since: 1.9.0.0

Constructors

Schedule Synchronicity (Sink action -> IO ()) 

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 parent props model action infixl 0 Source #

Smart constructor for an Effect with exactly one action.

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

Effect smart constructor, flipped

batch Source #

Arguments

:: [IO action]

Batch of IO actions to execute

-> Effect parent props model action 

Smart constructor for an Effect with multiple IO actions.

Since: 1.9.0.0

batch_ :: [IO ()] -> Effect parent 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 parent 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 parent 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 parent 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 parent 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 parent 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 parent props model action 

Issue a new action to be processed by update.

data Action = HelloWorld
type Model  = Int

update :: Action -> Effect parent 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 parent 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

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 parent 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 parent props model action

Effect whose IO actions are modified

-> Effect parent 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 parent props model action

Effect whose IO actions are modified

-> Effect parent 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 parent props model action

Effect whose IO actions are modified

-> Effect parent 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 parent 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 parent 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 parent 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 parent props model action -> ComponentInfo parent props -> model -> (model, [Schedule action]) Source #

Internal function used to unwrap an Effect

Props

componentInfoProps :: Lens (ComponentInfo parent 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 parent 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 parent props) m => m props Source #

props retrieval from within the Effect monad.

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