Skip to content

Commit 38d1e92

Browse files
committed
A plethora of new optics
[skip ci]
1 parent e31a360 commit 38d1e92

File tree

7 files changed

+224
-22
lines changed

7 files changed

+224
-22
lines changed

src/Control/Optics/Linear/Internal.hs

+73-10
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 L
4449
import qualified Data.Profunctor.Kleisli.NonLinear as NL
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_ (NL.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_ (NL.Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
106157
gets (Optical l) f s = getConst' (NL.runKleisli (l (NL.Kleisli (Const P.. f))) s)
107158

159+
preview :: Optic_ (NL.Kleisli (Const (Maybe (First a)))) a b s t -> s -> Maybe a
160+
preview (Optical l) s = getFirst P.<$> (getConst (NL.runKleisli (l (NL.Kleisli (\a -> Const (Just (First a))))) s))
161+
162+
get' :: Optic_ (L.Kleisli (Const (Top, a))) a b s t -> s ->. (Top, a)
163+
get' l = gets' l id
164+
165+
gets' :: Optic_ (L.Kleisli (Const (Top, r))) a b s t -> (a ->. r) -> s ->. (Top, r)
166+
gets' (Optical l) f s = getConst' (L.runKleisli (l (L.Kleisli (\a -> Const (mempty, f a)))) s)
167+
168+
set' :: Optic_ (L.Kleisli (MyFunctor a b)) a b s t -> s ->. b ->. (a, t)
169+
set' (Optical l) = runMyFunctor . L.runKleisli (l (L.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

@@ -113,7 +176,7 @@ match (Optical l) = withIso swap (\x _ -> x) . L.runKleisli (l (L.Kleisli Left))
113176

114177
-- will be redundant with multiplicity polymorphism
115178
match' :: Optic_ (NL.Kleisli (Either a)) a b s t -> s -> Either t a
116-
match' (Optical l) = withIso swap (\x _ -> forget x) P.. NL.runKleisli (l (NL.Kleisli Left))
179+
match' (Optical l) = withIso swap (\x _ -> eta x) P.. NL.runKleisli (l (NL.Kleisli Left))
117180

118181
build :: Optic_ (L.CoKleisli (Const b)) a b s t -> b ->. t
119182
build (Optical l) x = L.runCoKleisli (l (L.CoKleisli getConst')) (Const x)

src/Data/Functor/Linear.hs

+10
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/Kleisli/Linear.hs

+2-2
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module Data.Profunctor.Kleisli.NonLinear
1313
import Data.Profunctor.Linear
1414
import Data.Void
1515
import qualified Prelude
16-
import Prelude.Linear (Either(..), forget)
16+
import Prelude.Linear (Either(..), eta)
1717
import Prelude.Linear.Internal.Simple (($))
1818

1919
-- Non-linear Kleisli arrows for the monad `m`. As in the linear case,
@@ -22,7 +22,7 @@ import Prelude.Linear.Internal.Simple (($))
2222
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
2323

2424
instance Prelude.Functor f => Profunctor (Kleisli f) where
25-
dimap f g (Kleisli h) = Kleisli (\x -> forget g Prelude.<$> h (f x))
25+
dimap f g (Kleisli h) = Kleisli (\x -> eta g Prelude.<$> h (f x))
2626

2727
instance Prelude.Functor f => Strong (,) () (Kleisli f) where
2828
first (Kleisli f) = Kleisli (\(a,b) -> (,b) Prelude.<$> f a)

src/Data/Profunctor/Linear.hs

+52-3
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
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

912
module Data.Profunctor.Linear
1013
( Profunctor(..)
1114
, Monoidal(..)
1215
, Strong(..)
13-
, Wandering(..)
16+
, PWandering(..)
17+
, DWandering(..)
1418
, LinearArrow(..), getLA
1519
, Exchange(..)
20+
, Top(..)
21+
, MyFunctor(..), runMyFunctor
1622
) where
1723

1824
import qualified Data.Functor.Linear as Data
25+
import qualified Control.Monad.Linear as Control
1926
import Data.Bifunctor.Linear hiding (first, second)
2027
import Prelude.Linear
2128
import Data.Void
29+
import qualified Prelude
30+
import Data.Functor.Const
2231

2332
-- TODO: write laws
2433

@@ -52,8 +61,17 @@ class (SymmetricMonoidal m u, Profunctor arr) => Strong m u arr where
5261
second arr = dimap swap swap (first arr)
5362
{-# INLINE second #-}
5463

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

5876
---------------
5977
-- Instances --
@@ -82,7 +100,38 @@ instance Strong (,) () (->) where
82100
instance Strong Either Void (->) where
83101
first f (Left x) = Left (f x)
84102
first _ (Right y) = Right y
103+
instance PWandering (->) where
104+
pwander = Prelude.fmap
85105

86106
data Exchange a b s t = Exchange (s ->. a) (b ->. t)
87107
instance Profunctor (Exchange a b) where
88108
dimap f g (Exchange p q) = Exchange (p . f) (g . q)
109+
110+
data Top = forall x. Top x
111+
instance Show Top where
112+
show (Top _) = "something"
113+
instance Control.Functor (Const (Top, a)) where
114+
fmap f (Const (Top t, x)) = Const (Top (t,f), x)
115+
instance Monoid a => Control.Applicative (Const (Top, a)) where
116+
pure x = Const (Top x, mempty)
117+
Const (Top a, x) <*> Const (Top b, y) = Const (Top (a,b), 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)
130+
131+
instance Prelude.Semigroup Top where
132+
Top x <> Top y = Top (x,y)
133+
instance Semigroup Top where
134+
Top x <> Top y = Top (x,y)
135+
instance Prelude.Monoid Top where
136+
mempty = Top ()
137+
instance Monoid Top where

src/Data/Semigroup/Linear.hs

+70
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)

0 commit comments

Comments
 (0)