focus 0.1.5.2 → 1.0.3.2
raw patch · 5 files changed
Files
- Setup.hs +0/−2
- focus.cabal +82/−45
- library/Focus.hs +340/−65
- library/Focus/Prelude.hs +74/−0
- test/Main.hs +69/−0
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
focus.cabal view
@@ -1,65 +1,102 @@-name:- focus-version:- 0.1.5.2+cabal-version: 3.0+name: focus+version: 1.0.3.2 synopsis: A general abstraction for manipulating elements of container data structures+ description: An API for construction of free-form strategies of access and manipulation of elements of arbitrary data structures. It allows to implement efficient composite patterns, e.g., a simultaneous update and lookup of an element, and even more complex things.- . Strategies are meant to be interpreted by the host data structure libraries. Thus they allow to implement all access and modification patterns of a data structure with just a single function, which interprets strategies.- . This library provides pure and monadic interfaces, so it supports both immutable and mutable data structures.-category:- Containers, Data-homepage:- https://github.com/nikita-volkov/focus -bug-reports:- https://github.com/nikita-volkov/focus/issues -author:- Nikita Volkov <nikita.y.volkov@mail.ru>-maintainer:- Nikita Volkov <nikita.y.volkov@mail.ru>-copyright:- (c) 2014, Nikita Volkov-license:- MIT-license-file:- LICENSE-build-type:- Simple-cabal-version:- >=1.10-tested-with:- GHC==7.6.3,- GHC==7.8.4,- GHC==7.10.3,- GHC==8.0.1- GHC==8.2.1 +category: Containers, Data+homepage: https://github.com/nikita-volkov/focus+bug-reports: https://github.com/nikita-volkov/focus/issues+author: Nikita Volkov <nikita.y.volkov@mail.ru>+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>+copyright: (c) 2014, Nikita Volkov+license: MIT+license-file: LICENSE+ source-repository head- type:- git- location:- git://github.com/nikita-volkov/focus.git+ type: git+ location: git://github.com/nikita-volkov/focus.git +common language-settings+ default-extensions:+ NoImplicitPrelude+ NoMonomorphismRestriction+ ApplicativeDo+ BangPatterns+ BinaryLiterals+ BlockArguments+ ConstraintKinds+ DataKinds+ DefaultSignatures+ DeriveDataTypeable+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveTraversable+ DerivingVia+ DuplicateRecordFields+ EmptyDataDecls+ FlexibleContexts+ FlexibleInstances+ FunctionalDependencies+ GADTs+ GeneralizedNewtypeDeriving+ HexFloatLiterals+ LambdaCase+ LiberalTypeSynonyms+ MultiParamTypeClasses+ MultiWayIf+ NumericUnderscores+ OverloadedLabels+ OverloadedStrings+ ParallelListComp+ PatternGuards+ PatternSynonyms+ QuasiQuotes+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ StandaloneDeriving+ StrictData+ TemplateHaskell+ TupleSections+ TypeApplications+ TypeFamilies+ TypeOperators+ UndecidableInstances+ ViewPatterns + default-language: Haskell2010+ library- hs-source-dirs:- library- exposed-modules:- Focus+ import: language-settings+ hs-source-dirs: library+ exposed-modules: Focus+ other-modules: Focus.Prelude build-depends:- base >= 4.6 && < 5- default-extensions:- DeriveFunctor, NoImplicitPrelude, TupleSections- default-language:- Haskell2010+ , base >=4.11 && <5+ , transformers >=0.5 && <0.7++test-suite test+ import: language-settings+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends:+ , focus+ , rerebase <2+ , tasty >=0.12 && <2+ , tasty-hunit >=0.9 && <0.11
library/Focus.hs view
@@ -1,115 +1,390 @@ module Focus where -import Prelude hiding (adjust, update, alter, insert, delete, lookup)-import Control.Monad-+import Focus.Prelude hiding (delete, insert, lookup) -- |--- A general modification function for some match.--- By processing a 'Maybe' value it produces some value to emit and --- a 'Decision' to perform on the match.--- --- The interpretation of this function is up to the context APIs.-type Strategy a r = Maybe a -> (r, Decision a)+-- Abstraction over the modification of an element of a datastructure.+--+-- It is composable using the standard typeclasses, e.g.:+--+-- >lookupAndDelete :: Monad m => Focus a m (Maybe a)+-- >lookupAndDelete = lookup <* delete+data Focus element m result = Focus (m (result, Change element)) (element -> m (result, Change element)) --- |--- A monadic version of 'Strategy'.-type StrategyM m a r = Maybe a -> m (r, Decision a)+deriving instance (Functor m) => Functor (Focus element m) +instance (Monad m) => Applicative (Focus element m) where+ pure a = Focus (pure (a, Leave)) (const (pure (a, Leave)))+ (<*>) = ap++instance (Monad m) => Monad (Focus element m) where+ return = pure+ (>>=) (Focus lAbsent lPresent) rk =+ Focus absent present+ where+ absent =+ do+ (lr, lChange) <- lAbsent+ let Focus rAbsent rPresent = rk lr+ case lChange of+ Leave -> rAbsent+ Remove -> rAbsent & fmap (fmap (mappend lChange))+ Set newElement -> rPresent newElement & fmap (fmap (mappend lChange))+ present element =+ do+ (lr, lChange) <- lPresent element+ let Focus rAbsent rPresent = rk lr+ case lChange of+ Leave -> rPresent element+ Remove -> rAbsent & fmap (fmap (mappend lChange))+ Set newElement -> rPresent newElement & fmap (fmap (mappend lChange))++instance MonadTrans (Focus element) where+ lift m = Focus (fmap (,Leave) m) (const (fmap (,Leave) m))+ -- | -- What to do with the focused value.--- +-- -- The interpretation of the commands is up to the context APIs.-data Decision a =- Keep |- Remove |- Replace a- deriving (Functor)+data Change a+ = -- | Produce no changes+ Leave+ | -- | Delete it+ Remove+ | -- | Set its value to the provided one+ Set a+ deriving (Functor, Eq, Ord, Show) +instance Semigroup (Change a) where+ (<>) l r =+ case r of+ Leave -> l+ _ -> r --- * Constructors for common pure patterns--------------------------+instance Monoid (Change a) where+ mempty = Leave +-- * Pure functions++-- ** Reading functions+ -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:adjust adjust>@.-{-# INLINE adjust #-}-adjust :: (a -> a) -> Strategy a ()-adjust f = maybe ((), Keep) (\a -> ((), Replace (f a)))+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:member member>@.+{-# INLINE member #-}+member :: (Monad m) => Focus a m Bool+member = fmap (maybe False (const True)) lookup -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:update update>@.-{-# INLINE update #-}-update :: (a -> Maybe a) -> Strategy a ()-update f = maybe ((), Keep) (\a -> ((), maybe Remove Replace (f a)))+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:lookup lookup>@.+{-# INLINE [1] lookup #-}+lookup :: (Monad m) => Focus a m (Maybe a)+lookup = cases (Nothing, Leave) (\a -> (Just a, Leave)) -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:alter alter>@.-{-# INLINE alter #-}-alter :: (Maybe a -> Maybe a) -> Strategy a ()-alter f = ((),) . maybe Remove Replace . f+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:findWithDefault findWithDefault>@+-- with a better name.+{-# INLINE [1] lookupWithDefault #-}+lookupWithDefault :: (Monad m) => a -> Focus a m a+lookupWithDefault a = cases (a, Leave) (\a -> (a, Leave)) +-- ** Modifying functions+ -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:insert insert>@.+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:delete delete>@.+{-# INLINE [1] delete #-}+delete :: (Monad m) => Focus a m ()+delete = unitCases Leave (const Remove)++-- |+-- Lookup an element and delete it if it exists.+--+-- Same as @'lookup' <* 'delete'@.+{-# RULES+"lookup <* delete" [~1] lookup <* delete = lookupAndDelete+ #-}++{-# INLINE lookupAndDelete #-}+lookupAndDelete :: (Monad m) => Focus a m (Maybe a)+lookupAndDelete = cases (Nothing, Leave) (\element -> (Just element, Remove))++-- |+-- Reproduces the behaviour of+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:insert insert>@. {-# INLINE insert #-}-insert :: a -> Strategy a ()-insert a = const ((), Replace a)+insert :: (Monad m) => a -> Focus a m ()+insert a = unitCases (Set a) (const (Set a)) -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:delete delete>@.-{-# INLINE delete #-}-delete :: Strategy a ()-delete = const ((), Remove)+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:insertWith insertWith>@+-- with a better name.+{-# INLINE insertOrMerge #-}+insertOrMerge :: (Monad m) => (a -> a -> a) -> a -> Focus a m ()+insertOrMerge merge value = unitCases (Set value) (Set . merge value) -- | -- Reproduces the behaviour of--- @Data.Map.<http://hackage.haskell.org/package/containers-0.5.5.1/docs/Data-Map-Lazy.html#v:lookup lookup>@.-{-# INLINE lookup #-}-lookup :: Strategy a (Maybe a)-lookup r = (r, Keep)+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:alter alter>@.+{-# INLINE alter #-}+alter :: (Monad m) => (Maybe a -> Maybe a) -> Focus a m ()+alter fn = unitCases (maybe Leave Set (fn Nothing)) (maybe Remove Set . fn . Just) +-- |+-- Reproduces the behaviour of+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:adjust adjust>@.+{-# INLINE adjust #-}+adjust :: (Monad m) => (a -> a) -> Focus a m ()+adjust fn = unitCases Leave (Set . fn) --- * Constructors for monadic patterns--------------------------+-- |+-- Reproduces the behaviour of+-- @Data.Map.<http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#v:update update>@.+{-# INLINE update #-}+update :: (Monad m) => (a -> Maybe a) -> Focus a m ()+update fn = unitCases Leave (maybe Remove Set . fn) -- |+-- Same as all of the following expressions:+--+-- @\f g -> fmap (fmap f) lookup <* adjust g@+-- @\f g -> liftStateFn (f &&& g)@+-- @\f g -> liftStateFn ((,) <$> f <*> g)@+accessAndAdjust :: (Monad m) => (s -> a) -> (s -> s) -> Focus s m (Maybe a)+accessAndAdjust f g =+ liftStateFn (f &&& g)++-- |+-- Lift a pure state monad.+liftState :: (Monad m) => State s a -> Focus s m (Maybe a)+liftState (StateT fn) =+ liftStateFn (runIdentity . fn)++-- |+-- Lift a pure state-monad-like function.+liftStateFn :: (Monad m) => (s -> (a, s)) -> Focus s m (Maybe a)+liftStateFn fn =+ Focus+ (return (Nothing, Leave))+ (\s -> case fn s of (a, s) -> return (Just a, Set s))++-- ** Construction utils++-- |+-- Lift pure functions which handle the cases of presence and absence of the element.+{-# INLINE cases #-}+cases :: (Monad m) => (b, Change a) -> (a -> (b, Change a)) -> Focus a m b+cases sendNone sendSome = Focus (return sendNone) (return . sendSome)++-- |+-- Lift pure functions which handle the cases of presence and absence of the element and produce no result.+{-# INLINE unitCases #-}+unitCases :: (Monad m) => Change a -> (a -> Change a) -> Focus a m ()+unitCases sendNone sendSome = cases ((), sendNone) (\a -> ((), sendSome a))++-- * Monadic functions++-- ** Reading functions++-- |+-- A monadic version of 'lookupWithDefault'.+{-# INLINE [1] lookupWithDefaultM #-}+lookupWithDefaultM :: (Monad m) => m a -> Focus a m a+lookupWithDefaultM aM = casesM (liftM2 (,) aM (return Leave)) (\a -> return (a, Leave))++-- ** Modifying functions++-- |+-- A monadic version of 'insert'.+{-# INLINE insertM #-}+insertM :: (Monad m) => m a -> Focus a m ()+insertM aM = unitCasesM (fmap Set aM) (const (fmap Set aM))++-- |+-- A monadic version of 'insertOrMerge'.+{-# INLINE insertOrMergeM #-}+insertOrMergeM :: (Monad m) => (a -> a -> m a) -> m a -> Focus a m ()+insertOrMergeM merge aM = unitCasesM (fmap Set aM) (\a' -> aM >>= \a -> fmap Set (merge a a'))++-- |+-- A monadic version of 'alter'.+{-# INLINE alterM #-}+alterM :: (Monad m) => (Maybe a -> m (Maybe a)) -> Focus a m ()+alterM fn = unitCasesM (fmap (maybe Leave Set) (fn Nothing)) (fmap (maybe Remove Set) . fn . Just)++-- | -- A monadic version of 'adjust'. {-# INLINE adjustM #-}-adjustM :: (Monad m) => (a -> m a) -> StrategyM m a ()-adjustM f = maybe (return ((), Keep)) (liftM (((),) . Replace) . f)+adjustM :: (Monad m) => (a -> m a) -> Focus a m ()+adjustM fn = updateM (fmap Just . fn) -- | -- A monadic version of 'update'. {-# INLINE updateM #-}-updateM :: (Monad m) => (a -> m (Maybe a)) -> StrategyM m a ()-updateM f = maybe (return ((), Keep)) (liftM (((),) . maybe Remove Replace) . f)+updateM :: (Monad m) => (a -> m (Maybe a)) -> Focus a m ()+updateM fn = unitCasesM (return Leave) (fmap (maybe Remove Set) . fn) +-- ** Construction utils+ -- |--- A monadic version of 'alter'.-{-# INLINE alterM #-}-alterM :: (Monad m) => (Maybe a -> m (Maybe a)) -> StrategyM m a ()-alterM f = liftM (((),) . maybe Remove Replace) . f+-- Lift monadic functions which handle the cases of presence and absence of the element.+{-# INLINE casesM #-}+casesM :: m (b, Change a) -> (a -> m (b, Change a)) -> Focus a m b+casesM sendNone sendSome = Focus sendNone sendSome -- |--- A monadic version of 'insert'.-{-# INLINE insertM #-}-insertM :: (Monad m) => a -> StrategyM m a ()-insertM = fmap return . insert+-- Lift monadic functions which handle the cases of presence and absence of the element and produce no result.+{-# INLINE unitCasesM #-}+unitCasesM :: (Monad m) => m (Change a) -> (a -> m (Change a)) -> Focus a m ()+unitCasesM sendNone sendSome = Focus (fmap ((),) sendNone) (\a -> fmap ((),) (sendSome a)) +-- * Composition+ -- |--- A monadic version of 'delete'.-{-# INLINE deleteM #-}-deleteM :: (Monad m) => StrategyM m a ()-deleteM = fmap return delete+-- Map the Focus input.+{-# INLINE mappingInput #-}+mappingInput :: (Monad m) => (a -> b) -> (b -> a) -> Focus a m x -> Focus b m x+mappingInput aToB bToA (Focus consealA revealA) = Focus consealB revealB+ where+ consealB = do+ (x, aChange) <- consealA+ return (x, fmap aToB aChange)+ revealB b = do+ (x, aChange) <- revealA (bToA b)+ return (x, fmap aToB aChange) +-- * Change-inspecting functions+ -- |--- A monadic version of 'lookup'.-{-# INLINE lookupM #-}-lookupM :: (Monad m) => StrategyM m a (Maybe a)-lookupM = fmap return lookup+-- Extends the output with the input.+{-# INLINE extractingInput #-}+extractingInput :: (Monad m) => Focus a m b -> Focus a m (b, Maybe a)+extractingInput (Focus absent present) =+ Focus newAbsent newPresent+ where+ newAbsent = do+ (b, change) <- absent+ return ((b, Nothing), change)+ newPresent element = do+ (b, change) <- present element+ return ((b, Just element), change) +-- |+-- Extends the output with the change performed.+{-# INLINE extractingChange #-}+extractingChange :: (Monad m) => Focus a m b -> Focus a m (b, Change a)+extractingChange (Focus absent present) =+ Focus newAbsent newPresent+ where+ newAbsent = do+ (b, change) <- absent+ return ((b, change), change)+ newPresent element = do+ (b, change) <- present element+ return ((b, change), change) +-- |+-- Extends the output with a projection on the change that was performed.+{-# INLINE projectingChange #-}+projectingChange :: (Monad m) => (Change a -> c) -> Focus a m b -> Focus a m (b, c)+projectingChange fn (Focus absent present) =+ Focus newAbsent newPresent+ where+ newAbsent = do+ (b, change) <- absent+ return ((b, fn change), change)+ newPresent element = do+ (b, change) <- present element+ return ((b, fn change), change)++-- |+-- Extends the output with a flag,+-- signaling whether a change, which is not 'Leave', has been introduced.+{-# INLINE testingIfModifies #-}+testingIfModifies :: (Monad m) => Focus a m b -> Focus a m (b, Bool)+testingIfModifies =+ projectingChange $ \case+ Leave -> False+ _ -> True++-- |+-- Extends the output with a flag,+-- signaling whether the 'Remove' change has been introduced.+{-# INLINE testingIfRemoves #-}+testingIfRemoves :: (Monad m) => Focus a m b -> Focus a m (b, Bool)+testingIfRemoves =+ projectingChange $ \case+ Remove -> True+ _ -> False++-- |+-- Extends the output with a flag,+-- signaling whether an item will be inserted.+-- That is, it didn't exist before and a 'Set' change is introduced.+{-# INLINE testingIfInserts #-}+testingIfInserts :: (Monad m) => Focus a m b -> Focus a m (b, Bool)+testingIfInserts (Focus absent present) =+ Focus newAbsent newPresent+ where+ newAbsent = do+ (output, change) <- absent+ let testResult = case change of+ Set _ -> True+ _ -> False+ in return ((output, testResult), change)+ newPresent element = do+ (output, change) <- present element+ return ((output, False), change)++-- |+-- Extend the output with a flag, signaling how the size will be affected by the change.+{-# INLINE testingSizeChange #-}+testingSizeChange ::+ (Monad m) =>+ -- | Decreased+ sizeChange ->+ -- | Didn't change+ sizeChange ->+ -- | Increased+ sizeChange ->+ Focus a m b ->+ Focus a m (b, sizeChange)+testingSizeChange dec none inc (Focus absent present) =+ Focus newAbsent newPresent+ where+ newAbsent = do+ (output, change) <- absent+ let sizeChange = case change of+ Set _ -> inc+ _ -> none+ in return ((output, sizeChange), change)+ newPresent element = do+ (output, change) <- present element+ let sizeChange = case change of+ Remove -> dec+ _ -> none+ in return ((output, sizeChange), change)++-- * STM++-- |+-- Focus on the contents of a TVar.+{-# INLINE onTVarValue #-}+onTVarValue :: Focus a STM b -> Focus (TVar a) STM b+onTVarValue (Focus concealA presentA) = Focus concealTVar presentTVar+ where+ concealTVar = concealA >>= traverse interpretAChange+ where+ interpretAChange = \case+ Leave -> return Leave+ Set !a -> Set <$> newTVar a+ Remove -> return Leave+ presentTVar var = readTVar var >>= presentA >>= traverse interpretAChange+ where+ interpretAChange = \case+ Leave -> return Leave+ Set !a -> writeTVar var a $> Leave+ Remove -> return Remove
+ library/Focus/Prelude.hs view
@@ -0,0 +1,74 @@+module Focus.Prelude+ ( module Exports,+ )+where++import Control.Applicative as Exports+import Control.Arrow as Exports+import Control.Category as Exports+import Control.Concurrent as Exports+import Control.Exception as Exports+import Control.Monad as Exports hiding (forM, forM_, mapM, mapM_, msum, sequence, sequence_)+import Control.Monad.Fix as Exports hiding (fix)+import Control.Monad.IO.Class as Exports+import Control.Monad.ST as Exports+import Control.Monad.Trans.Class as Exports+import Control.Monad.Trans.Cont as Exports hiding (callCC, shift)+import Control.Monad.Trans.Except as Exports (Except, ExceptT (ExceptT), catchE, except, mapExcept, mapExceptT, runExcept, runExceptT, throwE, withExcept, withExceptT)+import Control.Monad.Trans.Maybe as Exports+import Control.Monad.Trans.Reader as Exports (Reader, ReaderT (ReaderT), ask, mapReader, mapReaderT, runReader, runReaderT, withReader, withReaderT)+import Control.Monad.Trans.State.Strict as Exports (State, StateT (StateT), evalState, evalStateT, execState, execStateT, mapState, mapStateT, runState, runStateT, withState, withStateT)+import Control.Monad.Trans.Writer.Strict as Exports (Writer, WriterT (..), execWriter, execWriterT, mapWriter, mapWriterT, runWriter)+import Data.Bits as Exports+import Data.Bool as Exports+import Data.Char as Exports+import Data.Coerce as Exports+import Data.Complex as Exports+import Data.Data as Exports+import Data.Dynamic as Exports+import Data.Either as Exports+import Data.Fixed as Exports+import Data.Foldable as Exports+import Data.Function as Exports hiding (id, (.))+import Data.Functor as Exports hiding (unzip)+import Data.Functor.Compose as Exports+import Data.Functor.Identity as Exports+import Data.IORef as Exports+import Data.Int as Exports+import Data.Ix as Exports+import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons)+import Data.Maybe as Exports+import Data.Monoid as Exports hiding (First (..), Last (..))+import Data.Ord as Exports+import Data.Proxy as Exports+import Data.Ratio as Exports+import Data.STRef as Exports+import Data.Semigroup as Exports+import Data.String as Exports+import Data.Traversable as Exports+import Data.Tuple as Exports+import Data.Unique as Exports+import Data.Version as Exports+import Data.Word as Exports+import Debug.Trace as Exports+import Foreign.ForeignPtr as Exports+import Foreign.Ptr as Exports+import Foreign.StablePtr as Exports+import Foreign.Storable as Exports hiding (alignment, sizeOf)+import GHC.Conc as Exports hiding (threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)+import GHC.Exts as Exports (groupWith, inline, lazy, sortWith)+import GHC.Generics as Exports (Generic)+import GHC.IO.Exception as Exports+import Numeric as Exports+import System.Environment as Exports+import System.Exit as Exports+import System.IO as Exports+import System.IO.Error as Exports+import System.IO.Unsafe as Exports+import System.Mem as Exports+import System.Mem.StableName as Exports+import System.Timeout as Exports+import Text.Printf as Exports (hPrintf, printf)+import Text.Read as Exports (Read (..), readEither, readMaybe)+import Unsafe.Coerce as Exports+import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))
+ test/Main.hs view
@@ -0,0 +1,69 @@+module Main where++import qualified Focus+import Test.Tasty+import Test.Tasty.HUnit+import Prelude hiding (choose)++main :: IO ()+main =+ defaultMain+ $ testGroup ""+ $ [ testCase "Monadically composed lookup and insert (https://github.com/nikita-volkov/stm-containers/issues/25)"+ $ let Focus.Focus absent present = do+ prev <- Focus.lookup+ Focus.insert "one"+ pure (isJust prev)+ in do+ assertEqual "" (True, Focus.Set "one") $ runIdentity (present "zero")+ assertEqual "" (False, Focus.Set "one") $ runIdentity absent,+ testCase "Applicatively composed lookup and insert"+ $ let Focus.Focus absent present = (isJust <$> Focus.lookup) <* Focus.insert "one"+ in do+ assertEqual "" (True, Focus.Set "one") $ runIdentity (present "zero")+ assertEqual "" (False, Focus.Set "one") $ runIdentity absent,+ testCase "Applicatively composed pure and insert"+ $ let Focus.Focus absent present = pure () <* Focus.insert "one"+ in do+ assertEqual "" ((), Focus.Set "one") (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Set "one") (runIdentity absent),+ testCase "insert"+ $ let Focus.Focus absent present = Focus.insert "one"+ in do+ assertEqual "" ((), Focus.Set "one") (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Set "one") (runIdentity absent),+ testCase "alter"+ $ let f :: Maybe String -> Maybe String+ f = const Nothing+ Focus.Focus absent present = Focus.alter f+ in do+ assertEqual "" ((), Focus.Remove) (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Leave) (runIdentity absent),+ testCase "update"+ $ let f :: String -> Maybe String+ f = const Nothing+ Focus.Focus absent present = Focus.update f+ in do+ assertEqual "" ((), Focus.Remove) (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Leave) (runIdentity absent),+ testCase "updateM"+ $ let f :: String -> Identity (Maybe String)+ f = const (pure Nothing)+ Focus.Focus absent present = Focus.updateM f+ in do+ assertEqual "" ((), Focus.Remove) (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Leave) (runIdentity absent),+ testCase "delete"+ $ let Focus.Focus absent present = Focus.delete+ in do+ assertEqual "" ((), Focus.Remove) (runIdentity (present "zero"))+ assertEqual "" ((), Focus.Leave @()) (runIdentity absent),+ testCase "Monadically composed lookup and delete (https://github.com/nikita-volkov/focus/issues/7)"+ $ let Focus.Focus absent present = do+ a <- Focus.lookup+ Focus.delete+ pure a+ in do+ assertEqual "" (Just (), Focus.Remove) (runIdentity (present ()))+ assertEqual "" (Nothing @(), Focus.Leave) (runIdentity absent)+ ]