Skip to content

Commit 78ccfa9

Browse files
committed
A plethora of optics
1 parent 6a7f877 commit 78ccfa9

File tree

4 files changed

+121
-14
lines changed

4 files changed

+121
-14
lines changed

src/Control/Optics/Linear/Internal.hs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,48 @@ module Control.Optics.Linear.Internal
1313
, Iso, Iso'
1414
, Lens, Lens'
1515
, Prism, Prism'
16-
, Traversal, Traversal'
16+
, PTraversal, PTraversal'
17+
, DTraversal, DTraversal'
1718
-- * Composing optics
1819
, (.>)
1920
-- * Common optics
2021
, swap, assoc
2122
, _1, _2
2223
, _Left, _Right
2324
, _Just, _Nothing
24-
, traversed
25+
, ptraversed, dtraversed
26+
, both, both'
27+
, get', gets', set'
2528
-- * Using optics
2629
, get, set, gets
2730
, match, match', build
31+
, preview
2832
, over, over'
2933
, traverseOf, traverseOf'
3034
, lengthOf
3135
, withIso
36+
, toListOf
3237
-- * Constructing optics
33-
, iso, prism
38+
, iso, prism, lens
3439
)
3540
where
3641

3742
import qualified Data.Bifunctor.Linear as Bifunctor
3843
import Data.Bifunctor.Linear (SymmetricMonoidal)
3944
import Data.Functor.Const
4045
import Data.Functor.Linear
41-
import Data.Monoid
46+
import Data.Semigroup.Linear
4247
import Data.Profunctor.Linear
4348
import qualified Data.Profunctor.Kleisli.Linear as Linear
4449
import qualified Data.Profunctor.Kleisli.NonLinear as NonLinear
4550
import Data.Void
46-
import Prelude.Linear
51+
import Prelude.Linear hiding ((<$>))
52+
-- ^ XXX: not entirely sure why the hiding is necessary here...
4753
import qualified Prelude as P
4854

55+
-- TODO: documentation in this module
56+
-- Put the functions in some sensible order: possibly split into separate
57+
-- Lens/Prism/Traversal/Iso modules
4958
newtype Optic_ arr a b s t = Optical (a `arr` b -> s `arr` t)
5059

5160
type Optic c a b s t =
@@ -57,8 +66,12 @@ type Lens a b s t = Optic (Strong (,) ()) a b s t
5766
type Lens' a s = Lens a a s s
5867
type Prism a b s t = Optic (Strong Either Void) a b s t
5968
type Prism' a s = Prism a a s s
60-
type Traversal a b s t = Optic Wandering a b s t
61-
type Traversal' a s = Traversal a a s s
69+
type PTraversal a b s t = Optic PWandering a b s t
70+
type PTraversal' a s = PTraversal a a s s
71+
type DTraversal a b s t = Optic DWandering a b s t
72+
type DTraversal' a s = DTraversal a a s s
73+
-- XXX: these will unify into
74+
-- type Traversal (p :: Multiplicity) a b s t = Optic (Wandering p) a b s t
6275

6376
swap :: SymmetricMonoidal m u => Iso (a `m` b) (c `m` d) (b `m` a) (d `m` c)
6477
swap = iso Bifunctor.swap Bifunctor.swap
@@ -69,6 +82,10 @@ assoc = iso Bifunctor.lassoc Bifunctor.rassoc
6982
(.>) :: Optic_ arr a b s t -> Optic_ arr x y a b -> Optic_ arr x y s t
7083
Optical f .> Optical g = Optical (f P.. g)
7184

85+
-- c is the complement (probably)
86+
lens :: (s ->. (c,a)) -> ((c,b) ->. t) -> Lens a b s t
87+
lens sca cbt = Optical $ \f -> dimap sca cbt (second f)
88+
7289
prism :: (b ->. t) -> (s ->. Either t a) -> Prism a b s t
7390
prism b s = Optical $ \f -> dimap s (either id id) (second (rmap b f))
7491

@@ -78,6 +95,37 @@ _1 = Optical first
7895
_2 :: Lens a b (c,a) (c,b)
7996
_2 = Optical second
8097

