packages feed

prosidy-1.6.0.2: src/Prosidy/Optics/Internal.hs

{-|
Module      : Prosidy.Optics.Internal
Description : Internal implementations of common Optics functions, removing a dependency on lens.
Copyright   : ©2020 James Alexander Feldman-Crough
License     : MPL-2.0
Maintainer  : alex@fldcr.com
-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE Safe #-}
module Prosidy.Optics.Internal
    ( module Prosidy.Optics.Internal
    , Profunctor(..)
    , Choice(..)
    , Strong(..)
    , Contravariant(..)
    )
where

import           Data.Profunctor                ( Profunctor(..)
                                                , Choice(..)
                                                , Strong(..)
                                                )
import           Data.Functor.Const             ( Const(..) )
import           Data.Monoid                    ( First(..)
                                                , Endo(..)
                                                )
import           Data.Functor.Identity          ( Identity(..) )
import           Data.Tagged                    ( Tagged(..) )
import           Data.Functor.Contravariant.Compat
                                                ( Contravariant(..) )

type Optic p f s t a b = p a (f b) -> p s (f t)
type Iso s t a b = forall p f . (Profunctor p, Functor f) => Optic p f s t a b
type Lens s t a b = forall p f . (Strong p, Functor f) => Optic p f s t a b
type Prism s t a b
    = forall p f . (Choice p, Applicative f) => Optic p f s t a b
type Affine s t a b
    = forall p f . (Choice p, Strong p, Applicative f) => Optic p f s t a b
type Traversal s t a b = forall f . (Applicative f) => Optic (->) f s t a b

type Optic' p f s a = Optic p f s s a a
type Iso' s a = Iso s s a a
type Lens' s a = Lens s s a a
type Prism' s a = Prism s s a a
type Affine' s a = Affine s s a a
type Traversal' s a = Traversal s s a a

type Getter s a = forall f . (Functor f, Contravariant f) => Optic' (->) f s a

iso :: (s -> a) -> (b -> t) -> Iso s t a b
iso get set = dimap get (fmap set)
{-# INLINE iso #-}

lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
lens get set = dimap into outof . second'
  where
    into x = (x, get x)
    outof (x, f) = fmap (set x) f
{-# INLINE lens #-}

prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
prism set get = dimap get rhs . right' where rhs = either pure (fmap set)
{-# INLINE prism #-}

prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' set get = dimap lhs rhs . right'
  where
    lhs x = maybe (Left x) Right (get x)
    rhs = either pure (fmap set)
{-# INLINE prism' #-}

affine :: (s -> Either t a) -> (s -> b -> t) -> Affine s t a b
affine get set = dimap lhs rhs . right' . second'
  where
    lhs x = fmap (x, ) $ get x
    rhs = either pure (\(x, f) -> set x <$> f)
{-# INLINE affine #-}

affine' :: (s -> Maybe a) -> (s -> b -> s) -> Affine s s a b
affine' get set = dimap lhs rhs . right' . second'
  where
    lhs x = maybe (Left x) (Right . (x, )) $ get x
    rhs (Left  x     ) = pure x
    rhs (Right (x, f)) = set x <$> f
{-# INLINE affine' #-}

nullAffine :: Affine s s a b
nullAffine = affine' (const Nothing) const
{-# INLINE nullAffine #-}

to :: (s -> a) -> Getter s a
to k = dimap k (contramap k)
{-# INLINE to #-}

view :: Lens s t a b -> s -> a
view f = getConst . f Const
{-# INLINE view #-}

views :: Traversal s t a b -> s -> [a]
views f = flip appEndo [] . getConst . f (Const . Endo . (:))
{-# INLINE views #-}

preview :: Optic (->) (Const (First a)) s t a b -> s -> Maybe a
preview f = getFirst . getConst . f (Const . First . Just)
{-# INLINE preview #-}

over :: Optic (->) Identity s t a b -> (a -> b) -> s -> t
over t f = runIdentity . t (Identity . f)
{-# INLINE over #-}

assign :: Optic' (->) Identity s a -> a -> s -> s
assign t = over t . const
{-# INLINE assign #-}

review :: Prism' s a -> a -> s
review p = runIdentity . unTagged . p . Tagged . Identity
{-# INLINE review #-}