lens-3.7.1: src/Control/Lens/Classes.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
#if defined(TRUSTWORTHY) && !defined(SAFE)
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : Control.Lens.Classes
-- Copyright : (C) 2012 Edward Kmett
-- License : BSD-style (see the file LICENSE)
-- Maintainer : Edward Kmett <ekmett@gmail.com>
-- Stability : provisional
-- Portability : Rank2Types
--
----------------------------------------------------------------------------
module Control.Lens.Classes
(
-- * Getters
Gettable(..)
, noEffect
-- * Actions
, Effective(..)
-- * Setters
, Settable(..)
-- * Isomorphisms
, Isomorphic(..)
-- * Prisms
, Prismatic(..)
-- * Indexable
, Indexable(..)
) where
import Control.Applicative
import Control.Applicative.Backwards (Backwards(..))
import Control.Category
import Control.Monad (liftM)
import Data.Functor.Compose (Compose(..))
import Data.Functor.Identity (Identity(..))
import Data.Monoid (Dual(..))
import Prelude hiding ((.),id)
#ifndef SAFE
import Unsafe.Coerce (unsafeCoerce)
#endif
#ifndef SAFE
#define UNSAFELY(x) unsafeCoerce
#else
#define UNSAFELY(f) (\g -> g `seq` \x -> (f) (g x))
#endif
-------------------------------------------------------------------------------
-- Gettables & Accessors
-------------------------------------------------------------------------------
-- | Generalizing 'Const' so we can apply simple 'Applicative'
-- transformations to it and so we can get nicer error messages
--
-- A 'Gettable' 'Functor' ignores its argument, which it carries solely as a
-- phantom type parameter.
--
-- To ensure this, an instance of 'Gettable' is required to satisfy:
--
-- @'id' = 'fmap' f = 'coerce'@
--
-- Which is equivalent to making a @'Gettable' f@ an \"anyvariant\" functor.
--
class Functor f => Gettable f where
-- | Replace the phantom type argument.
coerce :: f a -> f b
instance Gettable (Const r) where
coerce (Const m) = Const m
instance Gettable f => Gettable (Backwards f) where
coerce = Backwards . coerce . forwards
instance (Functor f, Gettable g) => Gettable (Compose f g) where
coerce = Compose . fmap coerce . getCompose
-- | The 'mempty' equivalent for a 'Gettable' 'Applicative' 'Functor'.
noEffect :: (Applicative f, Gettable f) => f a
noEffect = coerce $ pure ()
{-# INLINE noEffect #-}
-------------------------------------------------------------------------------
-- Programming with Effects
-------------------------------------------------------------------------------
-- | An 'Effective' 'Functor' ignores its argument and is isomorphic to a 'Monad' wrapped around a value.
--
-- That said, the 'Monad' is possibly rather unrelated to any 'Applicative' structure.
class (Monad m, Gettable f) => Effective m r f | f -> m r where
effective :: m r -> f a
ineffective :: f a -> m r
instance Effective m r f => Effective m (Dual r) (Backwards f) where
effective = Backwards . effective . liftM getDual
ineffective = liftM Dual . ineffective . forwards
-----------------------------------------------------------------------------
-- Settable
-----------------------------------------------------------------------------
-- | Anything 'Settable' must be isomorphic to the 'Identity' 'Functor'.
class Applicative f => Settable f where
untainted :: f a -> a
untainted# :: (a -> f b) -> a -> b
untainted# g = g `seq` \x -> untainted (g x)
tainted# :: (a -> b) -> a -> f b
tainted# g = g `seq` \x -> pure (g x)
-- | so you can pass our a 'Control.Lens.Setter.Setter' into combinators from other lens libraries
instance Settable Identity where
untainted = runIdentity
untainted# = UNSAFELY(runIdentity)
{-# INLINE untainted #-}
tainted# = UNSAFELY(Identity)
{-# INLINE tainted# #-}
-- | 'Control.Lens.Fold.backwards'
instance Settable f => Settable (Backwards f) where
untainted = untainted . forwards
{-# INLINE untainted #-}
instance (Settable f, Settable g) => Settable (Compose f g) where
untainted = untainted . untainted . getCompose
{-# INLINE untainted #-}
-----------------------------------------------------------------------------
-- Isomorphisms
-----------------------------------------------------------------------------
-- | Used to provide overloading of isomorphism application
--
-- An instance of 'Isomorphic' is a 'Category' with a canonical mapping to it from the
-- category of isomorphisms over Haskell types.
class Category k => Isomorphic k where
-- | Build a simple isomorphism from a pair of inverse functions
--
-- @
-- 'Control.Lens.Getter.view' ('iso' f g) ≡ f
-- 'Control.Lens.Getter.view' ('Control.Lens.Iso.from' ('iso' f g)) ≡ g
-- 'Control.Lens.Setter.set' ('iso' f g) h ≡ g '.' h '.' f
-- 'Control.Lens.Setter.set' ('Control.Lens.Iso.from' ('iso' f g)) h ≡ f '.' h '.' g
-- @
iso :: Functor f => (s -> a) -> (b -> t) -> k (a -> f b) (s -> f t)
instance Isomorphic (->) where
iso sa bt afb s = bt <$> afb (sa s)
{-# INLINE iso #-}
-----------------------------------------------------------------------------
-- Prisms
-----------------------------------------------------------------------------
-- | Used to provide overloading of prisms.
--
-- An instance of 'Prismatic' is a 'Category' with a canonical mapping to it from the category
-- of embedding-projection pairs over Haskell types.
class Isomorphic k => Prismatic k where
-- | Build a 'Control.Lens.Prism.Prism'.
--
-- @'Either' t a@ is used instead of @'Maybe' a@ to permit the types of @s@ and @t@ to differ.
prism :: Applicative f => (b -> t) -> (s -> Either t a) -> k (a -> f b) (s -> f t)
instance Prismatic (->) where
prism bt seta afb = either pure (fmap bt . afb) . seta
{-# INLINE prism #-}
----------------------------------------------------------------------------
-- Indexed Internals
-----------------------------------------------------------------------------
-- | This class permits overloading of function application for things that
-- also admit a notion of a key or index.
class Indexable i k where
-- | Build a function from an 'Indexed' function
indexed :: ((i -> a) -> b) -> k a b
instance Indexable i (->) where
indexed f = f . const
{-# INLINE indexed #-}