Skip to content

Commit abfc7b4

Browse files
committed
A plethora of new optics
todo: split the commit up a bit
1 parent 035f063 commit abfc7b4

File tree

5 files changed

+175
-14
lines changed

5 files changed

+175
-14
lines changed

src/Control/Optics/Linear/Internal.hs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ import qualified Data.Bifunctor.Linear as Bifunctor
1212
import Data.Bifunctor.Linear (SymmetricMonoidal)
1313
import Data.Functor.Const
1414
import Data.Functor.Linear
15-
import Data.Monoid
15+
import Data.Semigroup.Linear
1616
import Data.Profunctor.Linear
1717
import Data.Void
18-
import Prelude.Linear
18+
import Prelude.Linear hiding ((<$>))
19+
-- ^ XXX: not entirely sure why the hiding is necessary here...
1920
import qualified Prelude as P
2021

22+
-- TODO: documentation in this module
23+
-- Put the functions in some sensible order: possibly split into separate
24+
-- Lens/Prism/Traversal/Iso modules
2125
newtype Optic_ arr a b s t = Optical (a `arr` b -> s `arr` t)
2226

2327
type Optic c a b s t =
@@ -29,8 +33,12 @@ type Lens a b s t = Optic (Strong (,) ()) a b s t
2933
type Lens' a s = Lens a a s s
3034
type Prism a b s t = Optic (Strong Either Void) a b s t
3135
type Prism' a s = Prism a a s s
32-
type Traversal a b s t = Optic Wandering a b s t
33-
type Traversal' a s = Traversal a a s s
36+
type PTraversal a b s t = Optic PWandering a b s t
37+
type PTraversal' a s = PTraversal a a s s
38+
type DTraversal a b s t = Optic DWandering a b s t
39+
type DTraversal' a s = DTraversal a a s s
40+
-- XXX: these will unify into
41+
-- type Traversal (p :: Multiplicity) a b s t = Optic (Wandering p) a b s t
3442

3543
swap :: SymmetricMonoidal m u => Iso (a `m` b) (c `m` d) (b `m` a) (d `m` c)
3644
swap = iso Bifunctor.swap Bifunctor.swap
@@ -41,6 +49,10 @@ assoc = iso Bifunctor.lassoc Bifunctor.rassoc
4149
(.>) :: Optic_ arr a b s t -> Optic_ arr x y a b -> Optic_ arr x y s t
4250
Optical f .> Optical g = Optical (f P.. g)
4351

52+
-- c is the complement (probably)
53+
lens :: (s ->. (c,a)) -> ((c,b) ->. t) -> Lens a b s t
54+
lens sca cbt = Optical $ \f -> dimap sca cbt (second f)
55+
4456
prism :: (b ->. t) -> (s ->. Either t a) -> Prism a b s t
4557
prism b s = Optical $ \f -> dimap s (either id id) (second (rmap b f))
4658

@@ -50,6 +62,37 @@ _1 = Optical first
5062
_2 :: Lens a b (c,a) (c,b)
5163
_2 = Optical second
5264

65+
-- XXX: these will unify to
66+
-- > both :: forall (p :: Multiplicity). Traversal p a b (a,a) (b,b)
67+
both' :: PTraversal a b (a,a) (b,b)
68+
both' = _Pairing .> ptraversed
69+
70+
both :: DTraversal a b (a,a) (b,b)
71+
both = _Pairing .> dtraversed
72+
73+
-- XXX: these are a special case of Bitraversable, but just the simple case
74+
-- is included here for now
75+
_Pairing :: Iso (Pair a) (Pair b) (a,a) (b,b)
76+
_Pairing = iso Paired unpair
77+
78+
newtype Pair a = Paired (a,a)
79+
unpair :: Pair a ->. (a,a)
80+
unpair (Paired x) = x
81+
82+
instance P.Functor Pair where
83+
fmap f (Paired (x,y)) = Paired (f x, f y)
84+
instance Functor Pair where
85+
fmap f (Paired (x,y)) = Paired (f x, f y)
86+
instance Foldable Pair where
87+
foldMap f (Paired (x,y)) = f x P.<> f y
88+
instance P.Traversable Pair where
89+
traverse f (Paired (x,y)) = Paired P.<$> ((,) P.<$> f x P.<*> f y)
90+
instance Traversable Pair where
91+
traverse f (Paired (x,y)) = Paired <$> ((,) <$> f x <*> f y)
92+
93+
toListOf :: Optic_ (Kleisli (Const [a])) a b s t -> s -> [a]
94+
toListOf l = gets l (\a -> [a])
95+
5396
_Left :: Prism a b (Either a c) (Either b c)
5497
_Left = Optical first
5598

