Skip to content

Lots of optics #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 73 additions & 8 deletions src/Control/Optics/Linear/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,39 @@ module Control.Optics.Linear.Internal
, _Left, _Right
, _Just, _Nothing
, traversed
, both
-- * Using optics
, get, set, gets
, set', set''
, match, build
, preview
, over, over'
, traverseOf, traverseOf'
, lengthOf
, withIso, withPrism
, toListOf
, withIso, withLens, withPrism, withTraversal
-- * Constructing optics
, iso, prism
, iso, prism, lens, traversal
)
where

import qualified Control.Arrow as NonLinear
import qualified Data.Bifunctor.Linear as Bifunctor
import qualified Control.Monad.Linear as Control
import Data.Functor.Linear.Internal.Traversable
import Data.Bifunctor.Linear (SymmetricMonoidal)
import Data.Profunctor.Linear
import Data.Monoid.Linear
import Data.Functor.Const
import Data.Functor.Linear
import Data.Profunctor.Linear
import qualified Data.Profunctor.Kleisli.Linear as Linear
import Data.Void
import Prelude.Linear
import qualified Prelude as P

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

type Optic c a b s t =
Expand All @@ -55,7 +66,7 @@ type Lens a b s t = Optic (Strong (,) ()) a b s t
type Lens' a s = Lens a a s s
type Prism a b s t = Optic (Strong Either Void) a b s t
type Prism' a s = Prism a a s s
type Traversal a b s t = Optic Wandering a b s t
type Traversal a b s t = Optic Traversing a b s t
type Traversal' a s = Traversal a a s s

swap :: SymmetricMonoidal m u => Iso (a `m` b) (c `m` d) (b `m` a) (d `m` c)
Expand All @@ -67,6 +78,12 @@ assoc = iso Bifunctor.lassoc Bifunctor.rassoc
(.>) :: Optic_ arr a b s t -> Optic_ arr x y a b -> Optic_ arr x y s t
Optical f .> Optical g = Optical (f P.. g)

lens :: (s ->. (a, b ->. t)) -> Lens a b s t
lens k = Optical $ \f -> dimap k (\(x,g) -> g $ x) (first f)

withLens :: Optic_ (Linear.Kleisli (OtherFunctor a b)) a b s t -> s ->. (a, b ->. t)
withLens (Optical l) s = runOtherFunctor (Linear.runKleisli (l (Linear.Kleisli (\a -> OtherFunctor (a, id)))) s)

prism :: (b ->. t) -> (s ->. Either t a) -> Prism a b s t
prism b s = Optical $ \f -> dimap s (either id id) (second (rmap b f))

Expand All @@ -76,6 +93,28 @@ _1 = Optical first
_2 :: Lens a b (c,a) (c,b)
_2 = Optical second

both :: Traversal a b (a,a) (b,b)
both = _Pairing .> traversed

-- XXX: these are a special case of Bitraversable, but just the simple case
-- is included here for now
_Pairing :: Iso (Pair a) (Pair b) (a,a) (b,b)
_Pairing = iso Paired unpair

newtype Pair a = Paired (a,a)
unpair :: Pair a ->. (a,a)
unpair (Paired x) = x

instance P.Functor Pair where
fmap f (Paired (x,y)) = Paired (f x, f y)
instance Functor Pair where
fmap f (Paired (x,y)) = Paired (f x, f y)
instance Traversable Pair where
traverse f (Paired (x,y)) = Paired <$> ((,) <$> f x <*> f y)

toListOf :: Optic_ (NonLinear.Kleisli (Const [a])) a b s t -> s -> [a]
toListOf l = gets l (\a -> [a])

_Left :: Prism a b (Either a c) (Either b c)
_Left = Optical first

Expand All @@ -88,9 +127,6 @@ _Just = prism Just (maybe (Left Nothing) Right)
_Nothing :: Prism' () (Maybe a)
_Nothing = prism (\() -> Nothing) Left

traversed :: Traversable t => Traversal a b (t a) (t b)
traversed = Optical wander

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

