{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -- | Field combinators. module Data.Record.Field.Combinators ( -- * Basic combinators. idL , ( # ) , (#$ ) -- * Combinators for @'Functor'@s, @'Applicative'@s and -- @'Monad'@s. , (<.#>) , (<#>) , (<##>) -- * Zippy assignment. , (*#) , (=*) -- * Assignment and modification in a State monad. , (<=:) , (<=~) -- * Utility combinator for comparisons etc. , onField ) where import Data.Record.Field.Basic import Data.Record.Label hiding ((=:)) import qualified Control.Category as C import Control.Applicative import Control.Monad import "monads-fd" Control.Monad.State.Class cantSet :: String -> a cantSet n = error $ n ++ ": cannot set values." -- | Identity lens. idL :: a :-> a idL = C.id -- | Field composition with arguments in OO-like order. infixl 8 # ( # ) :: (Field a, Field b, Dst a ~ Src b) => a -> b -> Src a :-> Dst b a # b = (field b) C.. (field a) -- | Compose fields with ordinary functions. As functions are one-way, -- the resulting field cannot be used to set values. infixl 8 #$ (#$) :: (Field a) => a -> (Dst a -> b) -> Src a :-> b ab #$ f = lens getter (cantSet "(#$)") where getter a = f . getL (field ab) $ a -- | Infix @'fmap'@ for fields. -- -- Examples: -- -- -- > persons <.#> firstName -- -- > do (v1, v2) <- takeMVar mv <.#> (field1, field2) -- > putStrLn . unlines $ [ "v1: " ++ show v1, "v2: " ++ show v2 ] -- infixl 7 <.#> (<.#>) :: (Functor f, Field a) => f (Src a) -> a -> f (Dst a) f <.#> a = fmap (.# a) f -- | @'Applicative'@ functor composition for fields. -- -- > book .# characters <#> lastName -- infixr 9 <#> (<#>) :: (Applicative f, Field a, Field b, Dst a ~ f (Src b)) => a -> b -> Src a :-> f (Dst b) ab <#> bc = lens getter setter where getter = (fmap $ getL (field bc)) . getL (field ab) -- the flip is so effects get performed for b first. setter fc = modL (field ab) $ \fb -> flip (setL (field bc)) <$> fb <*> fc -- | Flattening monadic composition for fields. -- -- > person .# superior <##> superior <##> superior <##> superior -- infixr 9 <##> (<##>) :: (Monad m, Field a, Field b, Dst a ~ m (Src b), Dst b ~ m c) => a -> b -> Src a :-> m c ab <##> bc = lens getter setter where getter = getL (field ab) >=> getL (field bc) setter mc = modL (field ab) $ \mb -> do b <- mb return $ setL (field bc) mc b -- | Zippy field reference to be used with @('=*')@. -- -- > [ rec1, rec2 ] *# field =* [ value1, value2 ] -- infixl 7 *# (*#) :: (Field b) => [Src b] -> [b] -> [Dst b] rs *# as = zipWith (.#) rs as -- | Zippy infix assignment to be used with @('*#')@. infixl 8 =* (=*) :: (Field a) => a -> [Dst a] -> [Src a :-> Src a] a =* vs = [ a =: v | v <- vs ] -- | Infix assignment for the State monad. -- -- > (field1, field2) <=: (value1, value2) -- infix 3 <=: (<=:) :: (MonadState (Src a) m, Field a) => a -> Dst a -> m () a <=: v = modify (.# a =: v) -- | Infix modification for the State monad. -- -- > (field1, field2) <=~ (f, g) -- infix 3 <=~ (<=~) :: (MonadState (Src a) m, Field a) => a -> (Dst a -> Dst a) -> m () a <=~ f = modify (.# a =~ f) -- | Utility combinator in the manner of @'Data.Function.on'@. -- -- > sortBy (compare `onField` (lastName,firstName)) persons -- infixl 0 `onField` onField :: (Field a) => (Dst a -> Dst a -> t) -> a -> Src a -> Src a -> t onField f a r1 r2 = f (r1.#a) (r2.#a)