@@ -62,8 +105,11 @@ _Just = prism Just (maybe (Left Nothing) Right)
62105
_Nothing :: Prism' () (Maybe a)
63106
_Nothing = prism (\() -> Nothing) Left
64107

65-
traversed :: Traversable t => Traversal a b (t a) (t b)
66-
traversed = Optical wander
108+
ptraversed :: P.Traversable t => PTraversal a b (t a) (t b)
109+
ptraversed = Optical pwander
110+
111+
dtraversed :: Traversable t => DTraversal a b (t a) (t b)
112+
dtraversed = Optical dwander
67113

68114
over :: Optic_ LinearArrow a b s t -> (a ->. b) -> s ->. t
69115
over (Optical l) f = getLA (l (LA f))
@@ -74,6 +120,18 @@ traverseOf (Optical l) f = runLKleisli (l (LKleisli f))
74120
get :: Optic_ (Kleisli (Const a)) a b s t -> s -> a
75121
get l = gets l P.id
76122

123+
preview :: Optic_ (Kleisli (Const (Maybe (First a)))) a b s t -> s -> Maybe a
124+
preview (Optical l) s = getFirst P.<$> (getConst (runKleisli (l (Kleisli (\a -> Const (Just (First a))))) s))
125+
126+
get' :: Optic_ (LKleisli (Const (Top, a))) a b s t -> s ->. (Top, a)
127+
get' l = gets' l id
128+
129+
gets' :: Optic_ (LKleisli (Const (Top, r))) a b s t -> (a ->. r) -> s ->. (Top, r)
130+
gets' (Optical l) f s = getConst' (runLKleisli (l (LKleisli (\a -> Const (Top (), f a)))) s)
131+
132+
set' :: Optic_ (LKleisli (MyFunctor a b)) a b s t -> s ->. b ->. (a, t)
133+
set' (Optical l) = runMyFunctor . runLKleisli (l (LKleisli (\a -> MyFunctor (\b -> (a,b)))))
134+
77135
gets :: Optic_ (Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
78136
gets (Optical l) f s = getConst' (runKleisli (l (Kleisli (Const P.. f))) s)
79137

@@ -115,4 +173,3 @@ iso f g = Optical (dimap f g)
115173
withIso :: Optic_ (Exchange a b) a b s t -> ((s ->. a) -> (b ->. t) -> r) -> r
116174
withIso (Optical l) f = f fro to
117175
where Exchange fro to = l (Exchange id id)
118-

src/Data/Functor/Linear.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
module Data.Functor.Linear where
1616

1717
import Prelude.Linear.Internal.Simple
18+
import Prelude (Maybe(..))
1819
import Data.Functor.Const
20+
import Data.Semigroup.Linear
1921

2022
class Functor f where
2123
fmap :: (a ->. b) -> f a ->. f b
@@ -74,3 +76,11 @@ instance Traversable [] where
7476

7577
instance Functor (Const x) where
7678
fmap _ (Const x) = Const x
79+
80+
instance Monoid x => Applicative (Const x) where
81+
pure _ = Const mempty
82+
Const x <*> Const y = Const (x <> y)
83+
84+
instance Functor Maybe where
85+
fmap _ Nothing = Nothing
86+
fmap f (Just x) = Just (f x)

src/Data/Profunctor/Linear.hs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1+
{-# LANGUAGE GADTs #-}
12
{-# LANGUAGE FlexibleContexts #-}
23
{-# LANGUAGE FlexibleInstances #-}
34
{-# LANGUAGE KindSignatures #-}
45
{-# LANGUAGE LambdaCase #-}
56
{-# LANGUAGE LinearTypes #-}
67
{-# LANGUAGE MultiParamTypeClasses #-}
78
{-# LANGUAGE NoImplicitPrelude #-}
9+
{-# LANGUAGE RankNTypes #-}
810
{-# LANGUAGE TupleSections #-}
911
{-# LANGUAGE TypeOperators #-}
1012

1113
module Data.Profunctor.Linear
1214
( Profunctor(..)
1315
, Monoidal(..)
1416
, Strong(..)
15-
, Wandering(..)
17+
, PWandering(..)
18+
, DWandering(..)
1619
, LinearArrow(..), getLA
1720
, Kleisli(..)
1821
, LKleisli(..)
1922
, CoLKleisli(..)
2023
, Exchange(..)
24+
, Top(..)
25+
, MyFunctor(..), runMyFunctor
2126
) where
2227

2328
import qualified Control.Monad.Linear as Control
@@ -60,16 +65,24 @@ class (SymmetricMonoidal m u, Profunctor arr) => Strong m u arr where
6065
second arr = dimap swap swap (first arr)
6166
{-# INLINE second #-}
6267

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

6680
---------------
6781
-- Instances --
6882
---------------
6983

7084
newtype LinearArrow a b = LA (a ->. b)
7185
-- | Temporary deconstructor since inference doesn't get it right
72-
-- TODO: maybe use TH to automatically write things like this?
7386
getLA :: LinearArrow a b ->. a ->. b
7487
getLA (LA f) = f
7588

@@ -84,10 +97,13 @@ instance Strong Either Void LinearArrow where
8497
first (LA f) = LA $ either (Left . f) Right
8598
second (LA g) = LA $ either Left (Right . g)
8699

100+
instance DWandering LinearArrow where
101+
dwander (LA f) = LA (Data.fmap f)
102+
87103
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
88104

89105
instance Prelude.Functor f => Profunctor (Kleisli f) where
90-
dimap f g (Kleisli h) = Kleisli (\x -> forget g Prelude.<$> h (f x))
106+
dimap f g (Kleisli h) = Kleisli (\x -> eta g Prelude.<$> h (f x))
91107

92108
instance Prelude.Functor f => Strong (,) () (Kleisli f) where
93109
first (Kleisli f) = Kleisli (\(a,b) -> (,b) Prelude.<$> f a)
@@ -98,6 +114,8 @@ instance Prelude.Applicative f => Strong Either Void (Kleisli f) where
98114
Left x -> Prelude.fmap Left (f x)
99115
Right y -> Prelude.pure (Right y)
100116

117+
instance Prelude.Applicative f => PWandering (Kleisli f) where
118+
pwander (Kleisli f) = Kleisli (Prelude.traverse f)
101119

102120
newtype LKleisli m a b = LKleisli { runLKleisli :: a ->. m b }
103121

@@ -112,8 +130,8 @@ instance Control.Applicative f => Strong Either Void (LKleisli f) where
112130
first (LKleisli f) = LKleisli (either (Data.fmap Left . f) (Control.pure . Right))
113131
second (LKleisli g) = LKleisli (either (Control.pure . Left) (Data.fmap Right . g))
114132

115-
instance Control.Applicative f => Wandering (LKleisli f) where
116-
wander (LKleisli f) = LKleisli (Data.traverse f)
133+
instance Control.Applicative f => DWandering (LKleisli f) where
134+
dwander (LKleisli f) = LKleisli (Data.traverse f)
117135

118136
instance Profunctor (->) where
119137
dimap f g h x = g (h (f x))
@@ -122,6 +140,8 @@ instance Strong (,) () (->) where
122140
instance Strong Either Void (->) where
123141
first f (Left x) = Left (f x)
124142
first _ (Right y) = Right y
143+
instance PWandering (->) where
144+
pwander = Prelude.fmap
125145

126146
-- XXX: Since CoLKleisli has uses, it might be better to replace all this
127147
-- with a Bif-like structure...

src/Data/Semigroup/Linear.hs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{-# LANGUAGE DerivingVia #-}
2+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+
{-# LANGUAGE LinearTypes #-}
4+
{-# LANGUAGE NoImplicitPrelude #-}
5+
{-# LANGUAGE StandaloneDeriving #-}
6+
7+
-- | = The linear semigroup hierarchy
8+
--
9+
-- TODO: documentation
10+
11+
module Data.Semigroup.Linear
12+
( Semigroup(..)
13+
, Monoid(..)
14+
, LEndo(..), appLEndo
15+
, module Data.Semigroup
16+
)
17+
where
18+
19+
import Prelude.Linear.Internal.Simple
20+
import Data.Semigroup hiding (Semigroup(..))
21+
import qualified Data.Semigroup as Prelude
22+
import qualified Prelude
23+
import qualified Unsafe.Linear as Unsafe
24+
25+
class Prelude.Semigroup a => Semigroup a where
26+
(<>) :: a ->. a ->. a
27+
28+
class (Semigroup a, Prelude.Monoid a) => Monoid a where
29+
{-# MINIMAL #-}
30+
mempty :: a
31+
mempty = mempty
32+
-- convenience redefine
33+
34+
---------------
35+
-- Instances --
36+
---------------
37+
38+
instance Semigroup () where
39+
() <> () = ()
40+
41+
data LEndo a = LEndo (a ->. a)
42+
43+
appLEndo :: LEndo a ->. a ->. a
44+
appLEndo (LEndo f) = f
45+
46+
instance Prelude.Semigroup (LEndo a) where
47+
LEndo f <> LEndo g = LEndo (f . g)
48+
instance Prelude.Monoid (LEndo a) where
49+
mempty = LEndo id
50+
instance Semigroup (LEndo a) where
51+
LEndo f <> LEndo g = LEndo (f . g)
52+
instance Monoid (LEndo a) where
53+
54+
instance (Semigroup a, Semigroup b) => Semigroup (a,b) where
55+
(a,x) <> (b,y) = (a <> b, x <> y)
56+
instance (Monoid a, Monoid b) => Monoid (a,b)
57+
58+
newtype LWrap a = LWrap a
59+
deriving (Prelude.Semigroup, Prelude.Monoid)
60+
61+
-- This instance is unsafe: do not export LWrap so it cannot be used.
62+
instance Prelude.Semigroup a => Semigroup (LWrap a) where
63+
LWrap a <> LWrap b = LWrap (Unsafe.toLinear2 (Prelude.<>) a b)
64+
instance Prelude.Monoid a => Monoid (LWrap a)
65+
66+
-- XXX: I think these are safe but I'm not fully confident
67+
deriving via (LWrap (Sum a)) instance Prelude.Num a => Semigroup (Sum a)
68+
deriving via (LWrap (Sum a)) instance Prelude.Num a => Monoid (Sum a)
69+
deriving via (LWrap (Product a)) instance Prelude.Num a => Semigroup (Product a)
70+
deriving via (LWrap (Product a)) instance Prelude.Num a => Monoid (Product a)

src/Prelude/Linear.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,7 @@ instance Data.Functor Unrestricted where
223223
instance Data.Applicative Unrestricted where
224224
pure = Unrestricted
225225
Unrestricted f <*> Unrestricted x = Unrestricted (f x)
226+
227+
-- TODO: move this somewhere more sensible to avoid orphan instance
228+
instance Data.Functor (Either e) where
229+
fmap f = either Left (Right . f)

0 commit comments

Comments
 (0)