Skip to content

Commit

Permalink
Merge pull request #89 from marick/master
Browse files Browse the repository at this point in the history
API documentation for `Prism` and `Traversal`
  • Loading branch information
marick committed May 15, 2018
2 parents 02b1693 + 0397cd7 commit 2d10428
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 12 deletions.
140 changes: 132 additions & 8 deletions 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

Expand All @@ -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

Expand All @@ -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
63 changes: 59 additions & 4 deletions 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

Expand All @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 2d10428

Please sign in to comment.