98+
-- XXX: these will unify to
99+
-- > both :: forall (p :: Multiplicity). Traversal p a b (a,a) (b,b)
100+
both' :: PTraversal a b (a,a) (b,b)
101+
both' = _Pairing .> ptraversed
102+
103+
both :: DTraversal a b (a,a) (b,b)
104+
both = _Pairing .> dtraversed
105+
106+
-- XXX: these are a special case of Bitraversable, but just the simple case
107+
-- is included here for now
108+
_Pairing :: Iso (Pair a) (Pair b) (a,a) (b,b)
109+
_Pairing = iso Paired unpair
110+
111+
newtype Pair a = Paired (a,a)
112+
unpair :: Pair a ->. (a,a)
113+
unpair (Paired x) = x
114+
115+
instance P.Functor Pair where
116+
fmap f (Paired (x,y)) = Paired (f x, f y)
117+
instance Functor Pair where
118+
fmap f (Paired (x,y)) = Paired (f x, f y)
119+
instance Foldable Pair where
120+
foldMap f (Paired (x,y)) = f x P.<> f y
121+
instance P.Traversable Pair where
122+
traverse f (Paired (x,y)) = Paired P.<$> ((,) P.<$> f x P.<*> f y)
123+
instance Traversable Pair where
124+
traverse f (Paired (x,y)) = Paired <$> ((,) <$> f x <*> f y)
125+
126+
toListOf :: Optic_ (NonLinear.Kleisli (Const [a])) a b s t -> s -> [a]
127+
toListOf l = gets l (\a -> [a])
128+
81129
_Left :: Prism a b (Either a c) (Either b c)
82130
_Left = Optical first
83131

@@ -90,8 +138,11 @@ _Just = prism Just (maybe (Left Nothing) Right)
90138
_Nothing :: Prism' () (Maybe a)
91139
_Nothing = prism (\() -> Nothing) Left
92140

93-
traversed :: Traversable t => Traversal a b (t a) (t b)
94-
traversed = Optical wander
141+
ptraversed :: P.Traversable t => PTraversal a b (t a) (t b)
142+
ptraversed = Optical pwander
143+
144+
dtraversed :: Traversable t => DTraversal a b (t a) (t b)
145+
dtraversed = Optical dwander
95146

