| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Cooked.Tweak.Modify
Description
This module exposes involved tweaks to selectively and systematically
modify elements of the same nature within a TxSkel
Synopsis
- data Branching
- = OneBranchForAllFoci
- | OneBranchPerFoci
- | OneBranchPerSubset
- | Manual (forall is. [is] -> [[is]])
- data ModifyTweakParams k k' is is' f a b c where
- ModifyTweakParams :: {..} -> ModifyTweakParams k k' is is' f a b c
- modifyTweakParamsAllIndexes :: Branching -> Optic' k is TxSkel a -> Optic k' is' a a b c -> (b -> f c) -> ModifyTweakParams k k' is is' f a b c
- modifyTweakParamsNoTypeChange :: Branching -> Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a
- modifyTweakParamsOneBranchForAllFoci :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a
- modifyTweakParamsOneBranchPerFoci :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a
- modifyTweakParamsOneBranchPerSubset :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a
- modifyTweak :: (Ord is, Is k A_Traversal, Members '[Tweak, NonDet] effs) => ([is] -> [[is]]) -> Optic' k (WithIx is) TxSkel x -> (is -> x -> Sem effs (x, l)) -> Sem effs [l]
- modifyTweakFromParams :: (Members '[Tweak, NonDet] effs, Is k A_Traversal, Is k' An_AffineTraversal, Foldable f, Alternative f) => ModifyTweakParams k k' is is' f a b c -> Sem effs [b]
- selectP :: (a -> Bool) -> Prism' a a
Modification parameters
How to combine the branches generated when several foci are eligible for
modification in the same skeleton. See modifyTweak.
Constructors
| OneBranchForAllFoci | Modify all eligible foci together, yielding a single modified skeleton. |
| OneBranchPerFoci | Modify exactly one eligible focus per branch. |
| OneBranchPerSubset | Modify every non-empty subset of the eligible foci, yielding one branch per subset (the power set, minus the empty set). |
| Manual (forall is. [is] -> [[is]]) | Create a new branch for all the subsets computed by the given function applied on the focused indexes. |
data ModifyTweakParams k k' is is' f a b c where Source #
The set of parameters piloting the modification tweak
Constructors
| ModifyTweakParams | |
Fields
| |
modifyTweakParamsAllIndexes :: Branching -> Optic' k is TxSkel a -> Optic k' is' a a b c -> (b -> f c) -> ModifyTweakParams k k' is is' f a b c Source #
A standard ModifyTweakParams without the index filtering
modifyTweakParamsNoTypeChange :: Branching -> Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a Source #
A standard ModifyTweakParams without any index filtering or type changing
inner optic
modifyTweakParamsOneBranchForAllFoci :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a Source #
A standard ModifyTweakParams without any index filtering or type changing
inner optic, modifying all foci in the same transaction.
modifyTweakParamsOneBranchPerFoci :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a Source #
A standard ModifyTweakParams without any index filtering or type changing
inner optic, branching on each focus.
modifyTweakParamsOneBranchPerSubset :: Optic' k is TxSkel a -> (a -> f a) -> ModifyTweakParams k An_Iso is NoIx f a a a Source #
A standard ModifyTweakParams without any index filtering or type changing
inner optic, branching on each subset of foci.
Modifying tweaks
Arguments
| :: (Ord is, Is k A_Traversal, Members '[Tweak, NonDet] effs) | |
| => ([is] -> [[is]]) | Function that explains which subsets of targeted indexes will be
simultaneously subject to being transformed. If you want to transform all
foci in a single transaction (assuming the transformation itself does not
branch), use |
| -> Optic' k (WithIx is) TxSkel x | Optic targeting the various foci which should be subject to being
transformed. This optic can be built manually, but can also be enlarged
using convenience functions such as |
| -> (is -> x -> Sem effs (x, l)) | Function that describes how the foci and their indexes can be transformed
within the structure. Bear in mind that |
| -> Sem effs [l] | Returns the list of all foci modified in the transaction, as they were before the modification was applied, represented by their label. In most cases, the label will be the element itself, but other use cases are allowed as the label is arbitrary data. |
When constructing a tweak from an optic and a modification of foci, there are in principle two options for optics with many foci: (a) apply the modification to all foci and return one modified transaction (b) generate a number of transactions that contain different combinations of modified and un-modified foci.
This function is the most general building block for both strategies: its
first argument selects, per transaction, which foci are modified together,
so it can realise strategy (a), strategy (b), or anything in between. The
overMods... helpers defined below specialise it to common cases. The
meaning of each argument and of the return value is documented on the
parameters themselves below.
Shared setup for the examples
Assume the optic has three foci, which we denote by a, b, c :: x, with
indices 1, 2, 3 :: Integer respectively.
Example 1: modify every focus in a single transaction
Choosing (: []) for the [is] -> [[is]] argument yields the single
grouping [[1, 2, 3]], so all foci are modified together. Assuming the
modification does not itself branch (changes returns exactly one result per
focus), this produces exactly one modified transaction, in which a, b,
and c are all modified. This is the grouping used by the
OneBranchForAllFoci Branching of modifyTweak.
Example 2: one modification per transaction
Now additionally assume that changes, of type is -> x -> Sem effs (x,
l), branches into 2, 3, and 5 results on a, b, and c respectively;
call those a1, a2 and b1, b2, b3 and c1, c2, c3, c4, c5. Choosing map
(: []) for the [is] -> [[is]] argument tries every modification on a
separate transaction, since
map (: []) [1, 2, 3] = [[1], [2], [3]] .
Thus there will be 2 + 3 + 5 = 10 modified transactions: for each element of
[a1, a2, b1, b2, b3, c1, c2, c3, c4, c5]
you get one modified transaction that includes that value in place of the
original focus. This is the grouping used by the OneBranchPerFoci
Branching of modifyTweak.
Example 3: all combinations of modifications
In the same setting, if you want to combine all possible modifications of one
focus with all possible modifications of the other foci, choose tail .
subsequences for the [is] -> [[is]] argument. This is the grouping used by
the OneBranchPerSubset Branching of modifyTweak. We have
tail (subsequences [1, 2, 3])
== [ [1], [2], [3],
[1, 2], [1, 3], [2, 3],
[1, 2, 3]
]This corresponds to the following 71 modified transactions, represented by the list of modified foci they contain:
[ -- one modified focus (the 10 cases from Example 2) [a1], [a2], ... [c4], [c5], -- two modifications of different foci (2*3 + 2*5 + 3*5 = 31 cases) [a1, b1], [a1, b2], ... [b3, c4], [b3, c5], -- three modified foci, one from each focus (2*3*5 = 30 cases) [a1, b1, c1], [a1, b1, c2], ... [a1, b3, c4], [a1, b3, c5] ]
So you see that tweaks constructed like this can branch quite wildly. Use with caution!
Note that if changes branches to no result for a targeted focus (one
whose index occurs in a grouping), that entire grouping branch is dropped,
since there is no possible value to put in place of that focus.
modifyTweakFromParams Source #
Arguments
| :: (Members '[Tweak, NonDet] effs, Is k A_Traversal, Is k' An_AffineTraversal, Foldable f, Alternative f) | |
| => ModifyTweakParams k k' is is' f a b c | The parameters piloting the modifications |
| -> Sem effs [b] | Returns the list of inner foci (as they were before modification) that were modified. |
The most convenient and expressive way to build a focusing-and-modifying
Tweak. It targets foci in a TxSkel through two optics and applies a
(possibly type-changing, possibly failing) modification to them, branching
over the eligible foci according to the requested Branching strategy.
Why two optics?
The underlying engine (modifyTweak) can only apply type-preserving
modifications: each modified focus is written back into the TxSkel in
place, so the skeleton's overall shape is fixed and the outer optic must be
an . That optic can therefore only decide where in
the skeleton to act; it cannot express a change of type.Optic' ... TxSkel a
The modification we actually want to perform is finer-grained and
type-changing: within each selected element a, an inner part of type b
is replaced by a value of type c. This type change cannot ride on the
outer, type-preserving optic, which is exactly why a second optic is needed.
opticIn localises and carries the type change inside each selected element,
and traverseOf folds it back into a type-preserving operation a -> f a,
so that the outer traversal stays type-preserving while the inner focus
still changes from b to c.
The second optic serves a second purpose: being an AffineTraversal (zero
or one focus), it doubles as an extra selection layer. We guard on
matching, so an element is only eligible when its inner focus actually
exists there. Together with the failure allowed by f in change
and the index predicate select, this gives several independent layers of
selection: outer optic, inner-optic existence, modification success, and
index.
Helper to build optics
selectP :: (a -> Bool) -> Prism' a a Source #
A modification that can fail is sometimes best expressed by explicitly
stating which property the foci should satisfy to be eligible for a
modification that cannot fail. selectP provides a prism to make such a
selection. The intended use case is overTweak (optic % selectP prop) mod
where optic gives the candidate foci, prop is the predicate to be
satisfied by the foci, and mod is the modification to be applied to the
selected foci.
Note that selectP is not a lawful prism: its build side is id, so
reviewing a value that does not satisfy prop breaks the
preview o (review o b) ≡ Just b law. We nevertheless keep it as a Prism'
(rather than the lawful but read-only filtered, which is an AffineFold)
because we need the write capability: composing a traversal
with a prism stays a traversal, whereas composing it with an AffineFold
collapses to a read-only fold. This is safe in the intended
overTweak (optic % selectP prop) mod pattern, where the unlawful build side
is never exercised: we only ever reach foci that already satisfy prop.