Expand All @@ -103,8 +139,17 @@ get l = gets l P.id
gets :: Optic_ (NonLinear.Kleisli (Const r)) a b s t -> (a -> r) -> s -> r
gets (Optical l) f s = getConst' (NonLinear.runKleisli (l (NonLinear.Kleisli (Const P.. f))) s)

preview :: Optic_ (NonLinear.Kleisli (Const (Maybe (First a)))) a b s t -> s -> Maybe a
preview l s = P.fmap getFirst (gets l (\a -> Just (First a)) s)

set' :: Optic_ (Linear.Kleisli (MyFunctor a b)) a b s t -> s ->. b ->. (a, t)
set' (Optical l) s = runMyFunctor (Linear.runKleisli (l (Linear.Kleisli (\a -> MyFunctor (\b -> (a,b))))) s)

set'' :: Optic_ (NonLinear.Kleisli (Control.Reader b)) a b s t -> b ->. s -> t
set'' (Optical l) b s = Control.runReader (NonLinear.runKleisli (l (NonLinear.Kleisli (const (Control.reader id)))) s) b

set :: Optic_ (->) a b s t -> b -> s -> t
set (Optical l) x = l (const x)
set l b = over' l (const b)

match :: Optic_ (Market a b) a b s t -> s ->. Either t a
match (Optical l) = snd (runMarket (l (Market id Right)))
Expand Down Expand Up @@ -139,3 +184,23 @@ withIso (Optical l) f = f fro to
withPrism :: Optic_ (Market a b) a b s t -> ((b ->. t) -> (s ->. Either t a) -> r) -> r
withPrism (Optical l) f = f b m
where Market b m = l (Market id Right)

traversal :: (s ->. Batch a b t) -> Traversal a b s t
traversal h = Optical (\k -> dimap h fuse (traverse' k))

traverse' :: (Strong Either Void arr, Monoidal (,) () arr) => a `arr` b -> Batch a c t `arr` Batch b c t
traverse' k = dimap fromBatch toBatch (second (k *** traverse' k))

fromBatch :: Batch a b t ->. Either t (a, Batch a b (b ->. t))
fromBatch (Done t) = Left t
fromBatch (More l x) = Right (l, x)

toBatch :: Either t (a, Batch a b (b ->. t)) ->. Batch a b t
toBatch (Left t) = Done t
toBatch (Right (l, x)) = More l x

traversed :: Traversable t => Traversal a b (t a) (t b)
traversed = traversal (traverse batch)

withTraversal :: Optic_ (Linear.Kleisli (Batch a b)) a b s t -> s ->. Batch a b t
withTraversal (Optical l) = Linear.runKleisli (l (Linear.Kleisli batch))
24 changes: 24 additions & 0 deletions src/Data/Functor/Linear/Internal/Traversable.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Data.Functor.Linear.Internal.Traversable
Traversable(..)
, mapM, sequenceA, for, forM
, mapAccumL, mapAccumR
, batch, runWith, Batch(..), fuse
) where

import qualified Control.Monad.Linear.Internal as Control
Expand Down Expand Up @@ -79,6 +80,29 @@ instance Control.Applicative (StateR s) where
where go :: (a, (a ->. b, s)) ->. (b, s)
go (a, (h, s'')) = (h a, s'')

data Batch a b c = Done c | More a (Batch a b (b ->. c))
deriving (Data.Functor, Data.Applicative) via Control.Data (Batch a b)

instance Control.Functor (Batch a b) where
fmap f (Done c) = Done (f c)
fmap f (More x l) = More x ((f.) Control.<$> l)

instance Control.Applicative (Batch a b) where
pure = Done
Done f <*> l' = Control.fmap f l'
More x l <*> l' = More x (flip Control.<$> l Control.<*> l')

batch :: a ->. Batch a b b
batch x = More x (Done id)

runWith :: Control.Applicative f => (a ->. f b) -> Batch a b c ->. f c
runWith _ (Done c) = Control.pure c
runWith f (More x l) = runWith f l Control.<*> f x

fuse :: Batch b b t ->. t
fuse (Done i) = i
fuse (More x l) = fuse l x

