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

Miso.Lens

Description

A simple Lens formulation compatible with lens and microlens.

The Lens type is defined as:

data Lens record field
 = Lens
 { _get :: record -> field
 , _set :: record -> field -> record
 }

Key features:

  • Provides an out-of-the-box lens experience with a minimal dependency footprint.
  • Uses a simple formulation (not van Laarhoven) for smaller compilation payload.
  • Import separately: import Miso.Lens.
  • Works with the Effect monad inside miso applications.
  • Fixity and interface parity with lens and microlens; replace import Miso.Lens with import Control.Lens to switch seamlessly.
  • Re-exports Lens' for easy migration to lens or microlens.

For more on the van Laarhoven formulation, see the lens library.

Example: Lenses for a nested record

data Person = Person
  { _name :: String
  , _address :: Address
  , _age  :: Int
  } deriving (Show, Eq, Generic)

newtype Address
  = Address
  { _zipCode :: Zip
  } deriving (Show, Eq)

type Zip = String

name :: Lens Person String
name = lens _name $ \record x -> record { _name = x }

address :: Lens Person Address
address = lens _address $ \record x -> record { _address = x }

zipCode :: Lens Address Zip
zipCode = lens _zipCode $ \record x -> record { _zipCode = x }

-- Lenses compose via .
personZip :: Lens Person Zip
personZip = zipCode . address

main :: IO ()
main = print $ person & address .~ Address "10012"
  -- Person { _name = "john", _age = 33, _address = Address {_zipCode = "10012"} }

Example: Usage with the Effect monad

newtype Model = Model { _value :: Int }

value :: Lens Model Int
value = lens _value $ \model v -> model { _value = v }

data Action = AddOne | SubtractOne

updateModel :: Action -> Effect parent Model Action
updateModel = \case
  AddOne    -> value += 1
  SubtractOne -> value -= 1
Synopsis

Types

type Lens s a = LensCore a s Source #

A Lens is a generalized getter and setter.

Lenses allow both the retrieval of values from fields in a record and the assignment of values to fields in a record. The power of a Lens comes from its ability to be composed with other lenses.

In the context of building applications with miso, the model is often a deeply nested product type. This makes it highly conducive to Lens operations (as defined below).

data LensCore field record Source #

LensCore is an internal type used to reverse composition like VL libraries do.

Constructors

Lens 

Fields

  • _get :: record -> field

    Retrieves a field from a record

  • _set :: field -> record -> record

    Sets a field on a record

Instances

Instances details
Category LensCore Source #

Lens form a Category, and can therefore be composed.

Instance details

Defined in Miso.Lens

Methods

id :: LensCore a a #

(.) :: LensCore b c -> LensCore a b -> LensCore a c #

HasLens name s a => IsLabel name (Lens s a) Source # 
Instance details

Defined in Miso.Lens.Generic

Methods

fromLabel :: Lens s a #

data Prism s a Source #

A Prism is a first-class reference into a sum type constructor.

Prism values can be used with preview to try to extract a value, and with review to embed a value back into the sum type.

Constructors

Prism 

Fields

  • _up :: a -> s

    Embed a value a back into the sum type s (the review direction).

  • _down :: s -> Maybe a

    Try to extract a value a from s; Nothing if the wrong constructor.

Smart constructor

lens Source #

Arguments

:: (record -> field)

Getter: read the field from a record

-> (record -> field -> record)

Setter: write a new field value into a record

-> Lens record field 

Smart constructor lens function. Used to easily construct a Lens

name :: 'Lens' Person String
name = 'lens' _name $ \p n -> p { _name = n }

prism Source #

Arguments

:: (a -> s)

Embed direction: construct s from a.

-> (s -> Maybe a)

Match direction: try to extract a from s.

-> Prism s a 

Smart constructor for Prism.

Re-exports

(&) :: a -> (a -> b) -> b infixl 1 #

& is a reverse application operator. This provides notational convenience. Its precedence is one higher than that of the forward application operator $, which allows & to be nested in $.

This is a version of flip id, where id is specialized from a -> a to (a -> b) -> (a -> b) which by the associativity of (->) is (a -> b) -> a -> b. flipping this yields a -> (a -> b) -> b which is the type signature of &

Examples

Expand
>>> 5 & (+1) & show
"6"
>>> sqrt $ [1 / n^2 | n <- [1..1000]] & sum & (*6)
3.1406380562059946

Since: base-4.8.0.0

(<&>) :: Functor f => f a -> (a -> b) -> f b infixl 1 #

Flipped version of <$>.

(<&>) = flip fmap

Examples

Expand

