···11+{
22+ "cells": [
33+ {
44+ "cell_type": "code",
55+ "execution_count": 1,
66+ "metadata": {},
77+ "outputs": [],
88+ "source": [
99+ ":set -XRankNTypes\n",
1010+ ":set -XTypeOperators\n",
1111+ ":set -XKindSignatures\n",
1212+ ":set -XInstanceSigs\n",
1313+ ":set -XLambdaCase"
1414+ ]
1515+ },
1616+ {
1717+ "cell_type": "code",
1818+ "execution_count": 2,
1919+ "metadata": {},
2020+ "outputs": [],
2121+ "source": [
2222+ "f :: a -> b\n",
2323+ "f = undefined"
2424+ ]
2525+ },
2626+ {
2727+ "cell_type": "code",
2828+ "execution_count": 3,
2929+ "metadata": {},
3030+ "outputs": [],
3131+ "source": [
3232+ "type P = (->)"
3333+ ]
3434+ },
3535+ {
3636+ "cell_type": "code",
3737+ "execution_count": 4,
3838+ "metadata": {},
3939+ "outputs": [],
4040+ "source": [
4141+ "\n",
4242+ "\n",
4343+ "f' :: a `P` b\n",
4444+ "f' = undefined\n",
4545+ "\n",
4646+ "f'' :: P a b\n",
4747+ "f'' = undefined\n",
4848+ "\n",
4949+ "generateA :: i -> a\n",
5050+ "generateA = undefined\n",
5151+ "\n",
5252+ "useB :: b -> o\n",
5353+ "useB = undefined\n",
5454+ "\n",
5555+ "f''' :: (i -> a) -> (b -> o) -> P a b -> P i o\n",
5656+ "f''' g u f = u . f . g\n",
5757+ "\n",
5858+ "class Profunctor (p :: * -> * -> *) where\n",
5959+ " dimap :: (i -> a) -> (b -> o) -> p a b -> p i o \n",
6060+ " \n",
6161+ "instance Profunctor (->) where\n",
6262+ " dimap g u f = u . f . g\n",
6363+ "\n"
6464+ ]
6565+ },
6666+ {
6767+ "cell_type": "markdown",
6868+ "metadata": {},
6969+ "source": [
7070+ "So how to get a `Functor` be a `Profunctor`. For that we need a new data type that somehow talks about `Functor f`, the type contained within `f` i.e `a`, and the other type which is required for `Profunctor` i.e. `b`. Let's call it `Star` (we will get to the reason later)"
7171+ ]
7272+ },
7373+ {
7474+ "cell_type": "code",
7575+ "execution_count": 5,
7676+ "metadata": {},
7777+ "outputs": [],
7878+ "source": [
7979+ "\n",
8080+ "-- if you look closely, a here is in covariant position and b in contra\n",
8181+ "newtype Star f a b = Star {runStar :: a -> f b} \n",
8282+ "\n",
8383+ "-- now lets lift the Functor \n",
8484+ "instance Functor f => Profunctor (Star f) where\n",
8585+ " dimap :: (i -> a) -> (b -> o) -> Star f a b -> Star f i o\n",
8686+ " dimap f g s = Star (fmap g . runStar s . f)"
8787+ ]
8888+ },
8989+ {
9090+ "cell_type": "markdown",
9191+ "metadata": {},
9292+ "source": [
9393+ "So `Star` is another `Profunctor` like `(->)`. Let's just see one more. Like `Star` which uses all three type variables `f`, `a`, and `b`, this one will take 3 but use only 2 in a way that will enable us to make a `Profunctor` out of it. Since it forgets a type in its implementation, we will call it `Forget`"
9494+ ]
9595+ },
9696+ {
9797+ "cell_type": "code",
9898+ "execution_count": 6,
9999+ "metadata": {},
100100+ "outputs": [],
101101+ "source": [
102102+ "newtype Forget f a b = Forget {runForget :: a -> f} -- again, a here is covariant, f is contravariant, and b is forgotten\n",
103103+ "\n",
104104+ "-- hence for it to be a Profunctor we don't need the Functor constraint like Star\n",
105105+ "instance Profunctor (Forget f) where\n",
106106+ " dimap :: (i -> a) -> (b -> o) -> Forget f a b -> Forget f i o\n",
107107+ " dimap f _ fo = Forget (runForget fo . f)\n"
108108+ ]
109109+ },
110110+ {
111111+ "cell_type": "markdown",
112112+ "metadata": {},
113113+ "source": [
114114+ "As `functors` provide ways to map objects of a container (read a data structure), `natural transformations` provide ways to map `functors` i.e `type Natural f g = forall a. f a -> g a`. Of course, `natural transformations` need to adhere to some laws. For `profunctors`, `natural transformations` happen in couple of ways (one loose way to reason it is as profunctors deal with 2 type parameters)."
115115+ ]
116116+ },
117117+ {
118118+ "cell_type": "markdown",
119119+ "metadata": {},
120120+ "source": [
121121+ "**First**, is when both type parameters are lifted to a `product` type i.e. `p a b |-> p (a, c) (b, c)` or `p a b |-> p (c, a) (c, b)`. Here, the type parameter `c` can be considered to be providing additional information as a result of the transformation. It's encoded in a type class named `Strong`."
122122+ ]
123123+ },
124124+ {
125125+ "cell_type": "code",
126126+ "execution_count": 7,
127127+ "metadata": {},
128128+ "outputs": [],
129129+ "source": [
130130+ "class Profunctor p => Strong p where\n",
131131+ " first' :: p a b -> p (a, c) (b, c)\n",
132132+ " second' :: p a b -> p (c, a) (c, b)\n",
133133+ " \n",
134134+ "-- let's lift (->) to be a Strong one\n",
135135+ "instance Strong (->) where\n",
136136+ " first' f = \\(a, c) -> (f a, c)\n",
137137+ " second' f = \\(c, a) -> (c, f a)\n",
138138+ " \n",
139139+ "-- let's lift Star to be a Strong one\n",
140140+ "instance Functor f => Strong (Star f) where\n",
141141+ " first' :: Star f a b -> Star f (a, c) (b, c)\n",
142142+ " first' s = Star (\\(a, c) -> fmap (\\b -> (b, c)) $ runStar s a)\n",
143143+ " \n",
144144+ " second' :: Star f a b -> Star f (c, a) (c, b)\n",
145145+ " second' s = Star (\\(c, a) -> fmap (\\b -> (c, b)) $ runStar s a)\n",
146146+ " \n",
147147+ "-- simillarly, let's lift Forget to be a Strong one\n",
148148+ "instance Strong (Forget f) where\n",
149149+ " first' fo = Forget (runForget fo . fst)\n",
150150+ " second' fo = Forget (runForget fo . snd)"
151151+ ]
152152+ },
153153+ {
154154+ "cell_type": "markdown",
155155+ "metadata": {},
156156+ "source": [
157157+ "__Second__, when both type parameters are lifted to a `sum` type i.e. `p a b -> p (Either a c) (Either b c)` or `p a b -> p (Either c a) (Either c b)`. Here the type parameter `c` can be considered to be `maybe` providing additional information. It's encoded in a type class named `Choice`"
158158+ ]
159159+ },
160160+ {
161161+ "cell_type": "code",
162162+ "execution_count": 8,
163163+ "metadata": {},
164164+ "outputs": [],
165165+ "source": [
166166+ "\n",
167167+ "class Profunctor p => Choice p where\n",
168168+ " left' :: p a b -> p (Either a c) (Either b c)\n",
169169+ " right' :: p a b -> p (Either c a) (Either c b)\n",
170170+ " \n",
171171+ "instance Choice (->) where\n",
172172+ " left' f (Left a) = Left (f a)\n",
173173+ " left' _ (Right c) = Right c\n",
174174+ " \n",
175175+ " right' = fmap\n",
176176+ " \n",
177177+ "-- we need Applicative constraint to lift Right\n",
178178+ "instance Applicative f => Choice (Star f) where\n",
179179+ " left' (Star s) = Star $ either (fmap Left . s) (pure . Right)\n",
180180+ " \n",
181181+ " right' (Star s) = Star $ either (pure . Left) (fmap Right . s)\n",
182182+ " \n",
183183+ "-- we need Monoid constraint to get mempty for the forgotten case\n",
184184+ "instance Monoid fo => Choice (Forget fo) where\n",
185185+ " left' (Forget fo) = Forget $ either fo (const mempty)\n",
186186+ " \n",
187187+ " right' (Forget fo) = Forget $ either (const mempty) fo"
188188+ ]
189189+ },
190190+ {
191191+ "cell_type": "markdown",
192192+ "metadata": {},
193193+ "source": [
194194+ "`Strong` and `Choice` above are also seen as refinements of `Profunctor` which concerns its interaction with sum and product types."
195195+ ]
196196+ },
197197+ {
198198+ "cell_type": "raw",
199199+ "metadata": {},
200200+ "source": [
201201+ "There is a **third** refinement of `Profunctor`s that concerns with composition (esp. parallel composition) of individual `Profunctor`s. It's encoded in a type class named `Monoidal`. The name is such as the operations of the `Monoidal` type class satisfy laws that make them isomorphic with monoids on the value types."
202202+ ]
203203+ },
204204+ {
205205+ "cell_type": "code",
206206+ "execution_count": 9,
207207+ "metadata": {},
208208+ "outputs": [],
209209+ "source": [
210210+ "cross :: (a -> c) -> (b -> d) -> (a , b) -> (c, d)\n",
211211+ "cross f g (a, c) = (f a, g c)\n",
212212+ "\n",
213213+ "class Profunctor p => Monoidal p where\n",
214214+ " par :: p a b -> p c d -> p (a,c) (b,d)\n",
215215+ " empty :: p () ()\n",
216216+ " \n",
217217+ "instance Monoidal (->) where\n",
218218+ " empty = id\n",
219219+ " par = cross\n",
220220+ " \n",
221221+ "instance Applicative f => Monoidal (Star f) where\n",
222222+ " empty = Star pure\n",
223223+ " par (Star f) (Star g) = Star (\\(a,c) -> (,) <$> f a <*> g c)\n",
224224+ " \n",
225225+ "instance Monoid f => Monoidal (Forget f) where\n",
226226+ " empty = Forget (const mempty)\n",
227227+ " par (Forget f) (Forget g) = Forget (\\(a,c) -> f a <> g c)"
228228+ ]
229229+ },
230230+ {
231231+ "cell_type": "markdown",
232232+ "metadata": {},
233233+ "source": [
234234+ "#### Profunctor Optics"
235235+ ]
236236+ },
237237+ {
238238+ "cell_type": "code",
239239+ "execution_count": 10,
240240+ "metadata": {},
241241+ "outputs": [],
242242+ "source": [
243243+ "type Optic p a b s t = p a b -> p s t"
244244+ ]
245245+ },
246246+ {
247247+ "cell_type": "markdown",
248248+ "metadata": {},
249249+ "source": [
250250+ "Profunctor Optics : Modular Data Accessors. Matthew Pickering, Jeremy Gibbons, and Nicolas Wua\n",
251251+ "\n",
252252+ "> When `S` is a composite type with some component of type `A`, and `T` similarly a composite type in which that component has type `B`, and `P` is some notion of `transformer`, then we can think of a data accessor of type `Optic P A B S T` as lifting a component transformer of type `P A B` to a whole-structure transformer of type `P S T`. We will retrieve equivalents of our original definitions of lens, prism, and so on by placing various constraints on `P`"
253253+ ]
254254+ },
255255+ {
256256+ "cell_type": "markdown",
257257+ "metadata": {},
258258+ "source": [
259259+ "**Adapters**\n",
260260+ "\n",
261261+ "Adapters are the concrete representation of optics in which the things which is viewed or matched is the whole structure. hence it just need to pair of functions i.e. one for view/match `S -> A` and one for update/build `B -> T`"
262262+ ]
263263+ },
264264+ {
265265+ "cell_type": "code",
266266+ "execution_count": 11,
267267+ "metadata": {},
268268+ "outputs": [],
269269+ "source": [
270270+ "\n",
271271+ "\n",
272272+ "data Adapter a b s t = Adapter {from :: s -> a, to :: b -> t}\n",
273273+ "-- profunctor constraint to note that polymorphically the notion of an abstracter is simply a function from `P A B` to `P S T`\n",
274274+ "type AdapterP a b s t = forall p. Profunctor p => Optic p a b s t"
275275+ ]
276276+ },
277277+ {
278278+ "cell_type": "markdown",
279279+ "metadata": {},
280280+ "source": [
281281+ "Establishing isomorphism between `Adapter` and `AdapterP`"
282282+ ]
283283+ },
284284+ {
285285+ "cell_type": "code",
286286+ "execution_count": 12,
287287+ "metadata": {},
288288+ "outputs": [],
289289+ "source": [
290290+ "-- the type is equivalent to\n",
291291+ "-- Profunctor p => Adapter a b s t -> p a b -> p s t\n",
292292+ "adapterC2P :: Adapter a b s t -> AdapterP a b s t\n",
293293+ "adapterC2P (Adapter f t) = dimap f t\n",
294294+ "\n",
295295+ "instance Profunctor (Adapter a b) where\n",
296296+ " dimap :: (i -> c) -> (d -> o) -> Adapter a b c d -> Adapter a b i o\n",
297297+ " dimap f g (Adapter r s) = Adapter (r . f) (g . s)\n",
298298+ "\n",
299299+ "-- the type is equivalent to\n",
300300+ "-- Profunctor p q => p a b -> p s t -> Adapter a b s t\n",
301301+ "adapterP2C :: AdapterP a b s t -> Adapter a b s t\n",
302302+ "adapterP2C f = f (Adapter id id)"
303303+ ]
304304+ },
305305+ {
306306+ "cell_type": "markdown",
307307+ "metadata": {},
308308+ "source": [
309309+ "**Lens**\n",
310310+ "\n",
311311+ "Lenses provide ways to\n",
312312+ "\n",
313313+ "- _view_ the contained type `A` inside the container type `S` and \n",
314314+ "- _update_ the container type `S` to a container type `T` having elements of contained type `B`\n"
315315+ ]
316316+ },
317317+ {
318318+ "cell_type": "code",
319319+ "execution_count": 13,
320320+ "metadata": {},
321321+ "outputs": [],
322322+ "source": [
323323+ "data Lens a b s t = Lens {view :: s -> a, update :: (b, s) -> t}\n",
324324+ "\n",
325325+ "-- Strong profunctors produce the notion of lenses as there is product type involved here\n",
326326+ "type LensP a b s t = forall p. Strong p => Optic p a b s t"
327327+ ]
328328+ },
329329+ {
330330+ "cell_type": "markdown",
331331+ "metadata": {},
332332+ "source": [
333333+ "Establishing isomorphism between `Lens` and `LensP`"
334334+ ]
335335+ },
336336+ {
337337+ "cell_type": "code",
338338+ "execution_count": 14,
339339+ "metadata": {},
340340+ "outputs": [],
341341+ "source": [
342342+ "lensC2P :: Lens a b s t -> LensP a b s t\n",
343343+ "lensC2P (Lens v u) = dimap (\\s -> (v s, s)) u . first'\n",
344344+ "\n",
345345+ "instance Profunctor (Lens a b) where\n",
346346+ " dimap :: (i -> c) -> (d -> o) -> Lens a b c d -> Lens a b i o\n",
347347+ " dimap f g (Lens v u) = Lens (v . f) (\\(b,i) -> g $ u (b, f i))\n",
348348+ "\n",
349349+ "instance Strong (Lens a b) where\n",
350350+ " first' :: Lens a b c d -> Lens a b (c, e) (d, e)\n",
351351+ " first' (Lens v u) = Lens (v . fst) (\\(b, (c, e)) -> (u (b, c), e))\n",
352352+ " \n",
353353+ " second' :: Lens a b c d -> Lens a b (e, c) (e, d)\n",
354354+ " second' (Lens v u) = Lens (v . snd) (\\(b, (e, c)) -> (e, u (b, c)))\n",
355355+ "\n",
356356+ "lensP2C :: LensP a b s t -> Lens a b s t\n",
357357+ "lensP2C f = f (Lens id fst)"
358358+ ]
359359+ },
360360+ {
361361+ "cell_type": "markdown",
362362+ "metadata": {},
363363+ "source": [
364364+ "**Prisms**\n",
365365+ "\n",
366366+ "Prisms provide ways to\n",
367367+ " - _match_ the probable contained type `A` (and may be transform it to `B`) inside the compound type `S` (if no match then it reverts to `S` or may be transform to `T`) and \n",
368368+ " - _build_ the compound type `T` from one of the contained types `B`\n"
369369+ ]
370370+ },
371371+ {
372372+ "cell_type": "code",
373373+ "execution_count": 15,
374374+ "metadata": {},
375375+ "outputs": [],
376376+ "source": [
377377+ "data Prism a b s t = Prism {match :: s -> Either t a, build :: b -> t}\n",
378378+ "\n",
379379+ "-- Choice profunctors produce the notion of prisms as there is sum type involved here\n",
380380+ "type PrismP a b s t = forall p. Choice p => Optic p a b s t"
381381+ ]
382382+ },
383383+ {
384384+ "cell_type": "markdown",
385385+ "metadata": {},
386386+ "source": [
387387+ "Establishing isomorphism between `Prism` and `PrismP`"
388388+ ]
389389+ },
390390+ {
391391+ "cell_type": "code",
392392+ "execution_count": 16,
393393+ "metadata": {},
394394+ "outputs": [],
395395+ "source": [
396396+ "prismC2P :: Prism a b s t -> PrismP a b s t\n",
397397+ "prismC2P (Prism m b) = dimap m (either id b) . right'\n",
398398+ "\n",
399399+ "instance Profunctor (Prism a b) where\n",
400400+ " dimap :: (i -> c) -> (d -> o) -> Prism a b c d -> Prism a b i o\n",
401401+ " dimap f g (Prism m b) = Prism (either (Left . g) (Right) . m . f) (g . b)\n",
402402+ " \n",
403403+ "instance Choice (Prism a b) where\n",
404404+ " left' :: Prism a b c d -> Prism a b (Either c e) (Either d e)\n",
405405+ " left' (Prism m b) = Prism (either (either (Left . Left) Right . m) (Left . Right))\n",
406406+ " (Left . b)\n",
407407+ " \n",
408408+ " right' :: Prism a b c d -> Prism a b (Either e c) (Either e d)\n",
409409+ " right' (Prism m b) = Prism (either (Left . Left) (either (Left . Right) Right . m))\n",
410410+ " (Right . b)\n",
411411+ "\n",
412412+ "prismP2C :: PrismP a b s t -> Prism a b s t\n",
413413+ "prismP2C f = f (Prism Right id)"
414414+ ]
415415+ },
416416+ {
417417+ "cell_type": "markdown",
418418+ "metadata": {},
419419+ "source": [
420420+ "**Traversals**\n",
421421+ "\n",
422422+ "- traversal datatype is a container with finite number of elements having an ordering. It can be _traversed_ whereby each element is visited in the given order\n",
423423+ "\n",
424424+ "- a container type `S` with elements of type `A` is a _traversal_ when there exists types `B`, `T`, and a traversal function of type `(A -> F B) -> (S -> F T)` for each applicative functor `F` (additionally satisfying some traversal laws)\n",
425425+ "\n",
426426+ "- as a **generalisation of lenses and prisms**, traversals provides access not just to a single component within a whole structure but onto an entire sequence of such components\n",
427427+ "\n",
428428+ "- traversal type `S` is equivalent to `∃n. A^n x (B^n -> T)`, where `n` is the number of elements in `S`. This tupling represents both yielding of sequence of elements inside container in the order mentioned and also replacing the container with a sequence of new elements\n",
429429+ "\n",
430430+ "- above equivalence can be captured as a datatype"
431431+ ]
432432+ },
433433+ {
434434+ "cell_type": "code",
435435+ "execution_count": 17,
436436+ "metadata": {},
437437+ "outputs": [],
438438+ "source": [
439439+ "data FunList a b t = Done t | More a (FunList a b (b -> t))\n",
440440+ "\n",
441441+ "instance Functor (FunList a b) where\n",
442442+ " fmap f (Done t) = Done $ f t\n",
443443+ " fmap f (More x l) = More x (fmap (f .) l)\n",
444444+ " \n",
445445+ "instance Applicative (FunList a b) where\n",
446446+ " pure = Done\n",
447447+ " Done f <*> f' = fmap f f'\n",
448448+ " More x l <*> f' = More x (flip <$> l <*> f')"
449449+ ]
450450+ },
451451+ {
452452+ "cell_type": "markdown",
453453+ "metadata": {},
454454+ "source": [
455455+ "This datatype also is isomorphic to `Either T (A , FunList A B (B -> T))`"
456456+ ]
457457+ },
458458+ {
459459+ "cell_type": "code",
460460+ "execution_count": 18,
461461+ "metadata": {},
462462+ "outputs": [],
463463+ "source": [
464464+ "out :: FunList a b t -> Either t (a, FunList a b (b -> t))\n",
465465+ "out (Done t) = Left t\n",
466466+ "out (More a l) = Right (a, l)\n",
467467+ "\n",
468468+ "inn :: Either t (a, FunList a b (b -> t)) -> FunList a b t\n",
469469+ "inn = either Done (\\(a,l) -> More a l)"
470470+ ]
471471+ },
472472+ {
473473+ "cell_type": "markdown",
474474+ "metadata": {},
475475+ "source": [
476476+ "Based on the above isomorphism, it can be proved that traversal function of type `(A -> F B) -> (S -> F T)` for each applicative functor `F` yields an isomorphism `S ≍ FunList A B T`\n",
477477+ "\n",
478478+ "The definition of concrete traversal then is"
479479+ ]
480480+ },
481481+ {
482482+ "cell_type": "code",
483483+ "execution_count": 22,
484484+ "metadata": {},
485485+ "outputs": [],
486486+ "source": [
487487+ "newtype Traversal a b s t = Traversal {extract::s -> FunList a b t}\n",
488488+ "\n",
489489+ "-- traversal being the generalisation, we need all notions of sum, product, and empty\n",
490490+ "type TraversalP a b s t = forall p. (Strong p, Choice p, Monoidal p) => Optic p a b s t"
491491+ ]
492492+ },
493493+ {
494494+ "cell_type": "markdown",
495495+ "metadata": {},
496496+ "source": [
497497+ "To establish the isomorphism we need a notion of `traverse` that lifts a transformation of `A`s to `B`s to act on each of the elements of `FunList`"
498498+ ]
499499+ },
500500+ {
501501+ "cell_type": "code",
502502+ "execution_count": 27,
503503+ "metadata": {},
504504+ "outputs": [],
505505+ "source": [
506506+ "traverse :: (Choice p, Monoidal p) => p a b -> p (FunList a c t) (FunList b c t)\n",
507507+ "traverse k = dimap out inn (right' $ par k $ traverse k)"
508508+ ]
509509+ },
510510+ {
511511+ "cell_type": "code",
512512+ "execution_count": 52,
513513+ "metadata": {},
514514+ "outputs": [],
515515+ "source": [
516516+ "fuse :: FunList b b t -> t\n",
517517+ "fuse (Done t) = t\n",
518518+ "fuse (More x l) = fuse l x\n",
519519+ "\n",
520520+ "single :: a -> FunList a b b\n",
521521+ "single x = More x (Done id)\n",
522522+ "\n",
523523+ "traversalC2P :: Traversal a b s t -> TraversalP a b s t\n",
524524+ "traversalC2P (Traversal f) p = dimap f fuse (traverse p)\n",
525525+ "\n",
526526+ "instance Profunctor (Traversal a b) where\n",
527527+ " dimap :: (i -> c) -> (d -> o) -> Traversal a b c d -> Traversal a b i o\n",
528528+ " dimap f g (Traversal h) = Traversal (fmap g . h . f)\n",
529529+ "\n",
530530+ "instance Strong (Traversal a b) where\n",
531531+ " first' :: Traversal a b c d -> Traversal a b (c, e) (d, e)\n",
532532+ " first' (Traversal s) = Traversal (\\(c,e) -> (,) <$> s c <*> pure e)\n",
533533+ " \n",
534534+ " second' :: Traversal a b c d -> Traversal a b (e, c) (e, d)\n",
535535+ " second' (Traversal s) = Traversal (\\(e, c) -> (,) <$> pure e <*> s c) \n",
536536+ " \n",
537537+ "instance Choice (Traversal a b) where\n",
538538+ " left' :: Traversal a b c d -> Traversal a b (Either c e) (Either d e)\n",
539539+ " left' (Traversal s) = Traversal (either (fmap Left . s) (fmap Right . pure))\n",
540540+ " \n",
541541+ " right' :: Traversal a b c d -> Traversal a b (Either e c) (Either e d)\n",
542542+ " right' (Traversal s) = Traversal (either (fmap Left . pure) (fmap Right . s))\n",
543543+ " \n",
544544+ "instance Monoidal (Traversal a b) where\n",
545545+ " empty :: Traversal a b () ()\n",
546546+ " empty = Traversal pure\n",
547547+ " \n",
548548+ " par :: Traversal a b c d -> Traversal a b e f -> Traversal a b (c, e) (d, f)\n",
549549+ " par (Traversal s) (Traversal t) = Traversal (\\(c,e) -> (,) <$> s c <*> t e)\n",
550550+ "\n",
551551+ "traversalP2C :: TraversalP a b s t -> Traversal a b s t\n",
552552+ "traversalP2C f = f $ Traversal single"
553553+ ]
554554+ },
555555+ {
556556+ "cell_type": "code",
557557+ "execution_count": 54,
558558+ "metadata": {},
559559+ "outputs": [],
560560+ "source": [
561561+ "traverseOf :: TraversalP a b s t -> (forall f. Applicative f => (a -> f b) -> (s -> f t))\n",
562562+ "traverseOf p f = runStar $ p $ Star f "
563563+ ]
564564+ }
565565+ ],
566566+ "metadata": {
567567+ "kernelspec": {
568568+ "display_name": "Haskell",
569569+ "language": "haskell",
570570+ "name": "haskell"
571571+ },
572572+ "language_info": {
573573+ "codemirror_mode": "ihaskell",
574574+ "file_extension": ".hs",
575575+ "name": "haskell",
576576+ "pygments_lexer": "Haskell",
577577+ "version": "8.4.4"
578578+ }
579579+ },
580580+ "nbformat": 4,
581581+ "nbformat_minor": 2
582582+}