Core concepts
State & lenses
miso bundles a lightweight lens library in Miso.Lens to minimise dependencies and payload size. Any lens library (optics, lens) also works — Miso.Lens is not required.
Basic operations
view l -- read a field (MonadReader)
set l v -- write a field
over l f -- modify a field
r ^. l -- infix read
r & l .~ v -- infix writeMonadState operators (inside Effect)
l .= v -- set a field
l %= f -- modify a field
l += n -- increment a numeric field
l -= n -- decrement
l *= n -- multiplythis — the identity lens
When the model is the field (e.g. the model is a plain Int), use this:
update = \case
Increment -> this += 1
Decrement -> this -= 1Generating lenses
Three approaches, pick one:
Template Haskell
{-# LANGUAGE TemplateHaskell #-}
import Miso.Lens.TH (makeLenses)
data Model = Model { _count :: Int, _name :: MisoString }
makeLenses ''Model
update = \case
Increment -> count += 1
Rename n -> name .= nGenerics
Miso.Lens.Generic.field / HasLens derive lenses at compile time using GHC.Generics — no splice required. Needs TypeApplications and, optionally, OverloadedLabels for the #field shorthand:
{-# LANGUAGE DataKinds, DeriveGeneric #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TypeApplications #-}
import GHC.Generics (Generic)
import Miso.Lens.Generic (field)
data Model = Model
{ count :: Int
, name :: MisoString
} deriving (Eq, Generic)
update = \case
Increment -> field @"count" += 1
-- via TypeApplications
Rename n -> #name .= n
-- via OverloadedLabelsHand-written
name :: Lens Person MisoString
name = lens _name $ \p n -> p { _name = n }Try it
Ada is 36 years old
data Person = Person
{ _name :: MisoString
, _age :: Int
} deriving (Show, Eq)
-- Hand-written lenses (Miso.Lens.TH and
-- Miso.Lens.Generic can write these).
name :: Lens Person MisoString
name = lens _name $ \p n -> p { _name = n }
age :: Lens Person Int
age = lens _age $ \p a -> p { _age = a }
data PersonAction
= Rename MisoString
| Birthday
| Younger
person
:: Component ctx () Person PersonAction
person = component (Person "Ada" 36) update view
where
update = \case
Rename n -> name .= n
-- set through a lens
Birthday -> age += 1
-- arithmetic through a lens
Younger -> age %= max 0 . subtract 1
view p =
H.div_ []
[ H.input_
[ HP.value_ (p ^. name), HE.onInput Rename ]
, H.p_ []
[ text (p ^. name)
, " is "
, text (ms (p ^. age))
, " years old"
]
, H.button_ [ HE.onClick Birthday ] [ "birthday" ]
, H.button_ [ HE.onClick Younger ] [ "younger" ]
]