Apply (+1) to a list, a Just and a Right:

>>> Just 2 <&> (+1)
Just 3
>>> [1,2,3] <&> (+1)
[2,3,4]
>>> Right 3 <&> (+1)
Right 4

Since: base-4.11.0.0

Lens Combinators

(.~) :: Lens record field -> field -> record -> record infixr 4 Source #

Set a field on a record

newtype Person = Person { _name :: String }

name :: Lens Person String
name = lens _name $ \person n -> person { _name = n }

setName :: Person -> String -> Person
setName person newName = person & name .~ newName

(?~) :: Lens record (Maybe field) -> field -> record -> record infixr 4 Source #

Set an options field on a record

newtype Person = Person { _name :: Maybe String }

name :: Lens Person (Maybe String)
name = lens _name $ \person n -> person { _name = n }

setName :: Person -> String -> Person
setName person newName = person & name ?~ newName

set :: Lens record field -> field -> record -> record Source #

Synonym for (.~)

(%~) :: Lens record field -> (field -> field) -> record -> record infixr 4 Source #

Modify a field on a record by applying a function to it.

newtype Counter = Counter { _value :: Int }

value :: Lens Counter Int
value = lens _value $ \counter v -> counter { _value = v }

increment :: Counter -> Counter
increment counter = counter & value %~ (+1)

over :: Lens record field -> (field -> field) -> record -> record Source #

Synonym for (%~)

(^.) :: record -> Lens record field -> field infixl 8 Source #

Read a field from a record using a Lens

newtype Person = Person { _name :: String }
  deriving (Show, Eq)

name :: Lens Person String
name = lens _name $ \person n -> person { _name = n }

getName :: Person -> String
getName = person ^. name

(+~) :: Num field => Lens record field -> field -> record -> record infixr 4 Source #

Increment a Num field => field on a record using a Lens

newtype Person = Person { _age :: Int }

age :: Lens Person Int
age = lens _age $ \person a -> person { _age = a }

birthday :: Person -> Person
birthday person = person & age +~ 1

(*~) :: Num field => Lens record field -> field -> record -> record infixr 4 Source #

Multiply a Numeric field on a record using a Lens

newtype Circle = Circle { _radius :: Int }

radius :: Lens Circle Int
radius = lens _radius $ \circle r -> circle { _radius = r }

expand :: Circle -> Circle
expand circle = circle & radius *~ 10