------------------------
-- Standard instances --
------------------------
Expand Down
15 changes: 5 additions & 10 deletions src/Data/Profunctor/Kleisli/Linear.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,17 @@ instance Control.Applicative f => Strong Either Void (Kleisli f) where
first (Kleisli f) = Kleisli (either (Data.fmap Left . f) (Control.pure . Right))
second (Kleisli g) = Kleisli (either (Control.pure . Left) (Data.fmap Right . g))

instance Control.Applicative f => Wandering (Kleisli f) where
wander (Kleisli f) = Kleisli (Data.traverse f)
instance Control.Applicative f => Monoidal (,) () (Kleisli f) where
Kleisli f *** Kleisli g = Kleisli $ \(x,y) -> (,) Control.<$> f x Control.<*> g y
unit = Kleisli Control.pure

instance Control.Applicative f => Traversing (Kleisli f)

-- | Linear co-Kleisli arrows for the comonad `w`. These arrows are still
-- useful in the case where `w` is not a comonad however, and some
-- profunctorial properties still hold in this weaker setting.
-- However stronger requirements on `f` are needed for profunctorial
-- strength, so we have fewer instances.
--
-- Category theoretic remark: duality doesn't work in the obvious way, since
-- (,) isn't the categorical product. Instead, we have a product (&), called
-- "With", defined by
-- > type With a b = forall r. Either (a ->. r) (b ->. r) ->. r
-- which satisfies the universal property of the product of `a` and `b`.
-- CoKleisli arrows are strong with respect to this monoidal structure,
-- although this might not be useful...
newtype CoKleisli w a b = CoKleisli { runCoKleisli :: w a ->. b }

instance Data.Functor f => Profunctor (CoKleisli f) where
Expand Down
48 changes: 43 additions & 5 deletions src/Data/Profunctor/Linear.hs
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}

module Data.Profunctor.Linear
( Profunctor(..)
, Monoidal(..)
, Strong(..)
, Wandering(..)
, Traversing
, LinearArrow(..), getLA
, Exchange(..)
, Market(..), runMarket
, MyFunctor(..), runMyFunctor
, OtherFunctor(..), runOtherFunctor
) where

import qualified Data.Functor.Linear as Data
import qualified Control.Monad.Linear as Control
import Data.Bifunctor.Linear hiding (first, second)
import Prelude.Linear
import Data.Void
Expand All @@ -26,7 +31,7 @@ import Control.Arrow (Kleisli(..))

-- TODO: write laws

