diff --git a/src/Data/Lens/Prism.purs b/src/Data/Lens/Prism.purs index 3869a4e..23bb6f5 100644 --- a/src/Data/Lens/Prism.purs +++ b/src/Data/Lens/Prism.purs @@ -1,7 +1,77 @@ --- | This module defines functions for working with prisms. +-- | Prisms are used for selecting cases of a type, most often a sum +-- | type. Consider this: +-- | +-- | ```purescript +-- | data Fill -- think of a paint program filling a shape +-- | = NoFill +-- | | Solid Color +-- | | ... +-- | ``` +-- | +-- | A prism that focuses on `Solid` fills could be written like this: +-- | +-- | ```purescript +-- | solidFocus :: Prism' Fill Color +-- | solidFocus = prism' Solid case _ of +-- | Solid color -> Just color +-- | _ -> Nothing +-- | ``` +-- | +-- | ... and used like this: +-- | +-- | ```purescript +-- | preview solidFocus (Solid Color.white) == Just Color.white +-- | preview solidFocus NoFill == Nothing +-- | +-- | is solidFocus (Solid Color.white) == true +-- | ``` +-- | +-- | `review` can be used to go from a `Color` to a `Fill`: +-- | +-- | ```purescript +-- | review solidFocus Color.white == Solid Color.white +-- | ``` +-- | +-- | For more information, see `PrismsForSumTypes.purs` in the +-- | `examples/src` directory. +-- | +-- | --------------- +-- | +-- | A well-behaved `Prism` will follow these laws: +-- | +-- | **review-preview**: `preview` retrieves what `review` creates. Equationally: +-- | +-- | ```purescript +-- | review prism >>> preview prism ≡ Just +-- | ``` +-- | +-- | An example: +-- | +-- | ```purescript +-- | Color.white # review solidFocus # preview solidFocus +-- | == Just Color.white +-- | ``` +-- | +-- | **preview-review**: If `preview` retrieves something, `review` can create +-- | the original from that something. Equationally: +-- | +-- | ```purescript +-- | if preview prism s ≡ Just a then review prism a ≡ s +-- | ``` +-- | +-- | An example: +-- | +-- | ```purescript +-- | Solid Color.white # preview solidFocus <#> review solidFocus +-- | == Solid Color.white +-- | ``` + module Data.Lens.Prism - ( prism, prism', review, nearly, only, clonePrism, withPrism, matching - , is, isn't + ( prism', prism + , only, nearly + , review + , is, isn't, matching + , clonePrism, withPrism , module ExportTypes ) where @@ -18,23 +88,75 @@ import Data.Profunctor (dimap, rmap) import Data.Profunctor.Choice (right) import Data.Newtype (under) --- | Create a `Prism` from a constructor/pattern pair. +-- | Create a `Prism` from a constructor and a matcher function that +-- | produces an `Either`: +-- | +-- | ```purescript +-- | solidFocus :: Prism' Fill Color +-- | solidFocus = prism Solid case _ of +-- | Solid color -> Right color +-- | anotherCase -> Left anotherCase +-- | ``` +-- | +-- | _Note_: The matcher function returns a result wrapped in `Either t` +-- | to allow for type-changing prisms in the case where the input does +-- | not match. prism :: forall s t a b. (b -> t) -> (s -> Either t a) -> Prism s t a b prism to fro pab = dimap fro (either id id) (right (rmap to pab)) +-- | Create a `Prism` from a constructor and a matcher function that +-- | produces a `Maybe`: +-- | +-- | ```purescript +-- | solidFocus :: Prism' Fill Color +-- | solidFocus = prism' Solid case _ of +-- | Solid color -> Just color +-- | _ -> Nothing +-- | ``` prism' :: forall s a. (a -> s) -> (s -> Maybe a) -> Prism' s a prism' to fro = prism to (\s -> maybe (Left s) Right (fro s)) --- | Review a value through a `Prism`. -review :: forall s t a b. Review s t a b -> b -> t -review = under Tagged - +-- | `nearly` is a variant of `only`. Like `only`, `nearly` produces +-- | a prism that matches +-- | a single value. Unlike `only`, it uses a predicate you supply +-- | instead of depending on `class Eq`: +-- | +-- | ```purescript +-- | solidWhiteFocus :: Prism' Fill Unit +-- | solidWhiteFocus = nearly (Solid Color.white) predicate +-- | where +-- | predicate candidate = +-- | color.toHexString == Color.white.toHexString +-- | ``` nearly :: forall a. a -> (a -> Boolean) -> Prism' a Unit nearly x f = prism' (const x) (guard <<< f) +-- | `only` focuses not just on a case, but a specific value of that case. +-- | +-- | ```purescript +-- | solidWhiteFocus :: Prism' Fill Unit +-- | solidWhiteFocus = only $ Solid Color.white +-- | +-- | is solidWhiteFocus (Solid Color.white) == true +-- | preview solidWhiteFocus (Solid Color.white) == Just unit +-- | review solidWhiteFocus unit == Solid Color.white +-- | ``` +-- | +-- | *Note*: `only` depends on `Eq`. Strange definitions of `(==)` +-- | (for example, that it counts any `Fill` as being equal to `Solid Color.white`) +-- | will create a prism that violates the preview-review law. only :: forall a. Eq a => a -> Prism a a Unit Unit only x = nearly x (_ == x) + +-- | Create the "whole" corresponding to a specific "part": +-- | +-- | ```purescript +-- | review solidFocus Color.white == Solid Color.white +-- | ``` +review :: forall s t a b. Review s t a b -> b -> t +review = under Tagged + clonePrism :: forall s t a b. APrism s t a b -> Prism s t a b clonePrism l = withPrism l \x y p -> prism x y p @@ -45,8 +167,10 @@ withPrism l f = case l (Market id Right) of matching :: forall s t a b. APrism s t a b -> s -> Either t a matching l = withPrism l \_ f -> f +--| Ask if `preview prism` would produce a `Just`. is :: forall s t a b r. HeytingAlgebra r => APrism s t a b -> s -> r is l = either (const ff) (const tt) <<< matching l +--| Ask if `preview prism` would produce a `Nothing`. isn't :: forall s t a b r. HeytingAlgebra r => APrism s t a b -> s -> r isn't l = not <<< is l diff --git a/src/Data/Lens/Traversal.purs b/src/Data/Lens/Traversal.purs index 7ba9d99..9109c58 100644 --- a/src/Data/Lens/Traversal.purs +++ b/src/Data/Lens/Traversal.purs @@ -1,12 +1,30 @@ --- | This module defines functions for working with traversals. +-- | `Traversal` is an optic that focuses on zero or more values. An +-- | `Array` would be a typical example: +-- | +-- | ```purescript +-- | over traversed negate [1, 2, 3] == [-1, -2, -3] +-- | preview traversed [1, 2, 3] == Just 1 +-- | firstOf traversed [1, 2, 3] == Just 1 -- same as `preview` +-- | lastOf traversed [1, 2, 3] == Just 3 +-- | ``` +-- | +-- | `view` might surprise you. It assumes that the wrapped values +-- | are a monoid, and `append`s them together: +-- | +-- | ```purescript +-- | view traversed ["D", "a", "w", "n"] == "Dawn" +-- | ``` +-- | +-- | Many of the functions you'll use are documented in `Data.Lens.Fold`. + module Data.Lens.Traversal ( traversed + , element , traverseOf , sequenceOf , failover , elementsOf , itraverseOf - , element , module ExportTypes ) where @@ -24,7 +42,12 @@ import Data.Traversable (class Traversable, traverse) import Data.Tuple (Tuple(..), uncurry) import Data.Newtype (under, unwrap) --- | Create a `Traversal` which traverses the elements of a `Traversable` functor. +-- | A `Traversal` for the elements of a `Traversable` functor. +-- | +-- | ```purescript +-- | over traversed negate [1, 2, 3] == [-1,-2,-3] +-- | over traversed negate (Just 3) == Just -3 +-- | ``` traversed :: forall t a b. Traversable t => Traversal (t a) (t b) a b traversed = wander traverse @@ -37,6 +60,29 @@ traverseOf = under Star -- | Sequence the foci of a `Traversal`, pulling out an `Applicative` effect. -- | If you do not need the result, see `sequenceOf_` for `Fold`s. +-- | +-- | `sequenceOf traversed` has the same result as `Data.Traversable.sequence`: +-- | +-- | ```purescript +-- | sequenceOf traversed (Just [1, 2]) == [Just 1, Just 2] +-- | sequence (Just [1, 2]) == [Just 1, Just 2] +-- | ``` +-- | +-- | An example with effects: +-- | ```purescript +-- | > array = [random, random] +-- | > :t array +-- | Array (Eff ... Number) +-- | +-- | > effect = sequenceOf traversed array +-- | > :t effect +-- | Eff ... (Array Number) +-- | +-- | > effect >>= logShow +-- | [0.15556037108154985,0.28500369615270515] +-- | unit +-- | ``` + sequenceOf :: forall f s t a . Applicative f @@ -56,7 +102,16 @@ failover t f s = case unwrap (t $ Star $ Tuple (Disj true) <<< f) s of Tuple (Disj true) x -> pure x Tuple (Disj false) _ -> empty --- | Affine traversal the `n`-th focus of a `Traversal`. +-- | Combine an index and a traversal to narrow the focus to a single +-- | element. Compare to `Data.Lens.Index`. +-- | +-- | ```purescript +-- | set (element 2 traversed) 8888 [0, 0, 3] == [0, 0, 8888] +-- | preview (element 2 traversed) [0, 0, 3] == Just 3 +-- | ``` +-- | The resulting traversal is called an *affine traversal*, which +-- | means that the traversal focuses on one or zero (if the index is out of range) +-- | results. element :: forall p s t a . Wander p