96147
over :: Optic_ LinearArrow a b s t -> (a ->. b) -> s ->. t
97148
over (Optical l) f = getLA (l (LA f))
@@ -105,6 +156,18 @@ get l = gets l P.id
105156
gets :: Optic_ (NonLinear.Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
106157
gets (Optical l) f s = getConst' (NonLinear.runKleisli (l (NonLinear.Kleisli (Const P.. f))) s)
107158

159+
preview :: Optic_ (NonLinear.Kleisli (Const (Maybe (First a)))) a b s t -> s -> Maybe a
160+
preview (Optical l) s = getFirst P.<$> (getConst (NonLinear.runKleisli (l (NonLinear.Kleisli (\a -> Const (Just (First a))))) s))
161+
162+
get' :: Optic_ (Linear.Kleisli (Const (Top, a))) a b s t -> s ->. (Top, a)
163+
get' l = gets' l id
164+
165+
gets' :: Optic_ (Linear.Kleisli (Const (Top, r))) a b s t -> (a ->. r) -> s ->. (Top, r)
166+
gets' (Optical l) f s = getConst' (Linear.runKleisli (l (Linear.Kleisli (\a -> Const (mempty, f a)))) s)
167+
168+
set' :: Optic_ (Linear.Kleisli (MyFunctor a b)) a b s t -> s ->. b ->. (a, t)
169+
set' (Optical l) = runMyFunctor . Linear.runKleisli (l (Linear.Kleisli (\a -> MyFunctor (\b -> (a,b)))))
170+
108171
set :: Optic_ (->) a b s t -> b -> s -> t
109172
set (Optical l) x = l (const x)
110173

src/Data/Profunctor/Kleisli/Linear.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ instance Control.Applicative f => Strong Either Void (Kleisli f) where
4242
first (Kleisli f) = Kleisli (either (Data.fmap Left . f) (Control.pure . Right))
4343
second (Kleisli g) = Kleisli (either (Control.pure . Left) (Data.fmap Right . g))
4444

45-
instance Control.Applicative f => Wandering (Kleisli f) where
46-
wander (Kleisli f) = Kleisli (Data.traverse f)
45+
instance Control.Applicative f => DWandering (Kleisli f) where
46+
dwander (Kleisli f) = Kleisli (Data.traverse f)
4747

4848
-- | Linear co-Kleisli arrows for the comonad `w`. These arrows are still
4949
-- useful in the case where `w` is not a comonad however, and some

src/Data/Profunctor/Kleisli/NonLinear.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ instance Prelude.Applicative f => Strong Either Void (Kleisli f) where
3232
first (Kleisli f) = Kleisli $ \case
3333
Left x -> Prelude.fmap Left (f x)
3434
Right y -> Prelude.pure (Right y)
35+
36+
instance Prelude.Applicative f => PWandering (Kleisli f) where
37+
pwander (Kleisli f) = Kleisli (Prelude.traverse f)

src/Data/Profunctor/Linear.hs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
1+
{-# LANGUAGE GADTs #-}
12
{-# LANGUAGE FlexibleContexts #-}
3+
{-# LANGUAGE FlexibleInstances #-}
24
{-# LANGUAGE KindSignatures #-}
35
{-# LANGUAGE LinearTypes #-}
46
{-# LANGUAGE MultiParamTypeClasses #-}
57
{-# LANGUAGE NoImplicitPrelude #-}
8+
{-# LANGUAGE RankNTypes #-}
69
{-# LANGUAGE TupleSections #-}
710
{-# LANGUAGE TypeOperators #-}
811

12+
{-# OPTIONS_GHC -fno-warn-orphans #-}
13+
914
module Data.Profunctor.Linear
1015
( Profunctor(..)
1116
, Monoidal(..)
1217
, Strong(..)
13-
, Wandering(..)
18+
, PWandering(..)
19+
, DWandering(..)
1420
, LinearArrow(..), getLA
1521
, Exchange(..)
22+
, Top
23+
, MyFunctor(..), runMyFunctor
1624
) where
1725

1826
import qualified Data.Functor.Linear as Data
27+
import qualified Control.Monad.Linear as Control
1928
import Data.Bifunctor.Linear hiding (first, second)
2029
import Prelude.Linear
2130
import Data.Void
31+
import qualified Prelude
32+
import Data.Semigroup.Linear
33+
import Data.Functor.Const
2234

2335
-- TODO: write laws
2436

@@ -52,8 +64,17 @@ class (SymmetricMonoidal m u, Profunctor arr) => Strong m u arr where
5264
second arr = dimap swap swap (first arr)
5365
{-# INLINE second #-}
5466

55-
class (Strong (,) () arr, Strong Either Void arr) => Wandering arr where
56-
wander :: Data.Traversable f => a `arr` b -> f a `arr` f b
67+
-- XXX: Just as Prelude.Functor/Data.Functor will combine into
68+
-- > `class Functor (p :: Multiplicity) f`
69+
-- so will Traversable, and then we would instead write
70+
-- > class (...) => Wandering (p :: Multiplicity) arr where
71+
-- > wander :: Traversable p f => a `arr` b -> f a `arr` f b
72+
-- For now, however, we cannot do this, so we use two classes instead:
73+
-- PreludeWandering and DataWandering
74+
class (Strong (,) () arr, Strong Either Void arr) => PWandering arr where
75+
pwander :: Prelude.Traversable f => a `arr` b -> f a `arr` f b
76+
class (Strong (,) () arr, Strong Either Void arr) => DWandering arr where
77+
dwander :: Data.Traversable f => a `arr` b -> f a `arr` f b
5778

5879
---------------
5980
-- Instances --
@@ -82,7 +103,27 @@ instance Strong (,) () (->) where
82103
instance Strong Either Void (->) where
83104
first f (Left x) = Left (f x)
84105
first _ (Right y) = Right y
106+
instance PWandering (->) where
107+
pwander = Prelude.fmap
85108

86109
data Exchange a b s t = Exchange (s ->. a) (b ->. t)
87110
instance Profunctor (Exchange a b) where
88111
dimap f g (Exchange p q) = Exchange (p . f) (g . q)
112+
113+
instance Control.Functor (Const (Top, a)) where
114+
fmap f (Const (t, x)) = Const (throw f <> t, x)
115+
instance Monoid a => Control.Applicative (Const (Top, a)) where
116+
pure x = Const (throw x, mempty)
117+
Const x <*> Const y = Const (x <> y)
118+
119+
-- TODO: pick a more sensible name for this
120+
newtype MyFunctor a b t = MyFunctor (b ->. (a, t))
121+
runMyFunctor :: MyFunctor a b t ->. b ->. (a, t)
122+
runMyFunctor (MyFunctor f) = f
123+
124+
instance Data.Functor (MyFunctor a b) where
125+
fmap f (MyFunctor g) = MyFunctor (getLA (second (LA f)) . g)
126+
instance Control.Functor (MyFunctor a b) where
127+
fmap f (MyFunctor g) = MyFunctor (thing f . g)
128+
where thing :: (c ->. d) ->. (e, c) ->. (e, d)
129+
thing k (x,y) = (x, k y)

0 commit comments

Comments
 (0)