From 4d5318161c55841f2ac471a7e47b460c18f3d228 Mon Sep 17 00:00:00 2001 From: dmjio Date: Wed, 27 May 2026 01:34:35 -0500 Subject: [PATCH] Additional haddocks Courtesy of Claude Sonnet 4.6. --- ffi/ghc/Miso/DSL/FFI.hs | 2 + src/Miso.hs | 300 +++++++++++++++---------------- src/Miso/Binding.hs | 16 +- src/Miso/DSL.hs | 9 +- src/Miso/Data/Array.hs | 6 +- src/Miso/Data/Map.hs | 6 +- src/Miso/Data/Set.hs | 6 +- src/Miso/Date.hs | 4 +- src/Miso/Effect.hs | 18 +- src/Miso/FFI/Internal.hs | 6 +- src/Miso/FFI/QQ.hs | 2 +- src/Miso/JSON.hs | 119 +++++++++++- src/Miso/JSON/Lexer.hs | 7 + src/Miso/JSON/Parser.hs | 2 + src/Miso/JSON/Types.hs | 21 ++- src/Miso/Lens.hs | 15 +- src/Miso/Lens/Generic.hs | 15 +- src/Miso/Prelude.hs | 8 +- src/Miso/Random.hs | 2 +- src/Miso/Reload.hs | 24 +-- src/Miso/Router.hs | 4 + src/Miso/Runtime.hs | 2 +- src/Miso/String.hs | 2 +- src/Miso/Subscription/History.hs | 2 +- src/Miso/Types.hs | 26 +-- 25 files changed, 393 insertions(+), 231 deletions(-) diff --git a/ffi/ghc/Miso/DSL/FFI.hs b/ffi/ghc/Miso/DSL/FFI.hs index f0e1a9908..9109d98e5 100644 --- a/ffi/ghc/Miso/DSL/FFI.hs +++ b/ffi/ghc/Miso/DSL/FFI.hs @@ -95,9 +95,11 @@ isUndefined_ffi = undefined freeFunction_ffi :: JSVal -> IO () freeFunction_ffi = undefined ----------------------------------------------------------------------------- +-- | Schedules a callback to run before the next repaint. requestAnimationFrame :: JSVal -> IO Int requestAnimationFrame = undefined ----------------------------------------------------------------------------- +-- | Cancels a previously-scheduled 'requestAnimationFrame' callback. cancelAnimationFrame :: Int -> IO () cancelAnimationFrame = undefined ----------------------------------------------------------------------------- diff --git a/src/Miso.hs b/src/Miso.hs index 774bc4c64..a379e8b7e 100644 --- a/src/Miso.hs +++ b/src/Miso.hs @@ -30,7 +30,7 @@ -- miso supports common areas that arise naturally in web development: -- -- * __DOM manipulation__: @miso@ uses a [Virtual DOM](https://en.wikipedia.org/wiki/Virtual_DOM) with diffing algorithm that is --- responsible for all DOM modification and 'Component' lifecycle hooks. +-- responsible for all DOM modification and 'Miso.Types.Component' lifecycle hooks. -- -- * __Event delegation__: All event listeners are attached to a top-level element -- (typically @\@). When raised, events are routed through the virtual DOM @@ -45,25 +45,25 @@ -- ("Miso.Html.Render") to render HTML on the server and the 'miso' function exists on the client to \"hydrate\" -- the virtual DOM with the DOM. -- --- * __Components__: A 'Component' can be considered an instance of a @miso@ application. A 'Component' +-- * __Components__: A 'Miso.Types.Component' can be considered an instance of a @miso@ application. A 'Miso.Types.Component' -- contains user-defined state, logic for updating this state, and a function --- for creating UI templates from this user-defined state. 'Component' can nest other 'Component' because @miso@ +-- for creating UI templates from this user-defined state. 'Miso.Types.Component' can nest other 'Miso.Types.Component' because @miso@ -- is defined recursively. -- -- * __Custom renderers__: The underlying DOM operations are able to be abstracted. -- This allows a custom rendering engine to be used. This is seen in the [miso-lynx](https://github.com/haskell-miso/miso-lynx) project -- (which allows miso to target mobile phone devices). -- --- * __Lifecycle hooks__: 'Component' expose 'Miso.Types.mount' and 'Miso.Types.unmount' lifecycle hooks. This allow users to define custom logic that will --- execute when a 'Component' mounts or unmounts. 'Miso.Event.onCreated' and 'Miso.Event.onDestroyed' are 'VNode' specific lifecycle hooks. --- These hooks are commonly used for 'Component' communication and for third-party integration with JavaScript libraries. +-- * __Lifecycle hooks__: 'Miso.Types.Component' expose 'Miso.Types.mount' and 'Miso.Types.unmount' lifecycle hooks. This allow users to define custom logic that will +-- execute when a 'Miso.Types.Component' mounts or unmounts. 'Miso.Event.onCreated' and 'Miso.Event.onDestroyed' are 'VNode' specific lifecycle hooks. +-- These hooks are commonly used for 'Miso.Types.Component' communication and for third-party integration with JavaScript libraries. -- --- * __State management__: 'Component' @model@ state can be manipulated using "Miso.Lens" or "Miso.State" in response to application events. +-- * __State management__: 'Miso.Types.Component' @model@ state can be manipulated using "Miso.Lens" or "Miso.State" in response to application events. -- -- = The Model-View-Update pattern -- --- The core type of miso is 'Component'. The 'Component' API adheres to the [Elm](https://elm-lang.org) --- MVU (model-view-update) interface. This is similar to a left-fold, where the 'Component' @model@ +-- The core type of miso is 'Miso.Types.Component'. The 'Miso.Types.Component' API adheres to the [Elm](https://elm-lang.org) +-- MVU (model-view-update) interface. This is similar to a left-fold, where the 'Miso.Types.Component' @model@ -- will be updated via a list of @action@ given a specific 'Miso.Types.update' function, and rendered via 'Miso.Types.view'. -- -- * __model__: This can be any user-defined type in Haskell. An 'Eq' constraint @@ -82,23 +82,23 @@ -- -- The 'View' type is the core type for both templating and adding interactivity to a web page. It is similar to -- a [Rose tree](https://en.wikipedia.org/wiki/Rose_tree) data structure. This is how the Virtual DOM is constructed. It is --- mutually recursive with the 'Component' type (via the 'view' function), which allows us to embed --- 'Component' inside other 'Component'. +-- mutually recursive with the 'Miso.Types.Component' type (via the 'view' function), which allows us to embed +-- 'Miso.Types.Component' inside other 'Miso.Types.Component'. -- -- @ --- data 'View' model action --- = 'VNode' 'Namespace' 'Tag' ['Attribute' action] ['View' model action] --- | 'VText' (Maybe 'Key') 'MisoString' --- | 'VComp' (Maybe 'Key') ('SomeComponent' model) --- | 'VFrag' (Maybe 'Key') ['View' model action] +-- data View model action +-- = VNode Namespace Tag [Attribute action] [View model action] +-- | VText (Maybe Key) MisoString +-- | VComp (Maybe Key) (SomeComponent model) +-- | VFrag (Maybe Key) [View model action] -- @ -- --- 'VNode' and 'VText' have a one-to-one mapping from the virtual DOM to the physical DOM. The 'VComp' and 'VFrag' constructors are abstract (live only on the virtual DOM) and do not contain a reference to the physical DOM. The existential type of 'SomeComponent' is defined recursively in terms of 'View' and is what allows us to embed other polymorphic 'Component'. +-- 'VNode' and 'VText' have a one-to-one mapping from the virtual DOM to the physical DOM. The 'VComp' and 'VFrag' constructors are abstract (live only on the virtual DOM) and do not contain a reference to the physical DOM. The existential type of t'Miso.Types.SomeComponent' is defined recursively in terms of 'View' and is what allows us to embed other polymorphic 'Miso.Types.Component'. -- -- @ --- data 'SomeComponent' parent +-- data SomeComponent parent -- = forall model action props . (Eq model, Eq props) --- => 'SomeComponent' props ('Component' parent model action) +-- => SomeComponent props (Miso.Types.Component parent model action) -- @ -- -- The smart constructors: @@ -111,10 +111,10 @@ -- -- are used to build 'VNode', 'VText', 'VFrag' and 'VComp' respectively. A list of all the smart constructors defined in terms of 'node' (e.g. 'Miso.Html.Element.div_') can be found in "Miso.Html.Element". -- --- = Your first t'Component' +-- = Your first t'Miso.Types.Component' -- --- To define a 'Component' the 'component' smart constructor can be used. --- Below is an example of a simple counter 'Component'. +-- To define a 'Miso.Types.Component' the 'component' smart constructor can be used. +-- Below is an example of a simple counter 'Miso.Types.Component'. -- -- @ -- ----------------------------------------------------------------------------- @@ -126,12 +126,12 @@ -- import qualified Miso.Html.Event as HE -- import qualified Miso.Html.Property as HP -- ----------------------------------------------------------------------------- --- * - The type of the parent Component 'model' --- | * - The type of the parent Component 'props' accessible to the child --- | | * - The type of the current Component's 'model' --- | | | * - The type of the action that updates the 'model' +-- * - The type of the parent Component model +-- | * - The type of the parent Component props accessible to the child +-- | | * - The type of the current Component's model +-- | | | * - The type of the action that updates the model -- | | | | --- counter :: 'Component' ROOT () Int Action +-- counter :: 'Miso.Types.Component' ROOT () Int Action -- counter = 'vcomp' m u v -- where -- m :: Int @@ -139,8 +139,8 @@ -- -- u :: Action -> 'Effect' ROOT () Int Action -- u = \\case --- Add -> 'this' += 1 --- Subtract -> 'this' -= 1 +-- Add -> this += 1 +-- Subtract -> this -= 1 -- -- v :: () -> Int -> 'View' Int Action -- v _ x = 'vfrag' @@ -159,9 +159,9 @@ -- ----------------------------------------------------------------------------- -- @ -- --- = Running your first t'Component' +-- = Running your first t'Miso.Types.Component' -- --- The 'startApp' (or 'miso') functions are used to run the above t'Component'. +-- The 'startApp' (or 'miso') functions are used to run the above t'Miso.Types.Component'. -- -- @ -- main :: IO () @@ -169,13 +169,13 @@ -- @ -- -- The 'startApp' function is what we recommend using first. It sets up event listeners and performs the initial page draw. --- The 'startApp' function assumes that @\@ is empty, and it will begin drawing the 'Component' 'View' from @\@. +-- The 'startApp' function assumes that @\@ is empty, and it will begin drawing the 'Miso.Types.Component' 'View' from @\@. -- -- The 'miso' function (and also the 'prerender' function) assume that @\@ has already been populated by the results of the 'view' function. -- Instead of drawing, 'miso' will perform hydration. -- If the structures do not match 'miso' will fallback to drawing the page from scratch (clearing the contents of @\@ first). -- --- It is possible to execute an initial action when a t'Component' is first mounted. See the 'mount' (and similarly 'unmount') hooks. +-- It is possible to execute an initial action when a t'Miso.Types.Component' is first mounted. See the 'mount' (and similarly 'unmount') hooks. -- -- @ -- @@ -189,22 +189,22 @@ -- Init -> 'io_' ('consoleLog' "hello world!") -- @ -- --- = 'Component' composition +-- = 'Miso.Types.Component' composition -- --- @miso@ 'Component' can contain other 'Component'. This is --- accomplished through the 'Component' mounting combinator ('+>'). This combinator --- is responsible for encoding a typed 'Component' hierarchy, allowing 'Component' +-- @miso@ 'Miso.Types.Component' can contain other 'Miso.Types.Component'. This is +-- accomplished through the 'Miso.Types.Component' mounting combinator ('+>'). This combinator +-- is responsible for encoding a typed 'Miso.Types.Component' hierarchy, allowing 'Miso.Types.Component' -- type-safe read-only access to their @parent@ model state. -- -- This combinator unifies the parent @model@ with the child @parent@, and -- subsequently the grandchild @parent@ unifies with the child @model@. This --- gives us a correct-by-construction 'Component' hierarchy. +-- gives us a correct-by-construction 'Miso.Types.Component' hierarchy. -- -- @ -- ('+>') -- :: forall child model action a . Eq child -- => 'MisoString' --- -> 'Component' model () child action +-- -> 'Miso.Types.Component' model () child action -- -> 'View' model a -- key '+>' vcomp = 'VComp' (Just (toKey key)) ('SomeComponent' vcomp) -- @ @@ -213,34 +213,34 @@ -- -- @ -- view :: () -> Int -> 'View' Int action --- view x = 'div_' [ 'id_' "container" ] [ "counter" '+>' counter ] +-- view x = div_ [ id_ "container" ] [ "counter" '+>' counter ] -- @ -- --- You'll notice the @\"counter\"@ string was specified. This is a unique 'Key' --- to identify a 'Component' at runtime. These keys are very important when --- diffing two 'Component' together. When intentionally replacing 'Component' it is important --- to specify a new 'Key', otherwise the 'Component' will not be unmounted. +-- You'll notice the @\"counter\"@ string was specified. This is a unique t'Miso.Types.Key' +-- to identify a 'Miso.Types.Component' at runtime. These keys are very important when +-- diffing two 'Miso.Types.Component' together. When intentionally replacing 'Miso.Types.Component' it is important +-- to specify a new t'Miso.Types.Key', otherwise the 'Miso.Types.Component' will not be unmounted. -- --- It is possible to mount a component using the 'mount_' function, which avoids specifying a 'key_', but this should only be used --- when the user is certain they will not be diffing their 'Component' with another 'Component'. When in doubt, use the ('+>') combinator --- and 'key_' your 'Component'. +-- It is possible to mount a component using the 'mount_' function, which avoids specifying a 'Miso.Property.key_', but this should only be used +-- when the user is certain they will not be diffing their 'Miso.Types.Component' with another 'Miso.Types.Component'. When in doubt, use the ('+>') combinator +-- and 'Miso.Property.key_' your 'Miso.Types.Component'. -- -- Lastly, note also the signature of 'startApp'. -- -- @ --- 'startApp' :: 'Eq' model => 'Events' -> 'App' model action -> IO () +-- startApp :: 'Eq' model => 'Events' -> 'App' model action -> IO () -- @ -- --- The 'App' type signature is a synonym for 'Component' 'ROOT' +-- The 'App' type signature is a synonym for 'Miso.Types.Component' 'ROOT' -- -- @ --- type 'App' model action = 'Component' 'ROOT' () model action +-- type App model action = 'Miso.Types.Component' ROOT () model action -- @ -- --- 'ROOT' is a type tag that encodes a 'Component' as top-level. Which means it has no @parent@, hence we mark @parent@ as 'ROOT'. +-- 'ROOT' is a type tag that encodes a 'Miso.Types.Component' as top-level. Which means it has no @parent@, hence we mark @parent@ as 'ROOT'. -- -- @ --- data 'ROOT' +-- data ROOT -- @ -- -- 'startApp' and 'miso' will always infer @parent@ as 'ROOT'. @@ -248,15 +248,15 @@ -- = Props -- -- Inspired by [React props](https://react.dev/learn/passing-props-to-a-component), --- @miso@ allows a parent 'Component' to pass read-only data down to a child 'Component' +-- @miso@ allows a parent 'Miso.Types.Component' to pass read-only data down to a child 'Miso.Types.Component' -- via a mechanism called /props/ (short for /properties/). -- -- == Props vs. Component-local state -- --- * __model__: Component-local state. It is owned and mutated exclusively by the 'Component' --- itself through its 'Miso.Types.update' function. No other 'Component' can write to it directly. +-- * __model__: Component-local state. It is owned and mutated exclusively by the 'Miso.Types.Component' +-- itself through its 'Miso.Types.update' function. No other 'Miso.Types.Component' can write to it directly. -- --- * __props__: Data /inherited/ from the @parent@ 'Component'. Props flow downward through +-- * __props__: Data /inherited/ from the @parent@ 'Miso.Types.Component'. Props flow downward through -- the component hierarchy and are read-only from the child's perspective. The parent decides -- what props to pass at mount time; the child cannot mutate them. Props that change in a -- the parent cause the child to re-render. @@ -278,7 +278,7 @@ -- -- == Props in 'Miso.Types.view' -- --- The 'Miso.Types.view' field of a 'Component' always takes @props@ as its first argument: +-- The 'Miso.Types.view' field of a 'Miso.Types.Component' always takes @props@ as its first argument: -- -- @ -- view :: props -> model -> 'View' model action @@ -312,20 +312,20 @@ -- … -- @ -- --- == 'ROOT' — the top-level 'Component' +-- == 'ROOT' — the top-level 'Miso.Types.Component' -- --- When a 'Component' is passed to 'startApp' (or 'miso') it has no parent. +-- When a 'Miso.Types.Component' is passed to 'startApp' (or 'miso') it has no parent. -- The @parent@ type is specialized to 'ROOT' and @props@ is fixed to @()@: -- -- @ --- type 'App' model action = 'Component' 'ROOT' () model action +-- type 'App' model action = 'Miso.Types.Component' 'ROOT' () model action -- @ -- -- Because there is no parent to inherit from, @props@ will always be @()@ for a --- root-level 'Component'. You can simply ignore the first argument in 'view' and +-- root-level 'Miso.Types.Component'. You can simply ignore the first argument in 'view' and -- skip 'getProps' in 'Miso.Types.update'. -- --- == Passing props to a child 'Component' +-- == Passing props to a child 'Miso.Types.Component' -- -- Use 'mountWithProps_' (keyed) or 'mountWithProps' (unkeyed) in the parent's 'view' to -- mount a child and supply its props: @@ -335,14 +335,14 @@ -- :: ('Eq' child, 'Eq' props) -- => 'MisoString' -- -> props --- -> 'Component' parent props child action +-- -> 'Miso.Types.Component' parent props child action -- -> 'View' parent a -- @ -- -- == Example: child reading parent-supplied props -- --- The following shows a parent 'Component' that maintains a greeting string in its --- @model@ and passes it as @props@ to a child 'Component'. The child renders the +-- The following shows a parent 'Miso.Types.Component' that maintains a greeting string in its +-- @model@ and passes it as @props@ to a child 'Miso.Types.Component'. The child renders the -- greeting and can also read it from within its 'Miso.Types.update' function. -- -- @ @@ -354,18 +354,18 @@ -- -- -- -- parent props model action -- -- | | | | --- child :: 'Component' ParentModel Greeting () ChildAction +-- child :: 'Miso.Types.Component' ParentModel Greeting () ChildAction -- child = 'vcomp' () updateChild viewChild -- where -- viewChild :: Greeting -> () -> 'View' () ChildAction -- viewChild (Greeting g) _ = --- 'div_' [] [ 'text' ("Hello, " <> g <> "!") ] +-- div_ [] [ 'text' ("Hello, " <> g <> "!") ] -- -- updateChild :: ChildAction -> 'Effect' ParentModel Greeting () ChildAction -- updateChild = \\case -- ReadGreeting -> do -- Greeting g <- 'getProps' --- 'io_' ('consoleLog' g) +-- 'io_' (consoleLog g) -- ----------------------------------------------------------------------------- -- -- Parent component: owns the greeting, passes it to the child as props -- parent :: 'App' ParentModel ParentAction @@ -383,21 +383,21 @@ -- A few things to notice: -- -- * The child's @parent@ type parameter is @ParentModel@. This must match the --- parent 'Component' @model@ type — 'mountWithProps_' enforces this at compile time. +-- parent 'Miso.Types.Component' @model@ type — 'mountWithProps_' enforces this at compile time. -- * 'getProps' inside the child's 'Miso.Types.update' yields a @Greeting@, not -- the full @ParentModel@. The child only sees what the parent explicitly chose to share. -- * The root 'App' always has @props ~ ()@; no extra plumbing is needed when calling 'startApp'. -- -- = 'VComp' lifecycle hooks -- --- 'Component' are mounted on the fly during diffing. All t'Component` are equipped with `mount` and `unmount` hooks. This allows the defining of custom actions that will be processed in response to lifecycle events. +-- 'Miso.Types.Component' are mounted on the fly during diffing. All t'Miso.Types.Component' are equipped with @mount@ and @unmount@ hooks. This allows the defining of custom actions that will be processed in response to lifecycle events. -- -- * 'Miso.Types.mount' -- * 'Miso.Types.unmount' -- -- = 'VNode' lifecycle hooks -- --- Similar to t'Component' lifecycle hooks, all 'Miso.Types.VNode' also expose lifecycle hooks. +-- Similar to t'Miso.Types.Component' lifecycle hooks, all 'Miso.Types.VNode' also expose lifecycle hooks. -- -- * 'Miso.Event.onBeforeCreated' -- * 'Miso.Event.onCreated' / 'Miso.Event.onCreatedWith' @@ -418,11 +418,11 @@ -- update :: Action -> 'Effect' parent props model Action -- update = \\case -- Highlight domRef -> 'io_' $ do --- ['js'| hljs.highlight(${domRef}) |] +-- [js| hljs.highlight(${domRef}) |] -- -- view :: model -> 'View' model Action -- view x = --- 'code_' +-- code_ -- [ 'onCreatedWith' Highlight -- ] -- [ """ @@ -439,37 +439,37 @@ -- -- @ -- -- Renders two \ elements as direct siblings, no enclosing element --- 'fragment' [ 'li_' [] [ 'text' "Item A" ], 'li_' [] [ 'text' "Item B" ] ] +-- fragment [ li_ [] [ text "Item A" ], li_ [] [ text "Item B" ] ] -- @ -- --- A 'VFrag' may optionally carry a 'Key'. Keyed fragments participate in the same +-- A 'VFrag' may optionally carry a t'Miso.Types.Key'. Keyed fragments participate in the same -- reconciliation algorithm as keyed 'VNode' and 'VText' nodes, allowing the virtual -- DOM differ to identify, reorder, and reuse groups of siblings efficiently. -- -- @ -- -- Keyed fragment — survives reordering without full teardown\/remount --- 'vfrag_' "my-key" [ 'li_' [] [ 'text' "A" ], 'li_' [] [ 'text' "B" ] ] +-- vfrag_ "my-key" [ li_ [] [ text "A" ], li_ [] [ text "B" ] ] -- @ -- -- Fragments may be nested — a 'VFrag' child may itself be a 'VFrag'. The diff function -- recurses into nested fragments and processes all fragments as if they were -- flat sequence of sibling DOM nodes, so nesting carries no runtime cost beyond the extra 'VFrag' constructor allocation. -- --- Empty fragments (@'fragment' []@) in child nodes are erased from the virtual DOM tree in the +-- Empty fragments (@fragment []@) in child nodes are erased from the virtual DOM tree in the -- Haskell layer before they reach diffing in JavaScript and are therefore a no-op. -- -- The smart constructors for 'VFrag' are: -- --- * 'fragment' — unkeyed fragment +-- * 'Miso.Types.fragment' — unkeyed fragment -- * 'vfrag' — unkeyed fragment (alias) -- * 'fragment_' — keyed fragment -- * 'vfrag_' — keyed fragment (alias, infix-friendly: @\"key\" \`vfrag_\` [...]@) -- --- = 'Key' +-- = t'Miso.Types.Key' -- --- A 'Key' is a unique identifier used to optimize diffing. +-- A t'Miso.Types.Key' is a unique identifier used to optimize diffing. -- --- Virtual DOM nodes can be \"keyed\" (See 'key_'). Keys have multiple meanings in @miso@ (and react). +-- Virtual DOM nodes can be \"keyed\" (See 'Miso.Property.key_'). Keys have multiple meanings in @miso@ (and react). -- -- * Keys are used to optimize child node list diffing. -- @@ -477,18 +477,18 @@ -- -- * Keys are used to compare two identical nodes. -- --- If two `VNode` are being compared (or two `VComp`) and their keys differ, the old node will be destroyed and a new one created. Otherwise, the underlying DOM node won't be removed, but its properties will be diffed. In the case of diffing two t'Component' (the 'VComp' case), if the keys differ, the 'unmount' phase will be triggered for the old 'VComp' and the 'mount' phase will be triggered for the new 'Component'. The underlying DOM reference will be replaced. +-- If two @VNode@ are being compared (or two @VComp@) and their keys differ, the old node will be destroyed and a new one created. Otherwise, the underlying DOM node won't be removed, but its properties will be diffed. In the case of diffing two t'Miso.Types.Component' (the 'VComp' case), if the keys differ, the 'unmount' phase will be triggered for the old 'VComp' and the 'mount' phase will be triggered for the new 'Miso.Types.Component'. The underlying DOM reference will be replaced. -- --- See the 'key_' property for usage (and smart constructors like 'textKey_' and ('+>') as well). +-- See the 'Miso.Property.key_' property for usage (and smart constructors like 'textKey_' and ('+>') as well). -- -- @ --- 'ul_' +-- ul_ -- [] --- [ 'li_' [ 'key_' "key-1" ] [ "a" ] --- , 'li_' [ 'key_' "key-2" ] [ "b" ] +-- [ li_ [ key_ "key-1" ] [ "a" ] +-- , li_ [ key_ "key-2" ] [ "b" ] -- , "key-3" '+>' counter --- , 'textKey' "key-4" "text here" --- , 'vfrag_' "key-5" [ "foo", "bar" ] +-- , textKey "key-4" "text here" +-- , vfrag_ "key-5" [ "foo", "bar" ] -- ] -- @ -- @@ -501,7 +501,7 @@ -- -- * Using events -- --- Miso exposes a 'defaultEvents' for convenience, these events are commonly used events and listened for on @\@. They get routed through the 'View' to the virtual DOM node that raised the event. Other 'Events' are exposed as conveniences (e.g. 'touchEvents'). All events required by all 'Component' must be combined together for use when running your application (e.g. @keyboardEvents <> touchEvents@). +-- Miso exposes a 'defaultEvents' for convenience, these events are commonly used events and listened for on @\@. They get routed through the 'View' to the virtual DOM node that raised the event. Other 'Events' are exposed as conveniences (e.g. 'touchEvents'). All events required by all 'Miso.Types.Component' must be combined together for use when running your application (e.g. @keyboardEvents <> touchEvents@). -- -- @ -- 'touchEvents' :: 'Events' @@ -518,29 +518,29 @@ -- Users can define their own event handlers using the 'Miso.Event.on' combinator. By default this will define an event in the 'Miso.Event.Types.BUBBLE' phase. See 'Miso.Event.onCapture' for handling events during the 'Miso.Event.Types.CAPTURE' phase. See the the module "Miso.Html.Event" for many predefined events. -- -- @ --- 'onChangeWith' :: ('MisoString' -> 'DOMRef' -> action) -> 'Attribute' action --- 'onChangeWith' = 'on' "change" 'valueDecoder' +-- onChangeWith :: (MisoString -> DOMRef -> action) -> Attribute action +-- onChangeWith = on "change" valueDecoder -- @ -- --- The @*with@ variant of events (e.g. 'Miso.Event.onChangeWith') provides the target 'DOMRef' in the callback function. +-- The @*with@ variant of events (e.g. 'Miso.Html.Event.onChangeWith') provides the target 'DOMRef' in the callback function. -- -- * Decoding events -- --- After an event has been raised, one can extract information from the event for use in their application. This is accomplished through a 'Decoder'. Many common decoders are available for use in "Miso.Event.Decoder". +-- After an event has been raised, one can extract information from the event for use in their application. This is accomplished through a t'Miso.Event.Decoder.Decoder'. Many common decoders are available for use in "Miso.Event.Decoder". -- -- @ --- data 'Decoder' a --- = 'Decoder' --- { 'decoder' :: 'Value' -> 'Parser' a --- , 'decodeAt' :: 'DecodeTarget' +-- data Decoder a +-- = Decoder +-- { decoder :: Value -> Parser a +-- , decodeAt :: DecodeTarget -- } -- --- -- | Example of a custom 'Decoder' for the @value@ property of an event target. --- 'valueDecoder' :: 'Decoder' 'MisoString' --- 'valueDecoder' = Decoder {..} +-- -- | Example of a custom Decoder for the @value@ property of an event target. +-- valueDecoder :: Decoder MisoString +-- valueDecoder = Decoder {..} -- where --- decodeAt = 'DecodeTarget' ["target"] --- decoder = 'withObject' "target" $ \\o -> o .: "value" +-- decodeAt = DecodeTarget ["target"] +-- decoder = withObject "target" $ \\o -> o .: "value" -- @ -- -- = Attributes / Properties @@ -550,7 +550,7 @@ -- (like 'Miso.Html.Property.className' and 'Miso.Html.Property.id_'). See "Miso.Property" and "Miso.Html.Property" for more information. -- -- @ --- 'div_' [ 'id_' "some-id", 'className' "some-class" ] [ ] +-- div_ [ id_ "some-id", className "some-class" ] [ ] -- @ -- -- = 'Effect' @@ -559,17 +559,17 @@ -- 'Effect' also allows 'IO' to be scheduled for evaluation by the @miso@ scheduler. -- -- Note: 'IO' is never evaluated inside of 'Effect', it is only scheduled. --- There is no 'MonadIO' instance for 'Effect'. +-- There is no 'Control.Monad.IO.Class.MonadIO' instance for 'Effect'. -- --- The 'Effect' type is defined as a 'RWS'. +-- The 'Effect' type is defined as a 'Control.Monad.RWS.RWS'. -- -- @ --- type 'Effect' parent props model action = 'RWS' ('ComponentInfo' parent props) ['Schedule' action] model () +-- type Effect parent props model action = RWS (ComponentInfo parent props) [Schedule action] model () -- @ -- --- * The 'Control.Monad.Reader' portion of 'Effect' is 'ComponentInfo'. 'ask', 'asks', 'Miso.Lens.view' can be used to access its fields. --- * The 'Control.Monad.Writer' portion of 'Effect' is used to schedule t'IO' actions. 'tell' can be used to create a 'Schedule' for an 'IO' action that is executed according to 'Synchronicity'. See also 'withSink' for usage. --- * The 'Control.Monad.State' portion of 'Effect' is used to manipulate the @model@. 'get', 'put', 'modify', and the 'Control.Monad.State.MonadState' lenses in t'Miso.Lens.Lens' can be used to modify the @model@. +-- * The 'Control.Monad.Reader' portion of 'Effect' is t'Miso.Effect.ComponentInfo'. 'ask', 'asks', 'Miso.Lens.view' can be used to access its fields. +-- * The 'Control.Monad.Writer' portion of 'Effect' is used to schedule t'IO' actions. 'tell' can be used to create a t'Miso.Effect.Schedule' for an 'IO' action that is executed according to 'Synchronicity'. See also 'withSink' for usage. +-- * The 'Control.Monad.State' portion of 'Effect' is used to manipulate the @model@. 'get', 'put', 'modify', and the 'Control.Monad.State.Class.MonadState' lenses in t'Miso.Lens.Lens' can be used to modify the @model@. -- -- 'IO' can be performed either synchronously or asynchronously. By default all 'IO' is asynchronous -- @@ -582,7 +582,7 @@ -- introduce 'IO' into the system. The @miso@ scheduler attaches exception -- handlers to all 'IO' actions. -- --- * For maximum flexibility, the 'MonadWriter' instance ('tell') can be used to schedule 'IO' (see the 'withSink' implementation). +-- * For maximum flexibility, the 'Control.Monad.Writer.Class.MonadWriter' instance ('tell') can be used to schedule 'IO' (see the 'withSink' implementation). -- -- == Synchronous 'IO' -- @@ -592,24 +592,24 @@ -- == 'Sink' -- -- @ --- type 'Sink' a => a -> 'IO' () +-- type Sink a => a -> IO () -- @ -- -- The 'Sink' function allows one to write any @action@ to the global event queue. See 'withSink' for more information. -- --- == Managing 'model' state. +-- == Managing @model@ state. -- --- Any 'MonadState' function is allowed for use when manipulating @model@, 'Miso.State.get', 'Miso.State.put', etc. See "Miso.State". +-- Any 'Control.Monad.State.Class.MonadState' function is allowed for use when manipulating @model@, 'Miso.State.get', 'Miso.State.put', etc. See "Miso.State". -- --- The 'MonadReader' instances allows the retrieval of 'ComponentInfo' within 'Effect'. --- 'ComponentInfo' provides the current 'ComponentId' the @parent@ 'ComponentId', and the 'DOMRef' ('_componentDOMRef') that the 'Component' is mounted on. +-- The 'Control.Monad.Reader.Class.MonadReader' instances allows the retrieval of t'Miso.Effect.ComponentInfo' within 'Effect'. +-- t'Miso.Effect.ComponentInfo' provides the current 'ComponentId' the @parent@ 'ComponentId', and the 'DOMRef' ('_componentDOMRef') that the 'Miso.Types.Component' is mounted on. -- --- = 'Component' communication +-- = 'Miso.Types.Component' communication -- -- == Asynchronous communication -- --- 'Component' are able to communicate asynchronously via a message-passing system. --- The miso runtime exposes a few primitives to allow t'Component' communication. +-- 'Miso.Types.Component' are able to communicate asynchronously via a message-passing system. +-- The miso runtime exposes a few primitives to allow t'Miso.Types.Component' communication. -- -- * 'broadcast' -- * 'mail' @@ -617,9 +617,9 @@ -- * 'mailChildren' -- * 'mailAncestors' -- --- All t'Component' have a 'mailbox' that can receive messages (as t'Miso.JSON.Value') from other t'Component'. --- This is meant to be used with the 'checkMail' function. The 'mail' function allows a 'Component' to send a specific message (as 'Value') to another t'Component' via its t'ComponentId'. --- The 'ComponentId' can be found in the 'Effect' monad. Using 'ask' will return a t'ComponentInfo'. The 'Component' receiving +-- All t'Miso.Types.Component' have a 'mailbox' that can receive messages (as t'Miso.JSON.Value') from other t'Miso.Types.Component'. +-- This is meant to be used with the 'checkMail' function. The 'mail' function allows a 'Miso.Types.Component' to send a specific message (as 'Value') to another t'Miso.Types.Component' via its t'ComponentId'. +-- The 'ComponentId' can be found in the 'Effect' monad. Using 'ask' will return a t'ComponentInfo'. The 'Miso.Types.Component' receiving -- the message will find it in its 'mailbox'. -- -- * "Miso.PubSub" @@ -630,18 +630,18 @@ -- -- * "Miso.Binding" -- --- Experimental support for data bindings (where 'Component' model can synchronize fields via a 'Miso.Lens.Lens' in response to model differences along the parent-child relationship). See the "Miso.Binding" module for more information, and the [miso-reactive](https://github.com/haskell-miso/miso-reactive) example. *Warning*: This is still considered experimental. +-- Experimental support for data bindings (where 'Miso.Types.Component' model can synchronize fields via a 'Miso.Lens.Lens' in response to model differences along the parent-child relationship). See the "Miso.Binding" module for more information, and the [miso-reactive](https://github.com/haskell-miso/miso-reactive) example. *Warning*: This is still considered experimental. -- -- == Parent access -- -- * 'parent' -- --- While not direct communication, a 'Component' can asynchronously receive read-only access to its @parent@ state via the 'parent' function. +-- While not direct communication, a 'Miso.Types.Component' can asynchronously receive read-only access to its @parent@ state via the 'parent' function. -- -- = Subscriptions -- --- A t'Sub' is any long-running operation that is external to a 'Component', but that can write --- to a 'Component' 'Sink'. 'Sub' come in two flavors, a dynamic 'Sub' (via 'startSub' / 'stopSub') and 'subs'. +-- A t'Sub' is any long-running operation that is external to a 'Miso.Types.Component', but that can write +-- to a 'Miso.Types.Component' 'Sink'. 'Sub' come in two flavors, a dynamic 'Sub' (via 'startSub' / 'stopSub') and 'subs'. -- -- * 'subs' -- @@ -650,17 +650,17 @@ -- main = 'startApp' 'defaultEvents' app { 'subs' = [ timerSub ] } -- -- timerSub :: 'Sub' Action --- timerSub sink = 'forever' $ ('threadDelay' 100000) >> sink Log +-- timerSub sink = 'Control.Monad.forever' $ ('Control.Concurrent.threadDelay' 100000) >> sink Log -- -- data Action = Log -- @ -- --- The 'subs' field of 'Component' contains 'Sub' that exist for the lifetime of that 'Component'. --- When a 'Component' unmounts, these 'Sub' will be stopped, and their resources finalized. +-- The 'subs' field of 'Miso.Types.Component' contains 'Sub' that exist for the lifetime of that 'Miso.Types.Component'. +-- When a 'Miso.Types.Component' unmounts, these 'Sub' will be stopped, and their resources finalized. -- -- @ --- 'onLineSub' :: (Bool -> action) -> 'Sub' action --- 'onLineSub' f sink = 'Miso.Subscription.Util.createSub' acquire release sink +-- onLineSub :: (Bool -> action) -> Sub action +-- onLineSub f sink = 'Miso.Subscription.Util.createSub' acquire release sink -- where -- release (cb1, cb2) = do -- FFI.windowRemoveEventListener "online" cb1 @@ -680,9 +680,9 @@ -- update = \\case -- StartTimer -> 'startSub' ("timer" :: MisoString) timerSub -- StopTimer -> 'stopSub' "timer" --- Log -> 'io_' ('consoleLog' "log") +-- Log -> 'io_' (consoleLog "log") -- where --- timerSub :: 'Sub' Action +-- timerSub :: Sub Action -- timerSub sink = 'Control.Monad.forever' $ ('Control.Concurrent.threadDelay' 100000) >> sink Log -- -- data Action = Log @@ -691,8 +691,8 @@ -- * 'Miso.Subscription.Util.createSub' -- -- 'Miso.Subscription.Util.createSub' is a helper function for creating a 'Sub' using the 'Control.Exception.bracket' pattern. --- This ensures that event listeners can be unregistered when a 'Component' unmounts. For example usage --- please see the "Miso.Subscription" sub modules. 'createSub' is only meant to be used in scenarios where +-- This ensures that event listeners can be unregistered when a 'Miso.Types.Component' unmounts. For example usage +-- please see the "Miso.Subscription" sub modules. 'Miso.Subscription.Util.createSub' is only meant to be used in scenarios where -- custom event listeners are required. -- -- = (2D/3D) Canvas support @@ -701,7 +701,7 @@ -- -- = 'Control.Monad.State.State' management -- --- A simple 'Miso.Lens.Lens' implementation is included with miso, this was done for convenience, to minimize dependencies, reduce payload size, and provide a simpler interface. See "Miso.Lens". This is a simple lens formulation that exposes many common 'MonadState' lenses (e.g. @'+='@) that work in the 'Effect' monad. "Miso.Lens" is not required for use, any lens library will also work with miso. +-- A simple 'Miso.Lens.Lens' implementation is included with miso, this was done for convenience, to minimize dependencies, reduce payload size, and provide a simpler interface. See "Miso.Lens". This is a simple lens formulation that exposes many common 'Control.Monad.State.Class.MonadState' lenses (e.g. 'Miso.Lens.+=') that work in the 'Effect' monad. "Miso.Lens" is not required for use, any lens library will also work with miso. -- -- = HTML -- @@ -747,10 +747,10 @@ -- miso includes its own string type named t'MisoString'. This is the preferred string type to use -- in order to maximize application performance. Since strings are ubiquitous in applications we -- want to minimize the copying of these strings between the JS and Haskell heaps. t'MisoString' accomplishes this. --- t'MisoString' is a synonym for t'JSString' when using the JS / WASM backends. When using vanilla GHC +-- t'MisoString' is a synonym for t'Miso.String.MisoString' when using the JS / WASM backends. When using vanilla GHC -- it is t'Data.Text'. See "Miso.String" for more information. -- --- For string conversions see the 'ms', 'fromMisoString' functions and 'ToMisoString' / 'FromMisoString' classes. +-- For string conversions see the 'ms', 'fromMisoString' functions and 'Miso.String.ToMisoString' / 'Miso.String.FromMisoString' classes. -- -- t'MisoString' is also used in the "Miso.Util.Lexer" and "Miso.Util.Parser" modules. -- @@ -767,7 +767,7 @@ -- = Development -- -- When developing miso applications interactively it is possible to append 'styles' and 'scripts' to the @\@ portion of --- the page when the 'Component' mounts. This is a convenience only meant to be used in development. We recommend guarding the usage behind a flag. +-- the page when the 'Miso.Types.Component' mounts. This is a convenience only meant to be used in development. We recommend guarding the usage behind a flag. -- -- @ -- main :: 'IO' () @@ -785,7 +785,7 @@ -- -- = Debugging -- --- Sometimes things can go wrong. Common errors like using `onClick` but not listening for the the 'click' event are common. +-- Sometimes things can go wrong. Common errors like using 'Miso.Html.Event.onClick' but not listening for the the 'click' event are common. -- These are errors that cannot be caught statically. These can be detected by enabling 'DebugAll'. Currently, debugging event delegation -- and page hydration is supported. -- @@ -798,8 +798,8 @@ -- -- = Internals -- --- Internally miso uses a global event queue and a scheduler to process all events raised by 'Component' throughout the lifetime of --- an application. Events are processed in FIFO order, batched by the 'Component' that raised them. +-- Internally miso uses a global event queue and a scheduler to process all events raised by 'Miso.Types.Component' throughout the lifetime of +-- an application. Events are processed in FIFO order, batched by the 'Miso.Types.Component' that raised them. -- -- = Prerendering -- @@ -820,7 +820,7 @@ -- -- @ -- main :: IO () --- main = 'prerender' 'defaultEvents' $ ('vcomp' () 'noop' $ \\() -> "hello world") { 'logLevel' = 'DebugPrerender' } +-- main = 'prerender' 'defaultEvents' $ ('vcomp' () 'noop' $ \\() -> "hello world") { 'logLevel' = 'Miso.Types.DebugPrerender' } -- @ -- -- Assuming the JS / WASM payload and @index.html@ are delivered together from the web server, the console should output below @@ -831,10 +831,10 @@ -- -- == Dynamic prerendering -- --- More advanced usage of prerendering entails sharing the @model@ between the server and client. In such scenarios the 'hydateModel' function should be specified inside the t'Component'. --- The SSR flag (`-fssr`) must be specified when using this feature. +-- More advanced usage of prerendering entails sharing the @model@ between the server and client. In such scenarios the 'Miso.Types.hydrateModel' function should be specified inside the t'Miso.Types.Component'. +-- The SSR flag (@-fssr@) must be specified when using this feature. -- --- 'hydrateModel' is used to load initial data into a Component's 'model' that is necessary for hydration. +-- 'Miso.Types.hydrateModel' is used to load initial data into a Component's @model@ that is necessary for hydration. -- ----------------------------------------------------------------------------- module Miso @@ -967,7 +967,7 @@ miso events f = do vcomp_ <- f <$> getURI initComponent events Hydrate vcomp_ { mountPoint = Nothing } ---------------------------------------------------------------------------- --- | Like 'miso', except discards the 'URI' argument. +-- | Like 'miso', except discards the t'Miso.Types.URI' argument. -- -- Use this function if you'd like to prerender, but not use navigation. -- @@ -980,7 +980,7 @@ prerender => Events -- ^ Globally delegated 'Events' -> App model action - -- ^ 'Component' application + -- ^ 'Miso.Types.Component' application -> IO () prerender events comp = initComponent events Hydrate comp { mountPoint = Nothing } ----------------------------------------------------------------------------- @@ -1001,7 +1001,7 @@ startApp => Events -- ^ Globally delegated 'Events' -> App model action - -- ^ 'Component' application + -- ^ 'Miso.Types.Component' application -> IO () startApp events = initComponent events Draw ----------------------------------------------------------------------------- @@ -1011,7 +1011,7 @@ startApp events = initComponent events Draw => Events -- ^ Globally delegated 'Events' -> (URI -> App model action) - -- ^ 'Component' application, with the current URI as an argument + -- ^ 'Miso.Types.Component' application, with the current URI as an argument -> IO () (🍜) = miso ---------------------------------------------------------------------------- @@ -1034,7 +1034,7 @@ renderApp -> MisoString -- ^ Name of the JS object that contains the drawing context -> App model action - -- ^ 'Component' application + -- ^ 'Miso.Types.Component' application -> IO () renderApp events renderer comp = do FFI.setDrawingContext renderer diff --git a/src/Miso/Binding.hs b/src/Miso/Binding.hs index 0dc9c964a..3aeb0aee4 100644 --- a/src/Miso/Binding.hs +++ b/src/Miso/Binding.hs @@ -19,11 +19,11 @@ -- -- miso includes an experimental feature that allows fields of different models to be synchronized -- against each another in response to model changes. See 'Miso.Binding'. Note this feature is --- experimental, it is recommended to use asynchronous Component communication (like 'broadcast') by default. +-- experimental, it is recommended to use asynchronous Component communication (like 'Miso.Runtime.broadcast') by default. -- -- This module exposes combinators to construct a 'Binding' which holds two lenses that will alter --- t'Component' model state along the parent-child relationship using a 'Lens'. Practically, this means when --- one t'Component' is marked as dirty, another t'Component' will also potentially will be marked as +-- t'Miso.Types.Component' model state along the parent-child relationship using a t'Miso.Lens.Lens'. Practically, this means when +-- one t'Miso.Types.Component' is marked as dirty, another t'Miso.Types.Component' will also potentially will be marked as -- dirty if they are connected along an edge ('Binding'). -- -- See the [miso-reactive](https://github.com/haskell-miso/miso-reactive) project for more information. @@ -70,7 +70,7 @@ data Binding parent child | forall field . Bidirectional Precedence (parent -> field) (field -> parent -> parent) (child -> field) (field -> child -> child) ----------------------------------------------------------------------------- -- | Data type used to express if the Child state should take precendence --- over the parent state during 'Component' mount. +-- over the parent state during t'Miso.Types.Component' 'Miso.Types.mount'. data Precedence = Child | Parent deriving (Eq, Show) ----------------------------------------------------------------------------- @@ -109,7 +109,7 @@ p <---> c = Bidirectional Parent (get_ p) (set_ p) (get_ c) (set_ c) get_ lens_ record = getConst (lens_ Const record) set_ lens_ field = runIdentity . lens_ (\_ -> Identity field) ----------------------------------------------------------------------------- --- | Like '<--->' but biases to inherit 'Parent' state on 'Component' 'mount'. +-- | Like '<--->' but biases to inherit 'Parent' state on t'Miso.Types.Component' 'Miso.Types.mount'. -- -- @since 1.10.0.0 (<--->>) @@ -121,7 +121,7 @@ l <--->> r = Bidirectional _ w x y z -> Bidirectional Parent w x y z _ -> error "impossible" ----------------------------------------------------------------------------- --- | Like '<--->' but biases to inherit 'Child' state on 'Component' 'mount'. +-- | Like '<--->' but biases to inherit 'Child' state on t'Miso.Types.Component' 'Miso.Types.mount'. -- -- @since 1.10.0.0 (<<--->) @@ -144,7 +144,7 @@ p ---> c = ParentToChild (get_ p) (set_ c) get_ lens_ record = getConst (lens_ Const record) set_ lens_ field = runIdentity . lens_ (\_ -> Identity field) ----------------------------------------------------------------------------- --- | Like '<-->' but biases to inherit 'Parent' state on 'Component' 'mount'. +-- | Like '<-->' but biases to inherit 'Parent' state on t'Miso.Types.Component' 'Miso.Types.mount'. -- -- @since 1.10.0.0 (<-->>) @@ -156,7 +156,7 @@ l <-->> r = Bidirectional _ w x y z -> Bidirectional Parent w x y z _ -> error "impossible" ----------------------------------------------------------------------------- --- | Like '<-->' but biases to inherit 'Child' state on 'Component' 'mount'. +-- | Like '<-->' but biases to inherit 'Child' state on t'Miso.Types.Component' 'Miso.Types.mount'. -- -- @since 1.10.0.0 (<<-->) diff --git a/src/Miso/DSL.hs b/src/Miso/DSL.hs index ccc821681..d186138e5 100644 --- a/src/Miso/DSL.hs +++ b/src/Miso/DSL.hs @@ -109,7 +109,10 @@ class ToJSVal a where gToJSVal (from x) o toJSVal o ----------------------------------------------------------------------------- +-- | Generic helper for marshaling record fields into a JavaScript t'Object'. +-- Used by the default 'ToJSVal' implementation; not normally called directly. class GToJSVal (f :: Type -> Type) where + -- | Write the fields of @f a@ into the given t'Object'. gToJSVal :: f a -> Object -> IO () ----------------------------------------------------------------------------- instance GToJSVal a => GToJSVal (D1 i a) where @@ -237,7 +240,10 @@ class FromJSVal a where Nothing -> error "fromJSValUnchecked: failure" Just y -> pure y ----------------------------------------------------------------------------- +-- | Generic helper for unmarshaling JavaScript t'Object' fields into Haskell record fields. +-- Used by the default 'FromJSVal' implementation; not normally called directly. class GFromJSVal (f :: Type -> Type) where + -- | Read the fields of @f a@ from the given t'Object'. gFromJSVal :: Object -> IO (Maybe (f a)) ----------------------------------------------------------------------------- instance GFromJSVal a => GFromJSVal (D1 i a) where @@ -492,6 +498,7 @@ infixr 2 # invokeFunction func o' args' {-# INLINABLE (#) #-} ----------------------------------------------------------------------------- +-- | Invoke a t'Function' with the given arguments and unmarshal the result. apply :: (FromJSVal a, ToArgs args) => Function -> args -> IO a apply (Function func) args = do o <- toJSVal global @@ -514,7 +521,7 @@ create = Object <$> create_ffi {-# INLINABLE create #-} ----------------------------------------------------------------------------- -- | Creates a new JS t'Object' populated with key-value pairs specified --- in the list. Meant for use with 'inline' JS functionality. +-- in the list. Meant for use with 'Miso.FFI.Internal.inline' JS functionality. -- -- @ -- update = \case diff --git a/src/Miso/Data/Array.hs b/src/Miso/Data/Array.hs index ab83dedb4..be78744d2 100644 --- a/src/Miso/Data/Array.hs +++ b/src/Miso/Data/Array.hs @@ -11,7 +11,7 @@ -- Stability : experimental -- Portability : non-portable -- --- Mutable 'Array' data structure in 'IO'. +-- Mutable t'Array' data structure in 'IO'. -- -- A JavaScript [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). This is a convenience for manipulating JavaScript data structures from Haskell. -- @@ -52,6 +52,8 @@ import qualified Miso.DSL as DSL import Miso.FFI (callFunction) import Miso.String (ms, unpack) ----------------------------------------------------------------------------- +-- | Typed wrapper around a JavaScript +-- [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). newtype Array value = Array JSVal deriving (FromJSVal, ToJSVal, ToObject) ----------------------------------------------------------------------------- -- | Constructs a new JS [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) in t'IO'. @@ -92,7 +94,7 @@ size (Array m) = DSL.fromJSValUnchecked =<< m ! "length" null :: Array value -> IO Bool null m = (== 0) <$> size m ----------------------------------------------------------------------------- --- | Checks existence of 'value' in t'Array', returns t'Bool. +-- | Checks existence of @value@ in t'Array', returns t'Bool'. member :: ToJSVal value => value -> Array value -> IO Bool member value (Array m) = DSL.fromJSValUnchecked =<< callFunction m "includes" =<< DSL.toJSVal value ----------------------------------------------------------------------------- diff --git a/src/Miso/Data/Map.hs b/src/Miso/Data/Map.hs index 54c1dea9d..3acec513e 100644 --- a/src/Miso/Data/Map.hs +++ b/src/Miso/Data/Map.hs @@ -10,7 +10,7 @@ -- Stability : experimental -- Portability : non-portable -- --- Mutable 'Map' data structure in 'IO'. +-- Mutable t'Map' data structure in 'IO'. -- -- A JavaScript [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). This is a convenience for manipulating JavaScript data structures from Haskell. -- @@ -41,6 +41,8 @@ import Miso.DSL (jsg, JSVal, ToJSVal, FromJSVal, (!)) import qualified Miso.DSL as DSL import Miso.FFI (callFunction) ----------------------------------------------------------------------------- +-- | Typed wrapper around a JavaScript +-- [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). newtype Map key value = Map JSVal deriving (FromJSVal, ToJSVal) ----------------------------------------------------------------------------- -- | Constructs a new JS [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) in t'IO'. @@ -66,7 +68,7 @@ clear (Map m) = void (callFunction m "clear" ()) size :: Map key value -> IO Int size (Map m) = DSL.fromJSValUnchecked =<< m ! "size" ----------------------------------------------------------------------------- --- | Checks existence of a value by 'key', returns t'Bool. +-- | Checks existence of a value by @key@, returns t'Bool'. has :: ToJSVal key => key -> Map key value -> IO Bool has key (Map m) = DSL.fromJSValUnchecked =<< callFunction m "has" =<< DSL.toJSVal key ----------------------------------------------------------------------------- diff --git a/src/Miso/Data/Set.hs b/src/Miso/Data/Set.hs index dbf81347b..5b49a5d3e 100644 --- a/src/Miso/Data/Set.hs +++ b/src/Miso/Data/Set.hs @@ -10,7 +10,7 @@ -- Stability : experimental -- Portability : non-portable -- --- Mutable 'Set' data structure in 'IO'. +-- Mutable t'Set' data structure in 'IO'. -- -- A JavaScript [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). This is a convenience for manipulating JavaScript data structures from Haskell. -- @@ -46,6 +46,8 @@ import Miso.DSL (jsg, JSVal, ToJSVal, FromJSVal, (!)) import qualified Miso.DSL as DSL import Miso.FFI (callFunction) ----------------------------------------------------------------------------- +-- | Typed wrapper around a JavaScript +-- [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). newtype Set key = Set JSVal deriving (FromJSVal, ToJSVal) ----------------------------------------------------------------------------- -- | Constructs a new JS [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) in t'IO'. @@ -67,7 +69,7 @@ clear (Set m) = void (callFunction m "clear" ()) size :: Set key -> IO Int size (Set m) = DSL.fromJSValUnchecked =<< m ! "size" ----------------------------------------------------------------------------- --- | Checks existence of 'key' in t'Set', returns t'Bool. +-- | Checks existence of @key@ in t'Set', returns t'Bool'. member :: ToJSVal key => key -> Set key -> IO Bool member key (Set m) = DSL.fromJSValUnchecked =<< callFunction m "has" =<< DSL.toJSVal key ----------------------------------------------------------------------------- diff --git a/src/Miso/Date.hs b/src/Miso/Date.hs index 32d350a5c..8bd8f9e82 100644 --- a/src/Miso/Date.hs +++ b/src/Miso/Date.hs @@ -10,7 +10,7 @@ -- Stability : experimental -- Portability : non-portable -- --- Mutable 'Date' data structure in 'IO'. +-- Mutable t'Date' data structure in 'IO'. -- -- A JavaScript [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date). This is a convenience for manipulating JavaScript data structures from Haskell. -- @@ -79,6 +79,8 @@ import qualified Miso.DSL as DSL import Miso.FFI (callFunction) import Miso.String (MisoString) ----------------------------------------------------------------------------- +-- | Typed wrapper around a JavaScript +-- [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date). newtype Date = Date JSVal deriving (FromJSVal, ToJSVal, ToObject, Eq) ----------------------------------------------------------------------------- -- | Constructs a new JS [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) in t'IO'. diff --git a/src/Miso/Effect.hs b/src/Miso/Effect.hs index 1eabdd8fe..17c859ba1 100644 --- a/src/Miso/Effect.hs +++ b/src/Miso/Effect.hs @@ -80,8 +80,8 @@ mkComponentInfo = ComponentInfo ----------------------------------------------------------------------------- -- | This is the 'Reader r' in t'Miso.Effect'. Accessible via 'Control.Monad.Reader.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. +-- 'Miso.Runtime.parent' function. It gives access to t'Miso.Types.Component' metadata such as the 'DOMRef' the +-- t'Miso.Types.Component' was mounted on and the 'ComponentId' associated with it. data ComponentInfo parent props = ComponentInfo { _componentInfoId :: ComponentId @@ -218,12 +218,12 @@ batch_ actions = sequence_ ----------------------------------------------------------------------------- -- | A monad for succinctly expressing model transitions in the @update@ function. -- --- t'Effect' is a @RWS@, where the @State@ allows modification to 'model'. +-- t'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 'Control.Monad.Writer.Class.tell' -- from the @mtl@ library and a single asynchronous action can be scheduled using 'io_'. -- --- An t'Effect' represents the results of an 'update' action. +-- An t'Effect' represents the results of an @update@ action. -- -- It consists of the updated model and a list of subscriptions. Each t'Sub' is -- run in a new thread so there is no risk of accidentally blocking the @@ -243,8 +243,8 @@ batch_ actions = sequence_ -- MyAction2 -> do -- field2 '%=' f -- 'io_' $ do --- 'consoleLog' \"Hello\" --- 'consoleLog' \"World!\" +-- 'Miso.FFI.consoleLog' \"Hello\" +-- 'Miso.FFI.consoleLog' \"World!\" -- , ... -- } -- @ @@ -385,7 +385,7 @@ modifyAllIO f = censor $ \actions -> -- to turn events into actions. -- -- @ --- 'update' FetchJSON = 'withSink' $ \\sink -> getJSON (sink . ReceivedJSON) (sink . HandleError) +-- update FetchJSON = 'withSink' $ \\sink -> getJSON (sink . ReceivedJSON) (sink . HandleError) -- @ -- -- @since 1.9.0.0 @@ -401,8 +401,8 @@ withSink f = tell [ async f ] -- data Action = HelloWorld -- type Model = Int -- --- 'update' :: Action -> 'Effect' parent Model Action --- 'update' = \\case +-- update :: Action -> 'Effect' parent Model Action +-- update = \\case -- Click -> 'issue' HelloWorld -- @ -- diff --git a/src/Miso/FFI/Internal.hs b/src/Miso/FFI/Internal.hs index 7cd1e9411..39b5fef30 100644 --- a/src/Miso/FFI/Internal.hs +++ b/src/Miso/FFI/Internal.hs @@ -371,7 +371,7 @@ updateRef jsval1 jsval2 = do -- logNameGetAge :: Person -> IO Int -- logNameGetAge = inline -- """ --- console.log('name', name); +-- console.log("name", name); -- return age; -- """ -- @@ -604,7 +604,7 @@ addScript useModule js_ = do void $ context # "appendChild" $ (head_, script) pure script ----------------------------------------------------------------------------- --- | Sets the @.value@ property on a 'DOMRef'. +-- | Sets the @.value@ property on a t'Miso.Effect.DOMRef'. -- -- Useful for resetting the @value@ property on an input element. -- @@ -1128,7 +1128,7 @@ newCustomEvent :: ToArgs args => args -> IO Event {-# INLINABLE newCustomEvent #-} newCustomEvent args = Event <$> new (jsg "CustomEvent") args ----------------------------------------------------------------------------- --- | Uses the 'splitmix' function to generate a PRNG. +-- | Uses the @splitmix@ function to generate a PRNG. -- splitmix32 :: Double -> IO JSVal {-# INLINABLE splitmix32 #-} diff --git a/src/Miso/FFI/QQ.hs b/src/Miso/FFI/QQ.hs index 5238e9380..31a2f9076 100644 --- a/src/Miso/FFI/QQ.hs +++ b/src/Miso/FFI/QQ.hs @@ -89,7 +89,7 @@ extQ f g a = maybe (f a) g (cast a) withString :: (Quote m, Typeable a) => a -> Maybe (m Exp) withString a = liftString <$> cast a ---------------------------------------------------------------------------- --- | Use `isPrefixOf` as you traverse the string in lex order and do a replace +-- | Use 'Data.List.isPrefixOf' as you traverse the string in lex order and do a replace formatVars :: MisoString -> [(MisoString, MisoString)] -> MisoString formatVars s [] = s formatVars s table@((var,key):xs) = diff --git a/src/Miso/JSON.hs b/src/Miso/JSON.hs index 4a4dc9078..f385e06be 100644 --- a/src/Miso/JSON.hs +++ b/src/Miso/JSON.hs @@ -36,6 +36,7 @@ module Miso.JSON , Object , Pair , Result (..) + , IntMap -- ** Constructors , (.=) , object @@ -108,6 +109,8 @@ import GHC.Natural (Natural) import Data.Char import qualified Data.Map.Strict as M import Data.Map.Strict (Map) +import qualified Data.IntMap.Strict as IM +import Data.IntMap.Strict (IntMap) import Data.Int import GHC.Natural (naturalToInteger, naturalFromInteger) import GHC.TypeLits @@ -133,6 +136,12 @@ import System.IO.Unsafe (unsafePerformIO) ---------------------------------------------------------------------------- infixr 8 .= +-- | Construct a JSON 'Pair' from a key and any 'ToJSON' value. +-- Commonly used with 'object': +-- +-- @ +-- object [ "name" .= ("Alice" :: MisoString), "age" .= (30 :: Int) ] +-- @ (.=) :: ToJSON v => MisoString -> v -> Pair k .= v = (k, toJSON v) ---------------------------------------------------------------------------- @@ -140,7 +149,7 @@ k .= v = (k, toJSON v) object :: [Pair] -> Value object = Object . M.fromList ---------------------------------------------------------------------------- --- | The empty JSON 'Object' (i.e. @{}@). +-- | The empty JSON t'Object' (i.e. @{}@). emptyObject :: Value emptyObject = Object mempty ---------------------------------------------------------------------------- @@ -148,26 +157,43 @@ emptyObject = Object mempty emptyArray :: Value emptyArray = Array mempty ---------------------------------------------------------------------------- +-- | Look up a required key in a JSON t'Object' and decode its value. +-- Fails with a parse error if the key is absent. (.:) :: FromJSON a => Object -> MisoString -> Parser a m .: k = maybe (pfail ("Key not found: " <> k)) parseJSON (M.lookup k m) ---------------------------------------------------------------------------- +-- | Look up an optional key in a JSON t'Object'. Returns 'Nothing' +-- when the key is absent rather than failing. (.:?) :: FromJSON a => Object -> MisoString -> Parser (Maybe a) m .:? k = maybe (pure Nothing) parseJSON (M.lookup k m) ---------------------------------------------------------------------------- +-- | Like '(.:?)', but fails if the key is present and its value cannot be +-- decoded — unlike '(.:?)' which returns 'Nothing' for absent keys only. (.:!) :: FromJSON a => Object -> MisoString -> Parser (Maybe a) m .:! k = maybe (pure Nothing) (fmap Just . parseJSON) (M.lookup k m) ---------------------------------------------------------------------------- +-- | Provide a default value for an optional field parser. If the +-- t'Parser' produces 'Nothing', the default is used instead. (.!=) :: Parser (Maybe a) -> a -> Parser a mv .!= def = fmap (maybe def id) mv ---------------------------------------------------------------------------- +-- | Class for types that can be encoded as a JSON 'Value'. +-- A default instance is provided for 'Generic' types via 'genericToJSON'. class ToJSON a where + -- | Encode a value as a JSON 'Value'. toJSON :: a -> Value default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value toJSON = genericToJSON defaultOptions ---------------------------------------------------------------------------- +-- | Generic top-level JSON encoder. Encoding rules match @aeson@'s defaults; +-- see t'Options' and 'defaultOptions' for customisation. genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value genericToJSON opts = gToJSON opts . from ---------------------------------------------------------------------------- +-- | Options that control generic JSON encoding and decoding. +-- Construct with 'defaultOptions' and override individual fields as needed. +-- +-- @since 1.9.0.0 data Options = Options { fieldLabelModifier :: String -> String @@ -177,7 +203,7 @@ data Options , allNullaryToStringTag :: Bool -- ^ When 'True' (the default, matching aeson) and every constructor of a -- sum type is nullary, encode/decode each constructor as a bare JSON - -- 'String' (e.g. @\"Red\"@) rather than a tagged object + -- t'String' (e.g. @\"Red\"@) rather than a tagged object -- (e.g. @{\"tag\":\"Red\"}@). , omitNothingFields :: Bool -- ^ When 'True', record fields whose value is 'Nothing' are omitted from @@ -185,6 +211,9 @@ data Options -- they are encoded as @null@. } ---------------------------------------------------------------------------- +-- | Default t'Options': identity field/constructor modifiers, +-- @allNullaryToStringTag@ enabled, @omitNothingFields@ disabled. +-- Matches @aeson@'s @defaultOptions@. defaultOptions :: Options defaultOptions = Options { fieldLabelModifier = \x -> x @@ -193,6 +222,12 @@ defaultOptions = Options , omitNothingFields = False } ---------------------------------------------------------------------------- +-- | Convert a camelCase identifier to a lower-case separated string. +-- The separator character is given as the first argument. +-- +-- @ +-- camelTo2 '_' "myFieldName" -- "my_field_name" +-- @ camelTo2 :: Char -> String -> String camelTo2 c = Prelude.map toLower . go2 . go1 where go1 "" = "" @@ -219,6 +254,7 @@ combineFields _ _ = PositionalFields [] -- mixed; shouldn't occur in valid GHC ---------------------------------------------------------------------------- -- | Collect a constructor's fields into 'Fields'. class GToFields (f :: Type -> Type) where + -- | Collect the fields of a constructor into a 'Fields' value. gToFields :: Options -> f a -> Fields ---------------------------------------------------------------------------- instance GToFields U1 where @@ -259,6 +295,7 @@ instance {-# OVERLAPPING #-} (Selector m, ToJSON a) -- | Determine at the type level whether every constructor of a sum type -- is nullary (has no fields). Used to implement 'allNullaryToStringTag'. class GAllNullary (f :: Type -> Type) where + -- | Returns 'True' if all constructors in @f@ are nullary. gAllNullary :: Bool ---------------------------------------------------------------------------- instance GAllNullary U1 where @@ -316,6 +353,7 @@ encodeTaggedCon tag = \case -- * Sum nullary constructor: @{\"tag\": \"C\"}@ -- * Sum positional constructor: @{\"tag\": \"C\", \"contents\": v}@ or @[...]@ class GToJSON (f :: Type -> Type) where + -- | Encode a generic representation into a JSON 'Value'. gToJSON :: Options -> f a -> Value ---------------------------------------------------------------------------- instance GToJSONRep f => GToJSON (D1 m f) where @@ -336,8 +374,9 @@ instance (GToJSONSum f, GToJSONSum g, GToJSONSumNullary f, GToJSONSumNullary g, | otherwise = gToJSONSum opts x ---------------------------------------------------------------------------- --- | Encode all-nullary sum constructors as bare 'String' values. +-- | Encode all-nullary sum constructors as bare t'String' values. class GToJSONSumNullary (f :: Type -> Type) where + -- | Encode a nullary sum constructor as a bare JSON t'String'. gToJSONSumNullary :: Options -> f a -> Value ---------------------------------------------------------------------------- instance (GToJSONSumNullary f, GToJSONSumNullary g) => GToJSONSumNullary (f :+: g) where @@ -355,6 +394,7 @@ instance {-# OVERLAPPABLE #-} Constructor m => GToJSONSumNullary (C1 m f) where ---------------------------------------------------------------------------- -- | Encode sum constructors with a @\"tag\"@ key. class GToJSONSum (f :: Type -> Type) where + -- | Encode a sum constructor as a tagged JSON object. gToJSONSum :: Options -> f a -> Value ---------------------------------------------------------------------------- instance (GToJSONSum f, GToJSONSum g) => GToJSONSum (f :+: g) where @@ -388,6 +428,9 @@ instance {-# OVERLAPPABLE #-} ToJSON a => ToJSON [a] where instance ToJSON v => ToJSON (M.Map MisoString v) where toJSON = Object . M.map toJSON ---------------------------------------------------------------------------- +instance ToJSON v => ToJSON (IntMap v) where + toJSON = toJSON . IM.toList +---------------------------------------------------------------------------- instance ToJSON a => ToJSON (Maybe a) where toJSON = \case Nothing -> Null @@ -441,8 +484,13 @@ instance ToJSON Integer where toJSON = Number . fromInteger -- | Possibly lossy due to conversion to 'Double' instance ToJSON Natural where toJSON = Number . fromInteger . naturalToInteger ---------------------------------------------------------------------------- -newtype Parser a = Parser { unParser :: Either MisoString a } - deriving (Functor, Applicative, Monad) +-- | A lightweight JSON parser that returns either a 'MisoString' error or a +-- decoded value @a@. Build parsers with 'parseJSON', 'withObject', +-- 'withText', etc., and run them with 'parseMaybe' or 'parseEither'. +newtype Parser a = Parser + { unParser :: Either MisoString a + -- ^ The underlying result: 'Left' is a parse error, 'Right' is success. + } deriving (Functor, Applicative, Monad) ---------------------------------------------------------------------------- instance MonadFail Parser where fail = pfail . pack @@ -454,19 +502,26 @@ instance Alternative Parser where ---------------------------------------------------------------------------- instance MonadPlus Parser ---------------------------------------------------------------------------- +-- | Run a t'Parser' function on a value, returning 'Nothing' on failure. parseMaybe :: (a -> Parser b) -> a -> Maybe b parseMaybe m v = case parseEither m v of Left _ -> Nothing - Right r -> Just r + Right r -> Just r ---------------------------------------------------------------------------- +-- | Run a t'Parser' function on a value, returning 'Left' with the error +-- message on failure. parseEither :: (a -> Parser b) -> a -> Either MisoString b parseEither m v = unParser (m v) ---------------------------------------------------------------------------- pfail :: MisoString -> Parser a pfail message = Parser (Left message) ---------------------------------------------------------------------------- +-- | Class for types that can be decoded from a JSON 'Value'. +-- A default instance is provided for 'Generic' types via 'genericParseJSON'. class FromJSON a where + -- | Decode a JSON 'Value' into @a@, failing with a t'Parser' error on + -- type mismatch or missing fields. parseJSON :: Value -> Parser a default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a parseJSON = genericParseJSON defaultOptions @@ -475,8 +530,11 @@ class FromJSON a where -- -- Decoding rules match aeson's defaults (see 'Options' and 'defaultOptions'). class GFromJSON (f :: Type -> Type) where + -- | Decode a JSON 'Value' into a generic representation. gParseJSON :: Options -> Value -> Parser (f a) ---------------------------------------------------------------------------- +-- | Generic top-level JSON decoder. Decoding rules match @aeson@'s defaults; +-- see t'Options' and 'defaultOptions' for customisation. genericParseJSON :: (Generic a, GFromJSON (Rep a)) => Options -> Value -> Parser a genericParseJSON opts value = to <$> gParseJSON opts value ---------------------------------------------------------------------------- @@ -498,8 +556,9 @@ instance (GFromJSONSum f, GFromJSONSum g, GFromJSONSumNullary f, GFromJSONSumNul | otherwise = gFromJSONSum opts v ---------------------------------------------------------------------------- --- | Parse all-nullary sum constructors from bare 'String' values. +-- | Parse all-nullary sum constructors from bare t'String' values. class GFromJSONSumNullary (f :: Type -> Type) where + -- | Attempt to decode a nullary sum constructor from a bare JSON t'String'. gFromJSONSumNullary :: Options -> Value -> Parser (f a) ---------------------------------------------------------------------------- instance (GFromJSONSumNullary f, GFromJSONSumNullary g) => GFromJSONSumNullary (f :+: g) where @@ -521,6 +580,7 @@ instance {-# OVERLAPPABLE #-} Constructor m => GFromJSONSumNullary (C1 m f) wher ---------------------------------------------------------------------------- -- | Parse sum constructors, trying each branch left-to-right. class GFromJSONSum (f :: Type -> Type) where + -- | Attempt to decode a tagged JSON object as a sum constructor. gFromJSONSum :: Options -> Value -> Parser (f a) ---------------------------------------------------------------------------- instance (GFromJSONSum f, GFromJSONSum g) => GFromJSONSum (f :+: g) where @@ -564,14 +624,14 @@ parseTaggedCon tag opts = \case _ -> pfail ("expected JSON object for constructor " <> ms (show tag)) ---------------------------------------------------------------------------- -- | Field-level decoder. Knows whether the constructor is a record and --- how many fields it has; can decode from a JSON 'Object' (record mode) +-- how many fields it has; can decode from a JSON t'Object' (record mode) -- or a positional '[Value]' list. class GFromFields (f :: Type -> Type) where -- | Is this a record constructor (all selectors have names)? gIsRecord :: Bool -- | Number of fields. gFieldCount :: Int - -- | Decode from a JSON 'Object' (record mode: look up by field name). + -- | Decode from a JSON t'Object' (record mode: look up by field name). gFromRecord :: Options -> Object -> Parser (f a) -- | Decode from a positional list of 'Value'. gFromPositional :: Options -> [Value] -> Parser (f a) @@ -750,26 +810,42 @@ instance FromJSON Char where instance FromJSON v => FromJSON (Map MisoString v) where parseJSON = withObject "FromJSON v => Map MisoString v" $ mapM parseJSON ---------------------------------------------------------------------------- +instance FromJSON v => FromJSON (IntMap v) where + parseJSON = fmap IM.fromList . parseJSON +---------------------------------------------------------------------------- +-- | Expect a JSON @Bool@ value and pass it to the continuation. +-- Fails with a 'typeMismatch' error for any other 'Value'. withBool :: MisoString -> (Bool -> Parser a) -> Value -> Parser a withBool _ f (Bool arr) = f arr withBool expected _ v = typeMismatch expected v ---------------------------------------------------------------------------- +-- | Expect a JSON @String@ value and pass it to the continuation. +-- Fails with a 'typeMismatch' error for any other 'Value'. withText :: MisoString -> (MisoString -> Parser a) -> Value -> Parser a withText _ f (String txt) = f txt withText expected _ v = typeMismatch expected v ---------------------------------------------------------------------------- +-- | Expect a JSON 'Array' value and pass its element list to the continuation. +-- Fails with a 'typeMismatch' error for any other 'Value'. withArray :: MisoString -> ([Value] -> Parser a) -> Value -> Parser a withArray _ f (Array lst) = f lst withArray expected _ v = typeMismatch expected v ---------------------------------------------------------------------------- +-- | Expect a JSON t'Object' value and pass it to the continuation. +-- Fails with a 'typeMismatch' error for any other 'Value'. withObject :: MisoString -> (Object -> Parser a) -> Value -> Parser a withObject _ f (Object obj) = f obj withObject expected _ v = typeMismatch expected v ---------------------------------------------------------------------------- +-- | Expect a JSON 'Number' value and pass it to the continuation. +-- Fails with a 'typeMismatch' error for any other 'Value'. withNumber :: MisoString -> (Double -> Parser a) -> Value -> Parser a withNumber _ f (Number n) = f n withNumber expected _ v = typeMismatch expected v ---------------------------------------------------------------------------- +-- | Produce a t'Parser' failure describing an unexpected JSON constructor. +-- Used internally by the @with*@ combinators; can also be called from +-- custom 'FromJSON' instances. typeMismatch :: MisoString -> Value -> Parser a typeMismatch expected actual = pfail @@ -782,6 +858,9 @@ typeMismatch expected actual = Null -> "Null" ) ---------------------------------------------------------------------------- +-- | Encode a value as a JSON 'MisoString'. On JS/WASM backends this +-- delegates to @JSON.stringify()@ for maximum performance; on a plain +-- GHC build it falls back to 'encodePure'. #ifdef VANILLA encode :: ToJSON a => a -> MisoString encode = encodePure @@ -841,6 +920,9 @@ instance ToMisoString Value where MS.intercalate "," [ "\"" <> escapeJSONString k <> "\"" <> ":" <> ms v | (k,v) <- M.toList o ] <> "}" ---------------------------------------------------------------------------- +-- | Decode a JSON 'MisoString' into @a@, returning 'Nothing' on failure. +-- On JS/WASM backends this uses @JSON.parse()@; on plain GHC it uses +-- 'Miso.JSON.Parser.decodePure'. #ifdef VANILLA decode :: FromJSON a => MisoString -> Maybe a decode s @@ -872,27 +954,36 @@ foreign import javascript unsafe encodePretty_ffi :: JSVal -> Int -> IO MisoString #endif ----------------------------------------------------------------------------- +-- | Pretty-print a JSON value using the given t'Config' (indentation width). +-- On plain GHC this raises an error; use the JS/WASM backend for real output. #ifdef VANILLA encodePretty' :: ToJSON a => Config -> a -> MisoString encodePretty' = error "encodePretty': not implemented" ----------------------------------------------------------------------------- +-- | Pretty-print a JSON value with the 'defConfig' (4-space indentation). +-- On plain GHC this raises an error; use the JS/WASM backend for real output. encodePretty :: ToJSON a => a -> MisoString encodePretty _ = error "encodePretty: not implemented" ----------------------------------------------------------------------------- #else ----------------------------------------------------------------------------- +-- | Pretty-print a JSON value using the given t'Config' (indentation width). encodePretty' :: ToJSON a => Config -> a -> MisoString encodePretty' (Config s) x = unsafePerformIO (flip encodePretty_ffi s =<< toJSVal_Value (toJSON x)) ----------------------------------------------------------------------------- +-- | Pretty-print a JSON value with the 'defConfig' (4-space indentation). encodePretty :: ToJSON a => a -> MisoString encodePretty = encodePretty' defConfig #endif ----------------------------------------------------------------------------- +-- | Configuration for 'encodePretty' \/ 'encodePretty\''. newtype Config = Config { spaces :: Int + -- ^ Number of spaces per indentation level. } deriving (Show, Eq) ----------------------------------------------------------------------------- +-- | Default t'Config': 4-space indentation. defConfig :: Config defConfig = Config 4 ----------------------------------------------------------------------------- @@ -914,6 +1005,8 @@ foreign import javascript unsafe jsonStringify :: JSVal -> IO MisoString #endif ----------------------------------------------------------------------------- +-- | Serialise a t'JSVal' to a JSON 'MisoString' via @JSON.stringify()@. +-- Not available on plain GHC builds. #ifdef VANILLA jsonStringify :: JSVal -> IO MisoString jsonStringify _ = error "jsonStringify: not implemented" @@ -937,11 +1030,15 @@ foreign import javascript unsafe jsonParse :: MisoString -> IO JSVal #endif ----------------------------------------------------------------------------- +-- | Parse a JSON 'MisoString' into a t'JSVal' via @JSON.parse()@. +-- Not available on plain GHC builds. #ifdef VANILLA jsonParse :: MisoString -> IO JSVal jsonParse _ = error "jsonParse: not implemented" #endif ----------------------------------------------------------------------------- +-- | Decode a JSON 'MisoString' into @a@, returning 'Left' with a +-- descriptive error on failure. #ifdef VANILLA eitherDecode :: FromJSON a => MisoString -> Either MisoString a eitherDecode string = @@ -962,6 +1059,7 @@ eitherDecode string = unsafePerformIO $ do Error err -> Left err) #endif ---------------------------------------------------------------------------- +-- | Convert a 'Value' to @a@, returning a 'Result'. fromJSON :: FromJSON a => Value -> Result a fromJSON value = case parseEither parseJSON value of @@ -1043,11 +1141,14 @@ fromJSVal_Value jsval = do toObject = Object . M.fromList #endif ----------------------------------------------------------------------------- +-- | Unmarshal a t'JSVal' back into a 'Value', returning 'Nothing' when the +-- JavaScript value cannot be represented as a JSON 'Value'. #ifdef VANILLA ----------------------------------------------------------------------------- fromJSVal_Value :: JSVal -> IO (Maybe Value) fromJSVal_Value = error "fromJSVal_Value: not implemented" ----------------------------------------------------------------------------- +-- | Marshal a 'Value' into a t'JSVal' for use with the JavaScript FFI. toJSVal_Value :: Value -> IO JSVal toJSVal_Value = error "toJSVal_Value: not implemented" ----------------------------------------------------------------------------- diff --git a/src/Miso/JSON/Lexer.hs b/src/Miso/JSON/Lexer.hs index 65c0cec91..3d1ce23b1 100644 --- a/src/Miso/JSON/Lexer.hs +++ b/src/Miso/JSON/Lexer.hs @@ -35,12 +35,18 @@ import Miso.Util.Lexer hiding (string', token) import Control.Applicative (liftA2) #endif ---------------------------------------------------------------------------- +-- | A single lexical token produced by the JSON lexer. data Token = TokenPunctuator Char + -- ^ A JSON structural character: @[@ @]@ @{@ @}@ @,@ @:@ | TokenNumber Double + -- ^ A JSON number literal. | TokenBool Bool + -- ^ A JSON boolean literal (@true@ or @false@). | TokenString MisoString + -- ^ A JSON string literal (unescaped). | TokenNull + -- ^ The JSON @null@ literal. deriving (Eq, Show) ---------------------------------------------------------------------------- number :: Lexer Double @@ -116,6 +122,7 @@ token = oneOf , TokenNull <$ null ] ---------------------------------------------------------------------------- +-- | Lex a JSON input stream into a list of t'Token's, skipping whitespace. tokens :: Lexer [Token] tokens = some (many whitespace *> token) ---------------------------------------------------------------------------- diff --git a/src/Miso/JSON/Parser.hs b/src/Miso/JSON/Parser.hs index 27f16a9c6..c7eeb0ccf 100644 --- a/src/Miso/JSON/Parser.hs +++ b/src/Miso/JSON/Parser.hs @@ -75,6 +75,8 @@ value = oneOf , Null <$ null ] ---------------------------------------------------------------------------- +-- | Purely decode a JSON 'MisoString' into a 'Value', returning a 'Left' +-- error string on lexical or parse failure. Used on the server-side (SSR). decodePure :: MisoString -> Either String Value decodePure = first show . either (Left . LexicalError) (parse value . fst) diff --git a/src/Miso/JSON/Types.hs b/src/Miso/JSON/Types.hs index 9f2e04a29..b810e721d 100644 --- a/src/Miso/JSON/Types.hs +++ b/src/Miso/JSON/Types.hs @@ -34,25 +34,30 @@ import Prelude hiding (fail) import Control.Monad.Fail (MonadFail (..)) #endif ---------------------------------------------------------------------------- +-- | A JSON value, as described in [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259). data Value - = Number Double - | Bool Bool - | String MisoString - | Array [Value] - | Object (Map MisoString Value) - | Null + = Number Double -- ^ A JSON number. + | Bool Bool -- ^ A JSON boolean. + | String MisoString -- ^ A JSON string. + | Array [Value] -- ^ A JSON array. + | Object (Map MisoString Value) -- ^ A JSON object (key-value map). + | Null -- ^ The JSON null value. deriving (Show, Eq) ---------------------------------------------------------------------------- instance IsString Value where fromString = String . fromString ---------------------------------------------------------------------------- +-- | A key-value pair used in JSON objects. type Pair = (MisoString, Value) ---------------------------------------------------------------------------- +-- | A JSON object: a map from 'MisoString' keys to 'Value'. type Object = Map MisoString Value ---------------------------------------------------------------------------- +-- | The result of a JSON parse attempt: either 'Success' with a value or +-- an 'Error' with a message. data Result a - = Success a - | Error MisoString + = Success a -- ^ Successful parse result. + | Error MisoString -- ^ Parse failure with an error message. deriving (Show, Eq) ---------------------------------------------------------------------------- instance Functor Result where diff --git a/src/Miso/Lens.hs b/src/Miso/Lens.hs index 09e67bba1..6db4782aa 100644 --- a/src/Miso/Lens.hs +++ b/src/Miso/Lens.hs @@ -787,37 +787,50 @@ lens -> Lens record field lens getter setter = Lens getter (flip setter) ---------------------------------------------------------------------------- +-- | A t'Prism' is an optic for sum types, pairing a constructor (@_up@) with +-- a safe destructor (@_down@). data Prism s a = Prism { _up :: a -> s + -- ^ Inject @a@ into @s@. , _down :: s -> Maybe a + -- ^ Project @a@ out of @s@, returning 'Nothing' on mismatch. } ---------------------------------------------------------------------------- +-- | Reconstruct an @s@ from an @a@ using a t'Prism'. review :: Prism s a -> a -> s review = _up ---------------------------------------------------------------------------- +-- | Attempt to project an @a@ out of the @MonadReader@ environment using a t'Prism'. preview :: MonadReader r m => Prism r a -> m (Maybe a) preview = asks . preview ---------------------------------------------------------------------------- +-- | Attempt to project an @a@ out of the @MonadState@ state using a t'Prism'. preuse :: MonadState s m => Prism s a -> m (Maybe a) preuse = gets . preview ---------------------------------------------------------------------------- +-- | t'Prism' targeting the 'Left' branch of 'Either'. _Left :: Prism (Either a b) a _Left = prism Left $ either Just (const Nothing) ---------------------------------------------------------------------------- +-- | t'Prism' targeting the 'Right' branch of 'Either'. _Right :: Prism (Either a b) b _Right = prism Right (either (const Nothing) Just) ---------------------------------------------------------------------------- +-- | t'Prism' targeting the 'Just' branch of 'Maybe'. _Just :: Prism (Maybe a) a _Just = prism Just Prelude.id ---------------------------------------------------------------------------- +-- | t'Prism' targeting the 'Nothing' branch of 'Maybe'. _Nothing :: Prism (Maybe a) a _Nothing = prism (const Nothing) Prelude.id ---------------------------------------------------------------------------- +-- | Operator form of 'preview': @s ^? p@ projects @a@ from @s@. infixl 8 ^? -(^?) :: s -> Prism s a -> Maybe a +(^?) :: s -> Prism s a -> Maybe a (^?) = flip preview ---------------------------------------------------------------------------- +-- | Smart constructor for t'Prism'. prism :: (a -> s) -> (s -> Maybe a) -> Prism s a prism = Prism ---------------------------------------------------------------------------- diff --git a/src/Miso/Lens/Generic.hs b/src/Miso/Lens/Generic.hs index 2745ed6e6..779ac5eb1 100644 --- a/src/Miso/Lens/Generic.hs +++ b/src/Miso/Lens/Generic.hs @@ -1,9 +1,9 @@ ----------------------------------------------------------------------------- -{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} @@ -33,7 +33,10 @@ import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError) import Miso.Lens (Lens, lens) ----------------------------------------------------------------------------- -class Generic s => HasLens (name :: Symbol) s a | name s -> a where +-- | Class that associates a record field @name@ with a t'Lens' into @s@ focusing on @a@. +-- Instances are derived automatically via 'Generic'; use 'field' or @OverloadedLabels@ to construct lenses. +class Generic s => HasLens (name :: Symbol) s a | name s -> a where + -- | Retrieve the t'Lens' for the field named @name@ in @s@. getLens :: Lens s a instance @@ -45,8 +48,12 @@ instance instance HasLens name s a => IsLabel name (Lens s a) where fromLabel = getLens @name +-- | Construct a t'Lens' for the record field named @name@ using @TypeApplications@. +-- +-- > age :: Lens Person Int +-- > age = field @"age" {-# INLINE field #-} -field :: forall name s a. HasLens name s a => Lens s a +field :: forall name s a. HasLens name s a => Lens s a field = fromLabel @name class GSet (name :: Symbol) typ f where diff --git a/src/Miso/Prelude.hs b/src/Miso/Prelude.hs index 5a0032fd0..61c917ec1 100644 --- a/src/Miso/Prelude.hs +++ b/src/Miso/Prelude.hs @@ -39,9 +39,15 @@ module Miso.Prelude ( module Miso , module Prelude , (.) + -- | The boolean type, with values 'True' and 'False'. + , Bool (..) + -- | Ordering result of a comparison: 'LT', 'EQ', or 'GT'. + , Ordering (..) ) where ---------------------------------------------------------------------------- import Control.Category ((.)) -import Prelude hiding ((.), (!!)) +import Prelude hiding ((.), (!!), Bool, Ordering) +import Data.Bool (Bool (..)) +import Data.Ord (Ordering (..)) import Miso ---------------------------------------------------------------------------- diff --git a/src/Miso/Random.hs b/src/Miso/Random.hs index f463e4efb..98cf03106 100644 --- a/src/Miso/Random.hs +++ b/src/Miso/Random.hs @@ -55,7 +55,7 @@ newStdGen = do seed <- FFI.getRandomValue StdGen . Function <$> FFI.splitmix32 seed ----------------------------------------------------------------------------- --- | Get the next t'StdGen', extracting the value, useful with t'State'. +-- | Get the next t'StdGen', extracting the value, useful with t'Control.Monad.State.State'. next :: StdGen -> (Double, StdGen) next (StdGen func) = unsafePerformIO $ do result <- apply func () diff --git a/src/Miso/Reload.hs b/src/Miso/Reload.hs index 003d03446..47118e531 100644 --- a/src/Miso/Reload.hs +++ b/src/Miso/Reload.hs @@ -91,11 +91,11 @@ foreign import ccall unsafe "miso_x_clear" -- -- @ -- main :: IO () --- main = 'reload' 'defaultEvents' app +-- main = 'reload' 'Miso.Event.Types.defaultEvents' app -- @ -- --- N.B. This also resets the internal 'component' state. This means all currently --- mounted components become unmounted and t'ComponentId' are reset to their +-- N.B. This also resets the internal 'Miso.Types.component' state. This means all currently +-- mounted components become unmounted and t'Miso.Effect.ComponentId' are reset to their -- original form factory. -- -- If you'd like to preserve application state between calls to GHCi `:r`, see 'live'. @@ -121,21 +121,21 @@ reload events vcomp = do #endif initComponent events Draw vcomp ----------------------------------------------------------------------------- --- | Live reloading. Persists all t'Component' `model` between successive GHCi reloads. +-- | Live reloading. Persists all t'Miso.Types.Component' @model@ between successive GHCi reloads. -- --- This means application state should persist between GHCi reloads +-- This means application state should persist between GHCi reloads -- --- Schema changes to 'model' are currently unsupported. If you're --- changing fields in 'model' (adding, removing, changing a field's type), this --- will more than likely segfault. If you change the 'view' or 'update' functions --- it will be fine. +-- Schema changes to @model@ are currently unsupported. If you're +-- changing fields in @model@ (adding, removing, changing a field's type), this +-- will more than likely segfault. If you change the 'Miso.Types.view' or 'Miso.Types.update' functions +-- it will be fine. -- --- Use 'reload' if you're changing the 'model' frequently and 'live' --- if you're adjusting the 'view' / 'update' function logic. +-- Use 'reload' if you're changing the @model@ frequently and 'live' +-- if you're adjusting the 'Miso.Types.view' / 'Miso.Types.update' function logic. -- -- @ -- main :: IO () --- main = 'live' 'defaultEvents' app +-- main = 'live' 'Miso.Event.Types.defaultEvents' app -- @ -- -- @since 1.9.0.0 diff --git a/src/Miso/Router.hs b/src/Miso/Router.hs index b2506a78a..ba4926e20 100644 --- a/src/Miso/Router.hs +++ b/src/Miso/Router.hs @@ -322,6 +322,7 @@ index specified = do when (specified /= "index") (fail "index") pure "/" ----------------------------------------------------------------------------- +-- | Parses a URI fragment component matching the given string. fragment :: MisoString -> RouteParser MisoString fragment specified = do FragmentToken frag <- indexToken @@ -338,6 +339,7 @@ parseURI txt = ----------------------------------------------------------------------------- -- | Class used to facilitate routing for miso applications class Router route where + -- | Convert a route value into a list of t'Token's used to build the URL. fromRoute :: route -> [Token] default fromRoute :: (Generic route, GRouter (Rep route)) => route -> [Token] fromRoute = gFromRoute . from @@ -366,6 +368,7 @@ class Router route where toRoute :: MisoString -> Either RoutingError route toRoute input = parseRoute input routeParser + -- | The parser used to decode a URI into this route type. routeParser :: RouteParser route default routeParser :: (Generic route, GRouter (Rep route)) => RouteParser route routeParser = to <$> gRouteParser @@ -583,6 +586,7 @@ pctEncoded = do hexDig :: Lexer Char hexDig = L.satisfy C.isHexDigit ----------------------------------------------------------------------------- +-- | Tokenizes a URI string into a list of t'Token's, or returns a lexer error. lexTokens :: MisoString -> Either L.LexerError [Token] lexTokens input = case L.runLexer uriLexer (L.mkStream input) of diff --git a/src/Miso/Runtime.hs b/src/Miso/Runtime.hs index 14b780eaf..e40e69a47 100644 --- a/src/Miso/Runtime.hs +++ b/src/Miso/Runtime.hs @@ -616,7 +616,7 @@ dequeue dequeue q = case q ^. queueSchedule of S.Empty -> Nothing - sched@(vcompId S.:<| leftover) -> + sched@(vcompId S.:<| _leftover) -> case q ^. queue . at vcompId of Nothing -> let (_, remaining) = S.spanl (== vcompId) sched diff --git a/src/Miso/String.hs b/src/Miso/String.hs index ba9c876da..b61457f3c 100644 --- a/src/Miso/String.hs +++ b/src/Miso/String.hs @@ -3,7 +3,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleInstances #-} ----------------------------------------------------------------------------- -{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# OPTIONS_GHC -fno-warn-orphans -Wno-duplicate-exports #-} ----------------------------------------------------------------------------- -- | -- Module : Miso.String diff --git a/src/Miso/Subscription/History.hs b/src/Miso/Subscription/History.hs index 79fac3737..50c639f1e 100644 --- a/src/Miso/Subscription/History.hs +++ b/src/Miso/Subscription/History.hs @@ -40,7 +40,7 @@ pushURI uri = do pushState (prettyURI uri) raisePopState ----------------------------------------------------------------------------- --- | Pushes a new 'Route' onto the History stack. Also raises a `popstate` event. +-- | Pushes a new t'Miso.Router.Route' onto the History stack. Also raises a @popstate@ event. -- -- Converts the t'Route' to a t'URI' internally. -- diff --git a/src/Miso/Types.hs b/src/Miso/Types.hs index 717ffb5dd..798353129 100644 --- a/src/Miso/Types.hs +++ b/src/Miso/Types.hs @@ -161,7 +161,7 @@ data Component parent props model action , eventPropagation :: Bool -- ^ Should events bubble up past the t'Miso.Types.Component' barrier. -- - -- Defaults to t'False' + -- Defaults to @False@ -- -- @since 1.9.0.0 , mount :: Maybe action @@ -294,7 +294,7 @@ data LogLevel -- ^ Logs on all of the above deriving (Show, Eq) ----------------------------------------------------------------------------- --- | Tag type, (e.g. 'div_', 'p_') +-- | Tag type, (e.g. 'Miso.Html.Element.div_', 'Miso.Html.Element.p_') -- -- Meant to indicate the type of element being created. -- Used as the first argument to @document.createElement@ for the web backend. @@ -316,7 +316,7 @@ data SomeComponent parent ----------------------------------------------------------------------------- -- | Like '+>' but operates on any 'View', not just 'Component'. -- --- This appends a 'Key' to any 'View'. +-- This appends a t'Key' to any 'View'. -- -- @ -- keyed "key" ("some text" :: View model action) @@ -359,13 +359,13 @@ vfrag = fragment fragment :: [View model action] -> View model action fragment = VFrag Nothing ----------------------------------------------------------------------------- --- | Like 'fragment', but keyed for efficient diffing. +-- | Like 'Miso.Types.fragment', but keyed for efficient diffing. -- -- @since 1.10.0.0 vfrag_ :: MisoString -> [View model action] -> View model action vfrag_ key = VFrag (Just (Key key)) ----------------------------------------------------------------------------- --- | Like 'fragment', but keyed for efficient diffing. +-- | Like 'Miso.Types.fragment', but keyed for efficient diffing. -- -- @since 1.10.0.0 fragment_ :: MisoString -> [View model action] -> View model action @@ -384,9 +384,9 @@ fragment_ key = VFrag (Just (Key key)) (+>) :: forall child childAction model action . Eq child => MisoString - -- ^ 'VComp' 'key_' + -- ^ 'VComp' 'Miso.Property.key_' -> Component model () child childAction - -- ^ 'Component' + -- ^ t'Miso.Types.Component' -> View model action infixr 0 +> key +> comp = VComp (Just (toKey key)) (SomeComponent () comp) @@ -406,9 +406,9 @@ key +> comp = VComp (Just (toKey key)) (SomeComponent () comp) mountWithProps :: (Eq child, Eq props) => props - -- ^ 'props' to use + -- ^ @props@ to use -> Component parent props child action - -- ^ 'Component' to mount + -- ^ t'Miso.Types.Component' to mount -> View parent a mountWithProps props comp = VComp Nothing (SomeComponent props comp) ----------------------------------------------------------------------------- @@ -423,11 +423,11 @@ mountWithProps props comp = VComp Nothing (SomeComponent props comp) mountWithProps_ :: (Eq child, Eq props) => MisoString - -- ^ 'key' to use + -- ^ @key@ to use -> props - -- ^ 'props' to use + -- ^ @props@ to use -> Component parent props child action - -- ^ 'Component' to mount + -- ^ t'Miso.Types.Component' to mount -> View parent a mountWithProps_ key props comp = VComp (Just (Key key)) (SomeComponent props comp) ----------------------------------------------------------------------------- @@ -446,7 +446,7 @@ mountWithProps_ key props comp = VComp (Just (Key key)) (SomeComponent props co mount_ :: Eq child => Component parent () child childAction - -- ^ 'Component' to mount + -- ^ t'Miso.Types.Component' to mount -> View parent action mount_ comp = VComp Nothing (SomeComponent () comp) -----------------------------------------------------------------------------