class Profunctor (arr :: * -> * -> *) where
class Profunctor arr where
{-# MINIMAL dimap | lmap, rmap #-}

dimap :: (s ->. a) -> (b ->. t) -> a `arr` b -> s `arr` t
Expand Down Expand Up @@ -56,8 +61,7 @@ class (SymmetricMonoidal m u, Profunctor arr) => Strong m u arr where
second arr = dimap swap swap (first arr)
{-# INLINE second #-}

class (Strong (,) () arr, Strong Either Void arr) => Wandering arr where
wander :: Data.Traversable f => a `arr` b -> f a `arr` f b
class (Strong (,) () arr, Strong Either Void arr, Monoidal (,) () arr) => Traversing arr where

---------------
-- Instances --
Expand All @@ -79,13 +83,23 @@ instance Strong Either Void LinearArrow where
first (LA f) = LA $ either (Left . f) Right
second (LA g) = LA $ either Left (Right . g)

instance Monoidal (,) () LinearArrow where
LA f *** LA g = LA $ \(a,x) -> (f a, g x)
unit = LA id

instance Traversing LinearArrow

instance Profunctor (->) where
dimap f g h x = g (h (f x))
instance Strong (,) () (->) where
first f (x, y) = (f x, y)
instance Strong Either Void (->) where
first f (Left x) = Left (f x)
first _ (Right y) = Right y
instance Monoidal (,) () (->) where
(f *** g) (a,x) = (f a, g x)
unit () = ()
instance Traversing (->)

data Exchange a b s t = Exchange (s ->. a) (b ->. t)
instance Profunctor (Exchange a b) where
Expand All @@ -103,6 +117,12 @@ instance Prelude.Applicative f => Strong Either Void (Kleisli f) where
Left x -> Prelude.fmap Left (f x)
Right y -> Prelude.pure (Right y)

instance Prelude.Applicative f => Monoidal (,) () (Kleisli f) where
Kleisli f *** Kleisli g = Kleisli (\(x,y) -> (,) Prelude.<$> f x Prelude.<*> g y)
unit = Kleisli Prelude.pure

instance Prelude.Applicative f => Traversing (Kleisli f) where

data Market a b s t = Market (b ->. t) (s ->. Either t a)
runMarket :: Market a b s t ->. (b ->. t, s ->. Either t a)
runMarket (Market f g) = (f, g)
Expand All @@ -112,3 +132,21 @@ instance Profunctor (Market a b) where

instance Strong Either Void (Market a b) where
first (Market f g) = Market (Left . f) (either (either (Left . Left) Right . g) (Left . Right))

-- TODO: pick a more sensible name for this
newtype MyFunctor a b t = MyFunctor (b ->. (a, t))
runMyFunctor :: MyFunctor a b t ->. b ->. (a, t)
runMyFunctor (MyFunctor f) = f

instance Data.Functor (MyFunctor a b) where
fmap f (MyFunctor g) = MyFunctor (getLA (second (LA f)) . g)
instance Control.Functor (MyFunctor a b) where
fmap f (MyFunctor g) = MyFunctor (Control.fmap f . g)

newtype OtherFunctor a b t = OtherFunctor (a, b ->. t)
runOtherFunctor :: OtherFunctor a b t ->. (a, b ->. t)
runOtherFunctor (OtherFunctor f) = f
instance Data.Functor (OtherFunctor a b) where
fmap f (OtherFunctor (a,g)) = OtherFunctor (a,f . g)
instance Control.Functor (OtherFunctor a b) where
fmap f (OtherFunctor (a,g)) = OtherFunctor (a,f . g)
2 changes: 1 addition & 1 deletion src/Foreign/Marshal/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import Foreign.Marshal.Utils
import Foreign.Ptr
import Foreign.Storable
import Foreign.Storable.Tuple ()
import Prelude (($), return, (<*>))
import Prelude (($), return, (<*>), (<$>))
import Prelude.Linear hiding (($))
import System.IO.Unsafe
import qualified Unsafe.Linear as Unsafe
Expand Down
6 changes: 1 addition & 5 deletions src/Prelude/Linear.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import Prelude hiding
, foldr
, maybe
, (.)
, Functor(..)
, Functor(..), (<$>)
, Applicative(..)
, Monad(..)
, Traversable(..)
Expand Down Expand Up @@ -83,10 +83,6 @@ maybe _ f (Just y) = f y
forget :: (a ->. b) ->. a -> b
forget f x = f x

-- | Replacement for the flip function with generalized multiplicities.
flip :: (a -->.(p) b -->.(q) c) -->.(r) b -->.(q) a -->.(p) c
flip f b a = f a b

-- | Linearly typed replacement for the standard '(Prelude.<*)' function.
(<*) :: (Data.Applicative f, Consumable b) => f a ->. f b ->. f a
fa <* fb = Data.fmap (flip lseq) fa Data.<*> fb
4 changes: 4 additions & 0 deletions src/Prelude/Linear/Internal/Simple.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ foldr :: (a ->. b ->. b) -> b ->. [a] ->. b
foldr f z = \case
[] -> z
x:xs -> f x (foldr f z xs)

-- | Replacement for the flip function with generalized multiplicities.
flip :: (a -->.(p) b -->.(q) c) -->.(r) b -->.(q) a -->.(p) c
flip f b a = f a b
1 change: 1 addition & 0 deletions src/System/IO/Linear.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import qualified Control.Monad.Linear as Control
import qualified Data.Functor.Linear as Data
import GHC.Exts (State#, RealWorld)
import Prelude.Linear hiding (IO)
import Prelude ((<$>))
import qualified Unsafe.Linear as Unsafe
import qualified System.IO as System

Expand Down