Skip to content

Commit 12cb80a

Browse files
committed
Merge branch 'prisms'
2 parents 79b456e + e75fdbe commit 12cb80a

File tree

7 files changed

+198
-51
lines changed

7 files changed

+198
-51
lines changed

linear-base.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ library
2525
Data.Functor.Linear
2626
Data.Vector.Linear
2727
Data.Profunctor.Linear
28+
Data.Profunctor.Kleisli.Linear
29+
Data.Profunctor.Kleisli.NonLinear
2830
Foreign.Marshal.Pure
2931
Prelude.Linear
3032
Prelude.Linear.Internal.Simple

src/Control/Optics/Linear/Internal.hs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,42 @@
66
{-# LANGUAGE RankNTypes #-}
77
{-# LANGUAGE TypeOperators #-}
88

9-
module Control.Optics.Linear.Internal where
9+
module Control.Optics.Linear.Internal
10+
( -- * Types
11+
Optic_(..)
12+
, Optic
13+
, Iso, Iso'
14+
, Lens, Lens'
15+
, Prism, Prism'
16+
, Traversal, Traversal'
17+
-- * Composing optics
18+
, (.>)
19+
-- * Common optics
20+
, swap, assoc
21+
, _1, _2
22+
, _Left, _Right
23+
, _Just, _Nothing
24+
, traversed
25+
-- * Using optics
26+
, get, set, gets
27+
, match, match', build
28+
, over, over'
29+
, traverseOf, traverseOf'
30+
, lengthOf
31+
, withIso
32+
-- * Constructing optics
33+
, iso, prism
34+
)
35+
where
1036

1137
import qualified Data.Bifunctor.Linear as Bifunctor
1238
import Data.Bifunctor.Linear (SymmetricMonoidal)
1339
import Data.Functor.Const
1440
import Data.Functor.Linear
1541
import Data.Monoid
1642
import Data.Profunctor.Linear
43+
import qualified Data.Profunctor.Kleisli.Linear as Linear
44+
import qualified Data.Profunctor.Kleisli.NonLinear as NonLinear
1745
import Data.Void
1846
import Prelude.Linear
1947
import qualified Prelude as P
@@ -33,14 +61,17 @@ type Traversal a b s t = Optic Wandering a b s t
3361
type Traversal' a s = Traversal a a s s
3462

3563
swap :: SymmetricMonoidal m u => Iso (a `m` b) (c `m` d) (b `m` a) (d `m` c)
36-
swap = Optical (dimap Bifunctor.swap Bifunctor.swap)
64+
swap = iso Bifunctor.swap Bifunctor.swap
3765

3866
assoc :: SymmetricMonoidal m u => Iso ((a `m` b) `m` c) ((d `m` e) `m` f) (a `m` (b `m` c)) (d `m` (e `m` f))
39-
assoc = Optical (dimap Bifunctor.lassoc Bifunctor.rassoc)
67+
assoc = iso Bifunctor.lassoc Bifunctor.rassoc
4068

4169
(.>) :: Optic_ arr a b s t -> Optic_ arr x y a b -> Optic_ arr x y s t
4270
Optical f .> Optical g = Optical (f P.. g)
4371

72+
prism :: (b ->. t) -> (s ->. Either t a) -> Prism a b s t
73+
prism b s = Optical $ \f -> dimap s (either id id) (second (rmap b f))
74+
4475
_1 :: Lens a b (a,c) (b,c)
4576
_1 = Optical first
4677

@@ -53,31 +84,60 @@ _Left = Optical first
5384
_Right :: Prism a b (Either c a) (Either c b)
5485
_Right = Optical second
5586

87+
_Just :: Prism a b (Maybe a) (Maybe b)
88+
_Just = prism Just (maybe (Left Nothing) Right)
89+
90+
_Nothing :: Prism' () (Maybe a)
91+
_Nothing = prism (\() -> Nothing) Left
92+
5693
traversed :: Traversable t => Traversal a b (t a) (t b)
5794
traversed = Optical wander
5895

5996
over :: Optic_ LinearArrow a b s t -> (a ->. b) -> s ->. t
6097
over (Optical l) f = getLA (l (LA f))
6198

62-
traverseOf :: Optic_ (LKleisli f) a b s t -> (a ->. f b) -> s ->. f t
63-
traverseOf (Optical l) f = runLKleisli (l (LKleisli f))
99+
traverseOf :: Optic_ (Linear.Kleisli f) a b s t -> (a ->. f b) -> s ->. f t
100+
traverseOf (Optical l) f = Linear.runKleisli (l (Linear.Kleisli f))
64101

65-
get :: Optic_ (Kleisli (Const a)) a b s t -> s -> a
102+
get :: Optic_ (NonLinear.Kleisli (Const a)) a b s t -> s -> a
66103
get l = gets l P.id
67104

68-
gets :: Optic_ (Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
69-
gets (Optical l) f s = getConst (runKleisli (l (Kleisli (Const P.. f))) s)
105+
gets :: Optic_ (NonLinear.Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
106+
gets (Optical l) f s = getConst' (NonLinear.runKleisli (l (NonLinear.Kleisli (Const P.. f))) s)
70107

71108
set :: Optic_ (->) a b s t -> b -> s -> t
72109
set (Optical l) x = l (const x)
73110

74-
lengthOf :: Num r => Optic_ (Kleisli (Const (Sum r))) a b s t -> s -> r
111+
match :: Optic_ (Linear.Kleisli (Either a)) a b s t -> s ->. Either t a
112+
match (Optical l) = withIso swap (\x _ -> x) . Linear.runKleisli (l (Linear.Kleisli Left))
113+
114+
-- will be redundant with multiplicity polymorphism
115+
match' :: Optic_ (NonLinear.Kleisli (Either a)) a b s t -> s -> Either t a
116+
match' (Optical l) = withIso swap (\x _ -> forget x) P.. NonLinear.runKleisli (l (NonLinear.Kleisli Left))
117+
118+
build :: Optic_ (Linear.CoKleisli (Const b)) a b s t -> b ->. t
119+
build (Optical l) x = Linear.runCoKleisli (l (Linear.CoKleisli getConst')) (Const x)
120+
121+
-- XXX: move this to Prelude
122+
-- | Linearly typed patch for the newtype deconstructor. (Temporary until
123+
-- inference will get this from the newtype declaration.)
124+
getConst' :: Const a b ->. a
125+
getConst' (Const x) = x
126+
127+
lengthOf :: Num r => Optic_ (NonLinear.Kleisli (Const (Sum r))) a b s t -> s -> r
75128
lengthOf l s = getSum (gets l (const (Sum 1)) s)
76129

77130
-- XXX: the below two functions will be made redundant with multiplicity
78131
-- polymorphism on over and traverseOf'
79132
over' :: Optic_ (->) a b s t -> (a -> b) -> s -> t
80133
over' (Optical l) f = l f
81134

82-
traverseOf' :: Optic_ (Kleisli f) a b s t -> (a -> f b) -> s -> f t
83-
traverseOf' (Optical l) f = runKleisli (l (Kleisli f))
135+
traverseOf' :: Optic_ (NonLinear.Kleisli f) a b s t -> (a -> f b) -> s -> f t
136+
traverseOf' (Optical l) f = NonLinear.runKleisli (l (NonLinear.Kleisli f))
137+
138+
iso :: (s ->. a) -> (b ->. t) -> Iso a b s t
139+
iso f g = Optical (dimap f g)
140+
141+
withIso :: Optic_ (Exchange a b) a b s t -> ((s ->. a) -> (b ->. t) -> r) -> r
142+
withIso (Optical l) f = f fro to
143+
where Exchange fro to = l (Exchange id id)

src/Data/Functor/Linear.hs

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

1717
import Prelude.Linear.Internal.Simple
18+
import Data.Functor.Const
1819

1920
class Functor f where
2021
fmap :: (a ->. b) -> f a ->. f b
@@ -70,3 +71,6 @@ instance Functor [] where
7071
instance Traversable [] where
7172
traverse _f [] = pure []
7273
traverse f (a : as) = (:) <$> f a <*> traverse f as
74+
75+
instance Functor (Const x) where
76+
fmap _ (Const x) = Const x
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE LinearTypes #-}
3+
{-# LANGUAGE MultiParamTypeClasses #-}
4+
{-# LANGUAGE NoImplicitPrelude #-}
5+
{-# LANGUAGE TupleSections #-}
6+
module Data.Profunctor.Kleisli.Linear
7+
( Kleisli(..)
8+
, CoKleisli(..)
9+
)
10+
where
11+
12+
-- | This module is intended to be imported qualified
13+
14+
import Data.Functor.Const
15+
import Data.Profunctor.Linear
16+
import Data.Void
17+
import Prelude.Linear (Either(..), either)
18+
import Prelude.Linear.Internal.Simple
19+
import qualified Control.Monad.Linear as Control
20+
import qualified Data.Functor.Linear as Data
21+
22+
-- Ideally, there would only be one Kleisli arrow, parametrised by
23+
-- a multiplicity parameter:
24+
-- newtype Kleisli p m a b = Kleisli { runKleisli :: a # p -> m b }
25+
--
26+
-- Some instances would also still work, eg
27+
-- instance Functor p f => Profunctor (Kleisli p f)
28+
29+
-- | Linear Kleisli arrows for the monad `m`. These arrows are still useful
30+
-- in the case where `m` is not a monad however, and some profunctorial
31+
-- properties still hold in this weaker setting.
32+
newtype Kleisli m a b = Kleisli { runKleisli :: a ->. m b }
33+
34+
instance Data.Functor f => Profunctor (Kleisli f) where
35+
dimap f g (Kleisli h) = Kleisli (Data.fmap g . h . f)
36+
37+
instance Control.Functor f => Strong (,) () (Kleisli f) where
38+
first (Kleisli f) = Kleisli (\(a,b) -> (,b) Control.<$> f a)
39+
second (Kleisli g) = Kleisli (\(a,b) -> (a,) Control.<$> g b)
40+
41+
instance Control.Applicative f => Strong Either Void (Kleisli f) where
42+
first (Kleisli f) = Kleisli (either (Data.fmap Left . f) (Control.pure . Right))
43+
second (Kleisli g) = Kleisli (either (Control.pure . Left) (Data.fmap Right . g))
44+
45+
instance Control.Applicative f => Wandering (Kleisli f) where
46+
wander (Kleisli f) = Kleisli (Data.traverse f)
47+
48+
-- | Linear co-Kleisli arrows for the comonad `w`. These arrows are still
49+
-- useful in the case where `w` is not a comonad however, and some
50+
-- profunctorial properties still hold in this weaker setting.
51+
-- However stronger requirements on `f` are needed for profunctorial
52+
-- strength, so we have fewer instances.
53+
--
54+
-- Category theoretic remark: duality doesn't work in the obvious way, since
55+
-- (,) isn't the categorical product. Instead, we have a product (&), called
56+
-- "With", defined by
57+
-- > type With a b = forall r. Either (a ->. r) (b ->. r) ->. r
58+
-- which satisfies the universal property of the product of `a` and `b`.
59+
-- CoKleisli arrows are strong with respect to this monoidal structure,
60+
-- although this might not be useful...
61+
newtype CoKleisli w a b = CoKleisli { runCoKleisli :: w a ->. b }
62+
63+
instance Data.Functor f => Profunctor (CoKleisli f) where
64+
dimap f g (CoKleisli h) = CoKleisli (g . h . Data.fmap f)
65+
66+
-- instance of a more general idea, but this will do for now
67+
instance Strong Either Void (CoKleisli (Const x)) where
68+
first (CoKleisli f) = CoKleisli (\(Const x) -> Left (f (Const x)))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE LinearTypes #-}
4+
{-# LANGUAGE MultiParamTypeClasses #-}
5+
{-# LANGUAGE NoImplicitPrelude #-}
6+
{-# LANGUAGE TupleSections #-}
7+
8+
module Data.Profunctor.Kleisli.NonLinear
9+
( Kleisli(..)
10+
)
11+
where
12+
13+
import Data.Profunctor.Linear
14+
import Data.Void
15+
import qualified Prelude
16+
import Prelude.Linear (Either(..), forget)
17+
import Prelude.Linear.Internal.Simple (($))
18+
19+
-- Non-linear Kleisli arrows for the monad `m`. As in the linear case,
20+
-- these arrows are still useful if `m` is only a `Functor` or an
21+
-- `Applicative`.
22+
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
23+
24+
instance Prelude.Functor f => Profunctor (Kleisli f) where
25+
dimap f g (Kleisli h) = Kleisli (\x -> forget g Prelude.<$> h (f x))
26+
27+
instance Prelude.Functor f => Strong (,) () (Kleisli f) where
28+
first (Kleisli f) = Kleisli (\(a,b) -> (,b) Prelude.<$> f a)
29+
second (Kleisli g) = Kleisli (\(a,b) -> (a,) Prelude.<$> g b)
30+
31+
instance Prelude.Applicative f => Strong Either Void (Kleisli f) where
32+
first (Kleisli f) = Kleisli $ \case
33+
Left x -> Prelude.fmap Left (f x)
34+
Right y -> Prelude.pure (Right y)

src/Data/Profunctor/Linear.hs

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{-# LANGUAGE FlexibleContexts #-}
22
{-# LANGUAGE KindSignatures #-}
3-
{-# LANGUAGE LambdaCase #-}
43
{-# LANGUAGE LinearTypes #-}
54
{-# LANGUAGE MultiParamTypeClasses #-}
65
{-# LANGUAGE NoImplicitPrelude #-}
@@ -12,17 +11,14 @@ module Data.Profunctor.Linear
1211
, Monoidal(..)
1312
, Strong(..)
1413
, Wandering(..)
15-
, LinearArrow(..)
16-
, Kleisli(..)
17-
, LKleisli(..)
14+
, LinearArrow(..), getLA
15+
, Exchange(..)
1816
) where
1917

20-
import qualified Control.Monad.Linear as Control
2118
import qualified Data.Functor.Linear as Data
2219
import Data.Bifunctor.Linear hiding (first, second)
2320
import Prelude.Linear
2421
import Data.Void
25-
import qualified Prelude
2622

2723
-- TODO: write laws
2824

@@ -63,7 +59,10 @@ class (Strong (,) () arr, Strong Either Void arr) => Wandering arr where
6359
-- Instances --
6460
---------------
6561

66-
newtype LinearArrow a b = LA { getLA :: a ->. b }
62+
newtype LinearArrow a b = LA (a ->. b)
63+
-- | Temporary deconstructor since inference doesn't get it right
64+
getLA :: LinearArrow a b ->. a ->. b
65+
getLA (LA f) = f
6766

6867
instance Profunctor LinearArrow where
6968
dimap f g (LA h) = LA $ g . h . f
@@ -76,43 +75,14 @@ instance Strong Either Void LinearArrow where
7675
first (LA f) = LA $ either (Left . f) Right
7776
second (LA g) = LA $ either Left (Right . g)
7877

79-
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
80-
81-
instance Prelude.Functor f => Profunctor (Kleisli f) where
82-
dimap f g (Kleisli h) = Kleisli (\x -> forget g Prelude.<$> h (f x))
83-
84-
instance Prelude.Functor f => Strong (,) () (Kleisli f) where
85-
first (Kleisli f) = Kleisli (\(a,b) -> (,b) Prelude.<$> f a)
86-
second (Kleisli g) = Kleisli (\(a,b) -> (a,) Prelude.<$> g b)
87-
88-
instance Prelude.Applicative f => Strong Either Void (Kleisli f) where
89-
first (Kleisli f) = Kleisli $ \case
90-
Left x -> Prelude.fmap Left (f x)
91-
Right y -> Prelude.pure (Right y)
92-
93-
forget :: (a ->. b) -> a -> b
94-
forget f x = f x
95-
96-
newtype LKleisli m a b = LKleisli { runLKleisli :: a ->. m b }
97-
98-
instance Data.Functor f => Profunctor (LKleisli f) where
99-
dimap f g (LKleisli h) = LKleisli (Data.fmap g . h . f)
100-
101-
instance Control.Functor f => Strong (,) () (LKleisli f) where
102-
first (LKleisli f) = LKleisli (\(a,b) -> (,b) Control.<$> f a)
103-
second (LKleisli g) = LKleisli (\(a,b) -> (a,) Control.<$> g b)
104-
105-
instance Control.Applicative f => Strong Either Void (LKleisli f) where
106-
first (LKleisli f) = LKleisli (either (Data.fmap Left . f) (Control.pure . Right))
107-
second (LKleisli g) = LKleisli (either (Control.pure . Left) (Data.fmap Right . g))
108-
109-
instance Control.Applicative f => Wandering (LKleisli f) where
110-
wander (LKleisli f) = LKleisli (Data.traverse f)
111-
11278
instance Profunctor (->) where
11379
dimap f g h x = g (h (f x))
11480
instance Strong (,) () (->) where
11581
first f (x, y) = (f x, y)
11682
instance Strong Either Void (->) where
11783
first f (Left x) = Left (f x)
11884
first _ (Right y) = Right y
85+
86+
data Exchange a b s t = Exchange (s ->. a) (b ->. t)
87+
instance Profunctor (Exchange a b) where
88+
dimap f g (Exchange p q) = Exchange (p . f) (g . q)

src/Prelude/Linear.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Prelude.Linear
1818
, (.)
1919
, either
2020
, maybe
21+
, forget
2122
-- * Unrestricted
2223
-- $ unrestricted
2324
, Unrestricted(..)
@@ -71,6 +72,14 @@ maybe :: b -> (a ->. b) -> Maybe a ->. b
7172
maybe x _ Nothing = x
7273
maybe _ f (Just y) = f y
7374

75+
-- XXX: temporary: with multiplicity polymorphism functions expecting a
76+
-- non-linear arrow would allow a linear arrow passed, so this would be
77+
-- redundant
78+
-- | Convenience operator when a higher-order function expects a non-linear
79+
-- arrow but we have a linear arrow
80+
forget :: (a ->. b) ->. a -> b
81+
forget f x = f x
82+
7483
-- $ unrestricted
7584

7685
-- | @Unrestricted a@ represents unrestricted values of type @a@ in a linear context,

0 commit comments

Comments
 (0)