lens-typelevel-0.1.0.0: src/Data/Type/Lens.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Data.Type.Lens
-- Copyright : (c) Justin Le 2018
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- Lenses and optics for manipulating DataKind-based types, powered by
-- /singletons/ defunctionalization.
--
-- See "Data.Type.Lens.Examples" for example usage and syntax.
--
-- For the most part, you should be able to use them just like you'd use
-- the functions from the /lens/ or /microlens/ libraries; just remember
-- to capitalize names like 'Over' and 'Set', since they are type families.
--
-- Note that the ways of "creating" a lens or optic ('Sets_', 'Traverse_',
-- 'To_', 'MkLens_', etc. are all suffixed with @_@ for convenience, to
-- reserve the underscoreless identifiers for the fully applied type family
-- as per /singletons/ library convention.
module Data.Type.Lens (
LensLike, LensLike'
-- * Setting
, ASetter
-- ** Using
-- | Ways of consuming a setter.
, Over, type (%~), sOver
, Set, type (.~), sSet
-- ** Making
-- | Ways of creating a setter-only.
, Sets_, Sets, sSets
-- * Getting
, Getting
-- ** Using
-- | Ways of consuming a getter
, View, type (^.), sView
-- ** Making
-- | Ways of creating a getter-only.
, To_, To, sTo
-- * Lenses
, ALens
-- ** Making
-- | Ways of creating a lens
, MkLens_, MkLens, sMkLens
-- ** Cloning
, CloneLens_, CloneLens, sCloneLens
-- * Traversals and Folds
, ATraversal
-- ** Using
-- | Ways of consuming traversals and folds
, Preview, type (^?), sPreview
, ToListOf, type (^..), sToListOf
, UnsafePreview, type (^?!), sUnsafePreview
-- ** Making
-- | Ways of creating traversals and folds
, Folding_, Folding, sFolding
, Folded_, Folded, sFolded
, Traverse_, Traverse, sTraverse
-- ** Cloning
, CloneTraversal_, CloneTraversal, sCloneTraversal
-- * Samples
-- | Some sample lenses and traversals
--
-- ** Tuple
, L1_, L1, sL1
, L2_, L2, sL2
-- ** List
, N(..), SN
, IxList_, IxList, sIxList
-- * Util
, type (.@)
, Sing (SZ, SS, SMkContext)
-- * Defunctionalization Symbols
, ASetterSym0, ASetterSym1, ASetterSym2, ASetterSym3, ASetterSym4
, OverSym0, OverSym1, OverSym2, OverSym3
, SetSym0, SetSym1, SetSym2, SetSym3
, SetsSym0, SetsSym1, SetsSym2, SetsSym3
, GettingSym0, GettingSym1, GettingSym2, GettingSym3
, ViewSym0, ViewSym1, ViewSym2
, ToSym0, ToSym1, ToSym2, ToSym3
, ToListOfSym0, ToListOfSym1, ToListOfSym2
, LensLikeSym0, LensLikeSym1, LensLikeSym2, LensLikeSym3, LensLikeSym4, LensLikeSym5
, LensLike'Sym0, LensLike'Sym1, LensLike'Sym2, LensLike'Sym3
, MkLensSym0, MkLensSym1, MkLensSym2, MkLensSym3, MkLensSym4
, CloneLensSym0, CloneLensSym1, CloneLensSym2, CloneLensSym3
, FoldingSym0, FoldingSym1, FoldingSym2, FoldingSym3
, FoldedSym0, FoldedSym1, FoldedSym2
, L1Sym0, L1Sym1, L1Sym2
, L2Sym0, L2Sym1, L2Sym2
, ZSym0, SSym0, SSym1
, IxListSym0, IxListSym1, IxListSym2, IxListSym3
) where
import Control.Applicative
import Data.Foldable
import Data.Functor.Identity
import Data.Monoid
import Data.Singletons.Prelude.Const
import Data.Singletons.Prelude.Foldable hiding (Traverse_)
import Data.Singletons.Prelude.Function hiding (Const, ConstSym0)
import Data.Singletons.Prelude.Functor
import Data.Singletons.Prelude.Identity
import Data.Singletons.Prelude.Maybe
import Data.Singletons.Prelude.Monoid
import Data.Singletons.TH
import Data.Type.Lens.Internal
-- | The general shape of optics in this library. ("van Laarhoven")
--
-- For different levels of polymorphism on @f@, you get different types of
-- optics:
--
-- * If @f@ can be any 'Functor', you have a Lens (see 'ALens')
-- * If @f@ is only 'Identity', you have a setter (see 'ASetter')
-- * If @f@ is only @'Const' R@ for a specific @R@, you have a getter
-- of @R@ (see 'Getting')
-- * If @f@ can be @'Const' r@ for any 'Monoid' @r@, you have a Fold.
-- * If @f@ can be any 'Applicative', you have a Traversal (see
-- 'ATraversal')
--
-- Normal lens libraries implement the constraints for lenses, folds, and
-- traversals using RankN types, but we don't do that here to avoid working
-- with RankN kinds.
type LensLike f s t a b = (a -> f b) -> (s -> f t)
-- | A 'LensLike' that does not change any types.
type LensLike' f s a = LensLike f s s a a
-- | A settable "lens". Usable with 'Over' ('%~'), constructable with 'To'
-- or any of the general lens constructors.
--
-- See 'LensLike' for more information.
type ASetter s t a b = LensLike Identity s t a b
-- | A retrieving "lens". If @r@ is fixed to a type, it's a Getter for
-- that type. If @r@ is polymorphic over all 'Monoid', then it's a Fold
-- over @a@s.
--
-- As a Getter, usable with 'View' ('^.'); as a Fold, usable with
-- 'ToListOf' ('^..'), 'Preview' ('^?'), etc.
--
-- Normal lens libraries implement the constraints for folds using RankN
-- types, but we don't do that here to avoid working with RankN kinds.
--
-- See 'LensLike' for more information.
type Getting r s a = LensLike (Const r) s s a a
-- | Peano nats, used for implementation of list index traversals in
-- a termination-sane way.
data N = Z | S N
genSingletons [''LensLike, ''LensLike', ''ASetter, ''Getting, ''N]
-- | If a function expects an 'ALens', it can be given any Lens (a
-- @'LensLike' f@ that works for any 'Functor' f).
--
-- You can use an 'ALens' as a normal lens by using 'CloneLens_'.
type ALens s t a b = LensLike (Context a b) s t a b
-- | If a function expects an 'ATraversal', it can be given any Traversal
-- (a @'LensLike' f@ that works for any 'Applicative' f).
--
-- You can use an 'ATraversal' as a normal traversal by using
-- 'CloneTraversal_'.
type ATraversal s t a b = LensLike (Bazaar a b) s t a b
$(singletonsOnly [d|
over :: ASetter s t a b -> (a -> b) -> (s -> t)
over l f x = case l (Identity . f) x of
Identity y -> y
set :: ASetter s t a b -> b -> s -> t
set l y = over l (\_ -> y)
view :: Getting a s a -> s -> a
view l x = case l Const x of
Const y -> y
sets :: ((a -> b) -> (s -> t)) -> ASetter s t a b
sets f g = Identity . f (\x -> case g x of Identity y -> y)
to :: (s -> a) -> Getting r s a
to f g x = case g (f x) of
Const y -> Const y
mkLens
:: Functor f
=> (s -> a)
-> (s -> b -> t)
-> LensLike f s t a b
mkLens v s f x = s x <$> f (v x)
cloneLens
:: Functor f
=> LensLike (Context a b) s t a b
-> LensLike f s t a b
cloneLens l f x = case l (\y -> MkContext id y) x of
MkContext g y -> g <$> f y
toListOf :: Getting [a] s a -> s -> [a]
toListOf l x = case l (Const . (:[])) x of
Const ys -> ys
preview :: Getting (First a) s a -> s -> Maybe a
preview l x = case l (Const . First . Just) x of
Const (First y) -> y
unsafePreview :: Getting (First a) s a -> s -> a
unsafePreview l x = case preview l x of
Just y -> y
Nothing -> error "Failed indexing into empty traversal"
folding :: (Foldable f, Monoid r) => (s -> f a) -> Getting r s a
folding f g x = case traverse_ g (f x) of
Const y -> Const y
folded :: (Foldable f, Monoid r) => Getting r (f a) a
folded f x = case traverse_ f x of
Const y -> Const y
cloneTraversal
:: Applicative f
=> LensLike (Bazaar a b) s t a b
-> LensLike f s t a b
cloneTraversal l f xs = unBazaar f $ l (`More` Done id) xs
l1 :: Functor f => LensLike f (a, c) (b, c) a b
l1 f (x, y) = (\x' -> (x', y)) <$> f x
l2 :: Functor f => LensLike f (a, b) (a, c) b c
l2 f (x, y) = (\y' -> (x, y')) <$> f y
ixList :: Applicative f => N -> LensLike' f [a] a
ixList _ _ [] = pure []
ixList Z f (x:xs) = (:xs) <$> f x
ixList (S i) f (x:xs) = (x:) <$> ixList i f xs
|])
-- | Infix application of 'Over'
type l %~ f = OverSym2 l f
-- | Infix application of 'Set'
type l .~ x = SetSym2 l x
-- | Infix application of 'View'
type x ^. l = View l x
-- | Infix application of 'Preview'
type x ^? l = Preview l x
-- | Infix application of 'UnsafePreview'
type x ^?! l = UnsafePreview l x
-- | Infix application of 'ToListOf'
type x ^.. l = ToListOf l x
-- | Shorter name for type-level function composition
type f .@ g = f .@#@$$$ g
infixr 4 %~
infixr 4 .~
infixl 8 ^.
infixl 8 ^?
infixl 8 ^?!
infixl 8 ^..
infixr 9 .@
-- | Create a Getter from a getting function.
--
-- @
-- 'To_' :: (a ~> b) -> 'Getting' b a b
-- @
type To_ f = ToSym1 f
-- | Create a Setter from a setting function.
--
-- @
-- 'Sets_' :: ((a ~> b) ~> (s ~> t)) -> 'ASetter' s t a b
-- @
type Sets_ f = SetsSym1 f
-- | Create a Lens from a setter and a getter.
--
-- @
-- 'MkLens_'
-- :: 'Functor' f
-- => (s ~> a)
-- -> (s ~> b ~> t)
-- -> 'LensLike' f s t a b
-- @
type MkLens_ f g = MkLensSym2 f g
-- | "Clone" a polymorphic lens so it can be used as more than one type of
-- thing (getter or setter).
--
-- @
-- 'CloneLens_'
-- :: 'Functor' f
-- => 'LensLike' (Context a b) s t a b
-- -> 'LensLike' f s t a b
-- @
--
-- Useful for writing a function that takes a lens and uses it in more than
-- one way; if you have it take an 'ALens', you can then use 'CloneLens_' to
-- use it as a getter or setter.
type CloneLens_ l = CloneLensSym1 l
-- | "Clone" a polymorphic traversal so it can be used as more than one type of
-- thing (fold, traversal, getter, setter...).
--
-- @
-- 'CloneTraversal_'
-- :: 'Functor' f
-- => 'LensLike' (Bazaar a b) s t a b
-- -> 'LensLike' f s t a b
-- @
--
-- Useful for writing a function that takes a traversal and uses it in more
-- than one way; if you have it take an 'ATraversal', you can then use
-- 'CloneTraversal_' to use it as a fold or traversal or anything else.
type CloneTraversal_ l = CloneTraversalSym1 l
-- | The canonical Traversal for any instance of 'Traversable'.
--
-- @
-- 'Traverse_'
-- :: 'Applicative' f
-- => 'LensLike' f (t a) (t b) a b
-- @
type Traverse_ = TraverseSym0
-- | Create a Fold from a "folding function":
--
-- @
-- 'Folding_'
-- :: ('Foldable' f, 'Monoid' r)
-- => (s ~> f a)
-- -> 'Getting' r s a
-- @
type Folding_ f = FoldingSym1 f
-- | The canonical Fold for any instance of 'Foldable'.
--
-- @
-- 'Folded_'
-- :: 'Monoid' r
-- => 'Getting' r (t a) a
-- @
type Folded_ = FoldedSym0
-- | Lens into the first field of a tuple
type L1_ = L1Sym0
-- | Lens into the second field of a tuple
type L2_ = L2Sym0
-- | @'IxList' i@ is a Traversal into the i-th item into a list. Defined
-- in terms of 'N' to allow for sane termination guaruntees.
--
-- @
-- 'IxList_'
-- :: 'Applicative' f
-- => 'N'
-- -> 'LensLike'' f [a] a
-- @
type IxList_ i = IxListSym1 i