(//~) :: Fractional field => Lens record field -> field -> record -> record infixr 4 Source #

Divide a Fractional field on a record using a Lens

newtype Circle = Circle { _radius :: Int }

radius :: Lens Circle Int
radius = lens _radius $ \circle r -> circle { _radius = r }

shrink :: Circle -> Circle
shrink circle = circle & radius //~ 10

(-~) :: Num field => Lens record field -> field -> record -> record infixr 4 Source #

Increment a Numeric field on a record using a Lens

newtype Person = Person { _age :: Int }

age :: Lens Person Int
age = lens _age $ \person a -> person { _age = a }

timeTravel :: Person -> Person
timeTravel person = person & age -~ 1

(%=) :: MonadState record m => Lens record field -> (field -> field) -> m () infix 4 Source #

Modify a record in MonadState monad at a field using a Lens

newtype Model = Model { _value :: Int }

data Action = AddOne | SubtractOne

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update AddOne = do
  value %= (+1)

(%?=) :: MonadState record m => Lens record (Maybe field) -> (field -> field) -> m () infix 4 Source #

Alters the Just value of a field in a record using a Lens inside a MonadState

newtype Model = Model { _value :: Maybe Int }
  deriving (Show, Eq)

data Action = IncrementIfJust

value :: Lens Model (Maybe Int)
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update IncrementIfJust = value %?= (+1)

modifying :: MonadState record m => Lens record field -> (field -> field) -> m () Source #

Synonym for (%=)

(+=) :: (MonadState record m, Num field) => Lens record field -> field -> m () infix 4 Source #

Increments the value of a Numeric field of a record using a Lens inside a State Monad.

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = IncrementBy Int

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (IncrementBy x) = value += x

(*=) :: (MonadState record m, Num field) => Lens record field -> field -> m () infix 4 Source #

Multiplies the value of a Numeric field of a record using a Lens inside a State Monad.

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = MultiplyBy Int

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (MultiplyBy x) = value *= x

(//=) :: (MonadState record m, Fractional field) => Lens record field -> field -> m () infix 4 Source #

Divides the value of a Fractional field of a record using a Lens inside a State Monad.

newtype Model = Model { _value :: Double }
  deriving (Show, Eq)

data Action = DivideBy Double

value :: Lens Model Double
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (DivideBy x) = value //= x

(-=) :: (MonadState record m, Num field) => Lens record field -> field -> m () infix 4 Source #

Subtracts the value of a Numeric field of a record using a Lens inside of a State Monad.

newtype Model = Model { _value :: Double }
  deriving (Show, Eq)

data Action = SubtractBy Double

value :: Lens Model Double
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (SubtractBy x) = value -= x

(.=) :: MonadState record m => Lens record field -> field -> m () infix 4 Source #

Sets the value of a field in a record using MonadState and a Lens

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = SetValue Int

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update' :: Action -> Effect parent Model Action
update' (SetValue v) = value .= v

(<~) :: MonadState record m => Lens record field -> m field -> m () infixr 2 Source #

Execute a monadic action in MonadState that returns a field. Sets the return value equal to the field in the record.

As a reasonable mnemonic, this lets you store the result of a monadic action in a Lens rather than in a local variable.

do foo <- bar
   ...

will store the result in a variable, while

do fooLens <~ bar
   ...

will store the result in field focused by the Lens.

(<%=) :: MonadState record m => Lens record field -> (field -> field) -> m field infix 4 Source #

Modify the field of a record in MonadState using a Lens, then return the newly modified field from the updated record.

import Miso.String (ms)

newtype Model = Model { _value :: Int }
  deriving (Show)

data Action = AddOne

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update AddOne = do
  result <- value <%= (+1)
  io_ $ consoleLog (ms result)

(<.=) :: MonadState record m => Lens record field -> field -> m field infix 4 Source #

Assign the field of a record in MonadState to a value using a Lens Return the value after assignment.

import Miso.String (ms)

newtype Model = Model { _value :: Int }

data Action = Assign Int

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (Assign x) = do
  result <- value <.= x
  io_ $ consoleLog (ms result) -- x

(<?=) :: MonadState record m => Lens record (Maybe field) -> field -> m field infix 4 Source #

Assign the field of a record in a MonadState to a value (wrapped in a Just) using a Lens. Return the value after assignment.

import Miso.String (ms)

newtype Model = Model { _value :: Maybe Int }

data Action = SetValue Int

value :: Lens Model (Maybe Int)
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (SetValue x) = do
  result <- value <?= x
  io_ $ consoleLog (ms result) -- Just 1

(<<.=) :: MonadState record m => Lens record field -> field -> m field infix 4 Source #

Assign the field of a record in a MonadState to a value using a Lens. Returns the previous value, before assignment.

import Miso.String (ms)

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = Assign Int
  deriving (Show, Eq)

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (Assign x) = do
  value .= x
  previousValue <- value <<.= 1
  io_ $ consoleLog $ ms previousValue -- prints value at x

(<<%=) :: MonadState record m => Lens record field -> (field -> field) -> m field infix 4 Source #

Modifies the field of a record in MonadState using a Lens. Returns the previous value, before modification.

import Miso.String (ms)

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = Modify (Int -> Int)

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (Modify f) = do
  value .= 2
  result <- value <<%= f
  io_ $ consoleLog (ms result) -- prints previous value of 2

assign :: MonadState record m => Lens record field -> field -> m () Source #

Synonym for (.=)

use :: MonadState record m => Lens record field -> m field Source #

Retrieves the value of a field in a record using a Lens inside MonadState

import Miso.String (ms)

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = SetValue Int

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (SetValue x) = do
  value .= x
  result <- use value
  io_ $ consoleLog (ms result) -- prints the value of x

view :: MonadReader record m => Lens record field -> m field Source #

Retrieves the field associated with a record in MonadReader using a Lens.

import Miso.String (ms)

newtype Model = Model { _value :: Int }
  deriving (Show, Eq)

data Action = PrintInt

value :: Lens Model Int
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update PrintInt = do
  Model x <- view value
  io_ $ consoleLog (ms x) -- prints model value

(?=) :: MonadState record m => Lens record (Maybe field) -> field -> m () infix 4 Source #

Sets the value of a field in a record using a Lens inside a MonadState The value is wrapped in a Just before being assigned.

newtype Model = Model { _value :: Maybe Int }
  deriving (Show, Eq)

data Action = AssignValue Int

value :: Lens Model (Maybe Int)
value = lens _value $ \p x -> p { _value = x }

update :: Action -> Effect parent Model Action
update (AssignValue x) = value ?= x

(<>~) :: Monoid field => Lens record field -> field -> record -> record infixr 4 Source #

Monoidally append a field in a record using a Lens

newtype List = List { _values :: [Int] }

values :: Lens List [Int]
values = lens _values $ \l vs -> l { _values = vs }

addElement :: List -> List
addElement list = list & values <>~ [2]

addElement (List [])
-- List [2]

_1 :: Lens (a, b) a Source #

Lens that operates on the first element of a tuple

update AddOne = do
  _1 += 1

_2 :: Lens (a, b) b Source #

Lens that operates on the second element of a tuple

update AddOne = do
  _2 += 1

_id :: Lens a a Source #

Lens that operates on itself

update AddOne = do
  _id += 1

this :: Lens a a Source #

Lens that operates on itself

update AddOne = do
  this += 1

Prism Combinators

preview :: MonadReader r m => Prism r a -> m (Maybe a) Source #

Try to extract a value from a sum type using a Prism inside MonadReader.

preuse :: MonadState s m => Prism s a -> m (Maybe a) Source #

Try to extract a value from a sum type using a Prism inside MonadState.

review :: Prism s a -> a -> s Source #

Embed a value into a sum type using a Prism.

_Nothing :: Prism (Maybe a) a Source #

Prism that matches a Nothing value.

_Just :: Prism (Maybe a) a Source #

Prism for the Just constructor of Maybe.

_Left :: Prism (Either a b) a Source #

Prism for the Left constructor of Either.

_Right :: Prism (Either a b) b Source #

Prism for the Right constructor of Either.

(^?) :: s -> Prism s a -> Maybe a infixl 8 Source #

Infix alias for preview. Try to extract a value using a Prism.

Right 42 ^? _Right == Just 42
Left  "x" ^? _Right == Nothing

Containers

class At at where Source #

Class for getting and setting values across various container types.

M.singleton 'a' "foo" & at 'a' .~ Just "bar"
-- fromList [('a',"bar")]
update (SetValue value)
  at 10 ?= value

Since: 1.9.0.0

Associated Types

type Index at Source #

Index of the container

type IxValue at Source #

Indexed value of the container

Methods

at :: Index at -> Lens at (Maybe (IxValue at)) Source #

Instances

Instances details
At IntSet Source # 
Instance details

Defined in Miso.Lens

Associated Types

type Index IntSet 
Instance details

Defined in Miso.Lens

type IxValue IntSet 
Instance details

Defined in Miso.Lens

type IxValue IntSet = ()
At (IntMap v) Source # 
Instance details

Defined in Miso.Lens

Associated Types

type Index (IntMap v) 
Instance details

Defined in Miso.Lens

type Index (IntMap v) = Int
type IxValue (IntMap v) 
Instance details

Defined in Miso.Lens

type IxValue (IntMap v) = v

Methods

at :: Index (IntMap v) -> Lens (IntMap v) (Maybe (IxValue (IntMap v))) Source #

Ord k => At (Set k) Source # 
Instance details

Defined in Miso.Lens

Associated Types

type Index (Set k) 
Instance details

Defined in Miso.Lens

type Index (Set k) = k
type IxValue (Set k) 
Instance details

Defined in Miso.Lens

type IxValue (Set k) = ()

Methods

at :: Index (Set k) -> Lens (Set k) (Maybe (IxValue (Set k))) Source #

At [a] Source # 
Instance details

Defined in Miso.Lens

Associated Types

type Index [a] 
Instance details

Defined in Miso.Lens

type Index [a] = Int
type IxValue [a] 
Instance details

Defined in Miso.Lens

type IxValue [a] = a

Methods

at :: Index [a] -> Lens [a] (Maybe (IxValue [a])) Source #

Ord k => At (Map k v) Source # 
Instance details

Defined in Miso.Lens

Associated Types

type Index (Map k v) 
Instance details

Defined in Miso.Lens

type Index (Map k v) = k
type IxValue (Map k v) 
Instance details

Defined in Miso.Lens

type IxValue (Map k v) = v

Methods

at :: Index (Map k v) -> Lens (Map k v) (Maybe (IxValue (Map k v))) Source #

Re-exports

compose :: Category cat => cat a b -> cat b c -> cat a c Source #

Function composition generalized to Category

test :: Int -> Int
test = (+1) `compose` (+1)

Conversion

type Lens' s a = forall (f :: Type -> Type). Functor f => (a -> f a) -> s -> f s Source #

van Laarhoven formulation, used for conversion w/ miso Lens.

toVL :: Lens record field -> Lens' record field Source #

Convert a Lens to a van Laarhoven Lens'

fromVL Source #

Arguments

:: Lens' record field

Van Laarhoven lens to convert

-> Lens record field 

Convert a van Laarhoven Lens' to a Lens