packages feed

focuslist (empty) → 0.1.0.0

raw patch · 11 files changed

+1750/−0 lines, 11 filesdep +QuickCheckdep +basedep +containersbuild-type:Customsetup-changed

Dependencies added: QuickCheck, base, containers, doctest, focuslist, genvalidity-containers, genvalidity-hspec, hedgehog, lens, markdown-unlit, mono-traversable, tasty, tasty-hedgehog, tasty-hspec, template-haskell

Files

+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Changelog for focuslist++## Unreleased changes++## v0.1.0.0++- Initial release.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Dennis Gosnell (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,79 @@+FocusList+=========++[![Build Status](https://secure.travis-ci.org/cdepillabout/focuslist.svg)](http://travis-ci.org/cdepillabout/focuslist)+[![Hackage](https://img.shields.io/hackage/v/focuslist.svg)](https://hackage.haskell.org/package/focuslist)+[![Stackage LTS](http://stackage.org/package/focuslist/badge/lts)](http://stackage.org/lts/package/focuslist)+[![Stackage Nightly](http://stackage.org/package/focuslist/badge/nightly)](http://stackage.org/nightly/package/focuslist)+[![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)](./LICENSE)++A `FocusList` is a sequence of elements which has one element as its `Focus`. It+supports quick insertion and indexing by its implementation with+[`Seq`](http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Sequence.html#t:Seq).++## Example++Here is a short example of using `FocusList`.++```haskell+module Main where++import Data.FocusList+  ( Focus(Focus), FocusList, appendFL, fromListFL, getFocusItemFL, prependFL+  , singletonFL+  )+import Data.Foldable (toList)++-- | Create a new 'FocusList' from a list.  You must set the 'Focus' of the new+-- 'FocusList'.  The 'Focus' is counting from zero, so the @goat@ element should+-- have the 'Focus'.+--+-- If you try to specify a 'Focus' out of range from the input list,+-- 'fromListFL' will return 'Nothing'.+myFocusList :: Maybe (FocusList String)+myFocusList = fromListFL (Focus 2) ["hello", "bye", "goat", "dog"]++-- | You can get the focused element from an existing 'FocusList'+--+-- If the 'FocusList' is empty, this returns 'Nothing'.+myFocusElement :: FocusList String -> Maybe String+myFocusElement focuslist = getFocusItemFL focuslist++-- | You can append to either side of a 'FocusList'.+--+-- 'singletonFL' creates a 'FocusList' with a single element.+-- That single element will have the 'Focus'.+--+-- 'myFocusListAppended' will have a value of+-- @FocusList (Focus 1) ["bye", "hello", "foobar"]@+myFocusListAppended :: FocusList String+myFocusListAppended =+  prependFL "bye" (appendFL (singletonFL "hello") "foobar")++-- | 'FocusList' is an instance of 'Functor' and 'Foldable', so you can use+-- functions like 'fmap' and 'toList' on a 'FocusList'.+fmapAndConvertToList :: FocusList Int -> [String]+fmapAndConvertToList focuslist = toList (fmap show focuslist)++main :: IO ()+main = do+  putStrLn "myFocusList:"+  print myFocusList++  putStrLn "\nmyFocusListAppended:"+  print myFocusListAppended++  putStrLn "\nmyFocusElement myFocusListAppended:"+  print (myFocusElement myFocusListAppended)++  putStrLn "\nfmap length myFocusListAppended:"+  print (fmap length myFocusListAppended)++  putStrLn "\nfmapAndConvertToList (fmap length myFocusListAppended):"+  print (fmapAndConvertToList (fmap length myFocusListAppended))+```++## Maintainers++- [Grendel-Grendel-Grendel](https://github.com/Grendel-Grendel-Grendel)+- [cdepillabout](https://github.com/cdepillabout)
+ Setup.hs view
@@ -0,0 +1,40 @@+-- This file comes from cabal-doctest:+-- https://github.com/phadej/cabal-doctest/blob/master/simple-example+--+-- It is needed so that doctest can be run with the same options as the modules+-- are compiled with.++{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -Wall #-}++module Main (main) where++#ifndef MIN_VERSION_cabal_doctest+#define MIN_VERSION_cabal_doctest(x,y,z) 0+#endif++#if MIN_VERSION_cabal_doctest(1,0,0)++import Distribution.Extra.Doctest ( defaultMainWithDoctests )+main :: IO ()+main = defaultMainWithDoctests "focuslist-doctests"++#else++#ifdef MIN_VERSION_Cabal+-- If the macro is defined, we have new cabal-install,+-- but for some reason we don't have cabal-doctest in package-db+--+-- Probably we are running cabal sdist, when otherwise using new-build+-- workflow+#warning You are configuring this package without cabal-doctest installed. \+         The doctests test-suite will not work as a result. \+         To fix this, install cabal-doctest before configuring.+#endif++import Distribution.Simple++main :: IO ()+main = defaultMain++#endif
+ focuslist.cabal view
@@ -0,0 +1,103 @@+name:                focuslist+version:             0.1.0.0+synopsis:            Lists with a focused element+description:         Please see <https://github.com/cdepillabout/focuslist#readme README.md>.+homepage:            https://github.com/cdepillabout/focuslist+license:             BSD3+license-file:        LICENSE+author:              Dennis Gosnell and Grendel-Grendel-Grendel+maintainer:          cdep.illabout@gmail.com+copyright:           2017-2018 Dennis Gosnell+category:            Text+build-type:          Custom+cabal-version:       1.12+extra-source-files:  README.md+                   , CHANGELOG.md++custom-setup+  setup-depends:     base+                   , Cabal+                   , cabal-doctest >=1.0.2 && <1.1++-- This flag builds the example from the README.md file.  It is only used for+-- testing.  It should be enabled for CI.+flag buildreadme+  description: Build the example from the README.md file.  This+               is normally only used for testing.+  default:     False++library+  hs-source-dirs:      src+  exposed-modules:     Data.FocusList+  build-depends:       base >= 4.7 && < 5+                     , containers+                     , lens+                     , mono-traversable+                     , QuickCheck+  default-language:    Haskell2010+  ghc-options:         -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates++test-suite focuslist-doctests+  type:                exitcode-stdio-1.0+  main-is:             DocTest.hs+  -- other-modules:       Build_doctests+  hs-source-dirs:      test+  build-depends:       base+                     , doctest+                     , QuickCheck+                     , template-haskell+  default-language:    Haskell2010+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N++test-suite focuslist-test+  type:                exitcode-stdio-1.0+  main-is:             Test.hs+  hs-source-dirs:      test+  other-modules:       Test.FocusList+                     , Test.FocusList.Invariants+  build-depends:       base+                     , focuslist+                     , genvalidity-containers+                     , genvalidity-hspec+                     , hedgehog+                     , lens+                     , QuickCheck+                     , tasty+                     , tasty-hedgehog+                     , tasty-hspec+  default-language:    Haskell2010+  ghc-options:         -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -threaded -rtsopts -with-rtsopts=-N++executable focuslist-readme+  main-is:             README.lhs+  hs-source-dirs:      test/readme+  build-depends:       base+                     , focuslist+                     , markdown-unlit+  ghc-options:         -pgmL markdown-unlit+  default-language:    Haskell2010++  if flag(buildreadme)+    buildable:         True+  else+    buildable:         False+++source-repository head+  type:     git+  location: https://github.com/cdepillabout/focuslist/++-- benchmark termonad-bench+--   type:                exitcode-stdio-1.0+--   main-is:             Bench.hs+--   hs-source-dirs:      bench+--   build-depends:       base+--                      , criterion+--                      , termonad+--   default-language:    Haskell2010+--   ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N++source-repository head+  type:     git+  location: git@github.com:cdepillabout/focuslist.git+
+ src/Data/FocusList.hs view
@@ -0,0 +1,1207 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}++module Data.FocusList+  (+    -- * FocusList+    FocusList(FocusList, focusListFocus, focusList)+    -- ** Conversions+  , fromListFL+  , fromFoldableFL+  , toSeqFL+    -- ** Query+  , lengthFL+  , isEmptyFL+  , getFocusItemFL+  , lookupFL+  , indexOfFL+  , findFL+    -- *** Query 'Focus'+  , hasFocusFL+  , getFocusFL+    -- ** Manipulate+  , prependFL+  , appendFL+  , appendSetFocusFL+  , insertFL+  , removeFL+  , deleteFL+  , moveFromToFL+  , intersperseFL+  , reverseFL+    -- *** Manipulate 'Focus'+  , setFocusFL+  , updateFocusFL+    -- ** Sort+  , sortByFL+    -- ** Construction+  , emptyFL+  , singletonFL+    -- ** Unsafe Functions+  , unsafeFromListFL+  , unsafeGetFocusFL+  , unsafeGetFocusItemFL+    -- ** Invariants+  , invariantFL+    -- ** Testing+  , genValidFL+    -- ** Optics+    -- | These optics allow you to get/set the internal state of a 'FocusList'.+    -- You should make sure not to directly set the internal state of a+    -- 'FocusList' unless you are sure that the invariants for the 'FocusList'+    -- are protected.  See 'invariantFL'.+  , lensFocusListFocus+  , lensFocusList+    -- * Focus+  , Focus(Focus, NoFocus)+    , hasFocus+    , getFocus+    , maybeToFocus+    , foldFocus+    -- ** Optics+  , _Focus+  , _NoFocus+    -- ** Unsafe Functions+  , unsafeGetFocus+  ) where++import Prelude hiding (reverse)++import Control.Lens (Prism', (^.), (.~), (-~), makeLensesFor, prism')+import Data.Foldable (toList)+import Data.Function ((&))+import Data.MonoTraversable+  (Element, GrowingAppend, MonoFoldable, MonoFunctor, MonoTraversable, olength)+import qualified Data.Sequence as Sequence+import Data.Sequence+  (Seq((:<|), Empty), (<|), deleteAt, elemIndexL, insertAt, singleton)+import Data.Sequences+  (Index, SemiSequence, cons, find, intersperse, reverse, snoc, sortBy)+import GHC.Exts (fromList)+import GHC.Generics (Generic)+import Test.QuickCheck+  ( Arbitrary, Arbitrary1, CoArbitrary, Gen, arbitrary, arbitrary1, choose+  , frequency, liftArbitrary+  )+import Text.Show (Show(showsPrec), ShowS, showParen, showString)++-- $setup+-- >>> :set -XFlexibleContexts+-- >>> :set -XScopedTypeVariables+-- >>> import Data.Maybe (isJust)++-- | A 'Focus' for the 'FocusList'.+--+-- The 'Focus' is either 'NoFocus' (if the 'Focuslist' is empty), or 'Focus'+-- 'Int' to represent focusing on a specific element of the 'FocusList'.+data Focus = Focus {-# UNPACK #-} !Int | NoFocus deriving (Eq, Generic, Read, Show)++-- | 'NoFocus' is always less than 'Focus'.+--+-- prop> NoFocus < Focus a+--+-- The ordering of 'Focus' depends on the ordering of the integer contained+-- inside.+--+-- prop> (a < b) ==> (Focus a < Focus b)+instance Ord Focus where+  compare :: Focus -> Focus -> Ordering+  compare NoFocus NoFocus = EQ+  compare NoFocus (Focus _) = LT+  compare (Focus _) NoFocus = GT+  compare (Focus a) (Focus b) = compare a b++instance CoArbitrary Focus++instance Arbitrary Focus where+  arbitrary = frequency [(1, pure NoFocus), (3, fmap Focus arbitrary)]++-- | A fold function for 'Focus'.+--+-- This is similar to 'maybe' for 'Maybe'.+--+-- >>> foldFocus "empty" (\i -> "focus at " <> show i) (Focus 3)+-- "focus at 3"+--+-- >>> foldFocus Nothing Just NoFocus+-- Nothing+--+-- prop> foldFocus NoFocus Focus focus == focus+foldFocus :: b -> (Int -> b) -> Focus -> b+foldFocus b _ NoFocus = b+foldFocus _ f (Focus i) = f i++-- | A 'Prism'' for focusing on the 'Focus' constructor in a 'Focus' data type.+_Focus :: Prism' Focus Int+_Focus = prism' Focus (foldFocus Nothing Just)++-- | A 'Prism'' for focusing on the 'NoFocus' constructor in a 'Focus' data type.+_NoFocus :: Prism' Focus ()+_NoFocus = prism' (const NoFocus) (foldFocus (Just ()) (const Nothing))++-- | Returns 'True' if a 'Focus' exists, and 'False' if not.+--+-- >>> hasFocus (Focus 0)+-- True+--+-- >>> hasFocus NoFocus+-- False+--+-- /complexity/: @O(1)@+hasFocus :: Focus -> Bool+hasFocus NoFocus = False+hasFocus (Focus _) = True++-- | Get the focus index from a 'Focus'.+--+-- >>> getFocus (Focus 3)+-- Just 3+--+-- >>> getFocus NoFocus+-- Nothing+--+-- /complexity/: @O(1)@+getFocus :: Focus -> Maybe Int+getFocus NoFocus = Nothing+getFocus (Focus i) = Just i++-- | Convert a 'Maybe' 'Int' to a 'Focus'.+--+-- >>> maybeToFocus (Just 100)+-- Focus 100+--+-- >>> maybeToFocus Nothing+-- NoFocus+--+-- 'maybeToFocus' and 'getFocus' witness an isomorphism.+--+-- prop> focus == maybeToFocus (getFocus focus)+--+-- prop> maybeInt == getFocus (maybeToFocus maybeInt)+--+-- /complexity/: @O(1)@+maybeToFocus :: Maybe Int -> Focus+maybeToFocus Nothing = NoFocus+maybeToFocus (Just i) = Focus i++-- | Unsafely get the focus index from a 'Focus'.+--+-- Returns an error if 'NoFocus'.+--+-- >>> unsafeGetFocus (Focus 50)+-- 50+--+-- >>> unsafeGetFocus NoFocus+-- *** Exception: ...+-- ...+--+-- /complexity/: @O(1)@+unsafeGetFocus :: Focus -> Int+unsafeGetFocus NoFocus = error "unsafeGetFocus: NoFocus"+unsafeGetFocus (Focus i) = i++-- | A list with a given element having the 'Focus'.+--+-- 'FocusList' has some invariants that must be protected.  You should not use+-- the 'FocusList' constructor or the 'focusListFocus' or 'focusList'+-- accessors.+--+-- Implemented under the hood as a 'Seq'.+data FocusList a = FocusList+  { focusListFocus :: !Focus+  , focusList :: !(Seq a)+  } deriving (Eq, Functor, Generic)++$(makeLensesFor+    [ ("focusListFocus", "lensFocusListFocus")+    , ("focusList", "lensFocusList")+    ]+    ''FocusList+ )++instance Foldable FocusList where+  foldr f b (FocusList _ fls) = foldr f b fls++  length = lengthFL++instance Traversable FocusList where+  traverse :: Applicative f => (a -> f b) -> FocusList a -> f (FocusList b)+  traverse f (FocusList focus fls) = FocusList focus <$> traverse f fls++type instance Element (FocusList a) = a++instance MonoFunctor (FocusList a)++instance MonoFoldable (FocusList a) where+  olength = lengthFL++instance MonoTraversable (FocusList a)++instance GrowingAppend (FocusList a)++instance SemiSequence (FocusList a) where+  type Index (FocusList a) = Int++  intersperse = intersperseFL++  reverse = reverseFL++  find = findFL++  sortBy = sortByFL++  cons = prependFL++  snoc = appendFL++-- | Given a 'Gen' for @a@, generate a valid 'FocusList'.+genValidFL :: forall a. Gen a -> Gen (FocusList a)+genValidFL genA = do+  newFL <- genFL+  if invariantFL newFL+    then pure newFL+    else error "genValidFL generated an invalid FocusList!  This should never happen!"+  where+    genFL :: Gen (FocusList a)+    genFL = do+      arbList <- liftArbitrary genA+      case arbList of+        [] -> pure emptyFL+        (_:_) -> do+          let listLen = length arbList+          len <- choose (0, listLen - 1)+          pure $ unsafeFromListFL (Focus len) arbList++instance Arbitrary1 FocusList where+  liftArbitrary = genValidFL++instance Arbitrary a => Arbitrary (FocusList a) where+  arbitrary = arbitrary1++instance CoArbitrary a => CoArbitrary (FocusList a)++instance Show a => Show (FocusList a) where+  showsPrec :: Int -> FocusList a -> ShowS+  showsPrec d FocusList{..} =+    showParen (d > 10) $+      showString "FocusList " .+      showsPrec 11 focusListFocus .+      showString " " .+      showsPrec 11 (toList focusList)++-- | Get the underlying 'Seq' in a 'FocusList'.+--+-- /complexity/: @O(1)@+toSeqFL :: FocusList a -> Seq a+toSeqFL FocusList{focusList = fls} = fls++-- | Return the length of a 'FocusList'.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "parrot"]+-- >>> lengthFL fl+-- 3+--+-- /complexity/: @O(1)@+lengthFL :: FocusList a -> Int+lengthFL = length . focusList++-- | This is an invariant that the 'FocusList' must always protect.+--+-- The functions in this module should generally protect this invariant.  If+-- they do not, it is generally a bug.+--+-- The invariants are as follows:+--+-- - The 'Focus' in a 'FocusList' can never be negative.+--+-- - If there is a 'Focus', then it actually exists in+--   the 'FocusList'.+--+-- - There needs to be a 'Focus' if the length of the+--   'FocusList' is greater than 0.+--+-- /complexity/: @O(log n)@, where @n@ is the length of the 'FocusList'.+invariantFL :: FocusList a -> Bool+invariantFL fl =+  invariantFocusNotNeg &&+  invariantFocusInMap &&+  invariantFocusIfLenGT0+  where+    -- This makes sure that the 'Focus' in a 'FocusList' can never be negative.+    invariantFocusNotNeg :: Bool+    invariantFocusNotNeg =+      case fl ^. lensFocusListFocus of+        NoFocus -> True+        Focus i -> i >= 0++    -- | This makes sure that if there is a 'Focus', then it actually exists in+    -- the 'FocusList'.+    invariantFocusInMap :: Bool+    invariantFocusInMap =+      case fl ^. lensFocusListFocus of+        NoFocus -> length (fl ^. lensFocusList) == 0+        Focus i ->+          case Sequence.lookup i (fl ^. lensFocusList) of+            Nothing -> False+            Just _ -> True++    -- | This makes sure that there needs to be a 'Focus' if the length of the+    -- 'FocusList' is greater than 0.+    invariantFocusIfLenGT0 :: Bool+    invariantFocusIfLenGT0 =+      let len = lengthFL fl+          focus = fl ^. lensFocusListFocus+      in+      case focus of+        Focus _ -> len /= 0+        NoFocus -> len == 0++-- | Unsafely create a 'FocusList'.  This does not check that the focus+-- actually exists in the list.  This is an internal function and should+-- generally not be used.  It is only safe to use if you ALREADY know+-- the 'Focus' is within the list.+--+-- Instead, you should generally use 'fromListFL'.+--+-- The following is an example of using 'unsafeFromListFL' correctly.+--+-- >>> unsafeFromListFL (Focus 1) [0..2]+-- FocusList (Focus 1) [0,1,2]+--+-- >>> unsafeFromListFL NoFocus []+-- FocusList NoFocus []+--+-- 'unsafeFromListFL' can also be used uncorrectly.  The following is an+-- example of 'unsafeFromListFL' allowing you to create a 'FocusList' that does+-- not pass 'invariantFL'.+--+-- >>> unsafeFromListFL (Focus 100) [0..2]+-- FocusList (Focus 100) [0,1,2]+--+-- If 'fromListFL' returns a 'Just' 'FocusList', then 'unsafeFromListFL' should+-- return the same 'FocusList'.+--+-- /complexity/: @O(n)@ where @n@ is the length of the input list.+unsafeFromListFL :: Focus -> [a] -> FocusList a+unsafeFromListFL focus list =+  FocusList+    { focusListFocus = focus+    , focusList = fromList list+    }++-- | Safely create a 'FocusList' from a list.+--+-- >>> fromListFL (Focus 1) ["cat","dog","goat"]+-- Just (FocusList (Focus 1) ["cat","dog","goat"])+--+-- >>> fromListFL NoFocus []+-- Just (FocusList NoFocus [])+--+-- If the 'Focus' is out of range for the list, then 'Nothing' will be returned.+--+-- >>> fromListFL (Focus (-1)) ["cat","dog","goat"]+-- Nothing+--+-- >>> fromListFL (Focus 3) ["cat","dog","goat"]+-- Nothing+--+-- >>> fromListFL NoFocus ["cat","dog","goat"]+-- Nothing+--+-- /complexity/: @O(n)@ where @n@ is the length of the input list.+fromListFL :: Focus -> [a] -> Maybe (FocusList a)+fromListFL NoFocus [] = Just emptyFL+fromListFL _ [] = Nothing+fromListFL NoFocus (_:_) = Nothing+fromListFL (Focus i) list =+  let len = length list+  in+  if i < 0 || i >= len+    then Nothing+    else+      Just $+        FocusList+          { focusListFocus = Focus i+          , focusList = fromList list+          }++-- | Create a 'FocusList' from any 'Foldable' container.+--+-- This just calls 'toList' on the 'Foldable', and then passes the result to+-- 'fromListFL'.+--+-- prop> fromFoldableFL foc (foldable :: Data.Sequence.Seq Int) == fromListFL foc (toList foldable)+--+-- /complexity/: @O(n)@ where @n@ is the length of the 'Foldable'+fromFoldableFL :: Foldable f => Focus -> f a -> Maybe (FocusList a)+fromFoldableFL foc as = fromListFL foc (toList as)++-- | Create a 'FocusList' with a single element.+--+-- >>> singletonFL "hello"+-- FocusList (Focus 0) ["hello"]+--+-- /complexity/: @O(1)@+singletonFL :: a -> FocusList a+singletonFL a =+  FocusList+    { focusListFocus = Focus 0+    , focusList = singleton a+    }++-- | Create an empty 'FocusList' without a 'Focus'.+--+-- >>> emptyFL+-- FocusList NoFocus []+--+-- /complexity/: @O(1)@+emptyFL :: FocusList a+emptyFL =+  FocusList+    { focusListFocus = NoFocus+    , focusList = mempty+    }++-- | Return 'True' if the 'FocusList' is empty.+--+-- >>> isEmptyFL emptyFL+-- True+--+-- >>> isEmptyFL $ singletonFL "hello"+-- False+--+-- Any 'FocusList' with a 'Focus' should never be empty.+--+-- prop> hasFocusFL fl ==> not (isEmptyFL fl)+--+-- The opposite is also true.+--+-- /complexity/: @O(1)@+isEmptyFL :: FocusList a -> Bool+isEmptyFL fl = (lengthFL fl) == 0++-- | Append a value to the end of a 'FocusList'.+--+-- This can be thought of as a \"snoc\" operation.+--+-- >>> appendFL emptyFL "hello"+-- FocusList (Focus 0) ["hello"]+--+-- >>> appendFL (singletonFL "hello") "bye"+-- FocusList (Focus 0) ["hello","bye"]+--+-- Appending a value to an empty 'FocusList' is the same as using 'singletonFL'.+--+-- prop> appendFL emptyFL a == singletonFL a+--+-- /complexity/: @O(log n)@ where @n@ is the length of the 'FocusList'.+appendFL :: FocusList a -> a -> FocusList a+appendFL fl a =+  if isEmptyFL fl+    then singletonFL a+    else insertFL (length $ focusList fl) a fl++-- | A combination of 'appendFL' and 'setFocusFL'.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "tree"]+-- >>> appendSetFocusFL fl "pie"+-- FocusList (Focus 3) ["hello","bye","tree","pie"]+--+-- The 'Focus' will always be updated after calling 'appendSetFocusFL'.+--+-- prop> getFocusFL (appendSetFocusFL fl a) > getFocusFL fl+--+-- /complexity/: @O(log n)@ where @n@ is the length of the 'FocusList'.+appendSetFocusFL :: FocusList a -> a -> FocusList a+appendSetFocusFL fl a =+  let oldLen = length $ focusList fl+  in+  case setFocusFL oldLen (appendFL fl a) of+    Nothing -> error "Internal error with setting the focus.  This should never happen."+    Just newFL -> newFL++-- | Prepend a value to a 'FocusList'.+--+-- This can be thought of as a \"cons\" operation.+--+-- >>> prependFL "hello" emptyFL+-- FocusList (Focus 0) ["hello"]+--+-- The focus will be updated when prepending:+--+-- >>> prependFL "bye" (singletonFL "hello")+-- FocusList (Focus 1) ["bye","hello"]+--+-- Prepending to a 'FocusList' will always update the 'Focus':+--+-- prop> getFocusFL fl < getFocusFL (prependFL a fl)+--+-- /complexity/: @O(1)@+prependFL :: a -> FocusList a -> FocusList a+prependFL a fl@FocusList{ focusListFocus = focus, focusList = fls}  =+  case focus of+    NoFocus -> singletonFL a+    Focus i ->+      fl+        { focusListFocus = Focus (i+1)+        , focusList = a <| fls+        }++-- | Unsafely get the 'Focus' from a 'FocusList'.  If the 'Focus' is+-- 'NoFocus', this function returns 'error'.+--+-- This function is only safe if you already have knowledge that+-- the 'FocusList' has a 'Focus'.+--+-- Generally, 'getFocusFL' should be used instead of this function.+--+-- >>> let Just fl = fromListFL (Focus 1) [0..9]+-- >>> unsafeGetFocusFL fl+-- 1+--+-- >>> unsafeGetFocusFL emptyFL+-- *** Exception: ...+-- ...+--+-- /complexity/: @O(1)@+unsafeGetFocusFL :: FocusList a -> Int+unsafeGetFocusFL fl =+  let focus = fl ^. lensFocusListFocus+  in+  case focus of+    NoFocus -> error "unsafeGetFocusFL: the focus list doesn't have a focus"+    Focus i -> i++-- | Return 'True' if the 'Focus' in a 'FocusList' exists.+--+-- Return 'False' if the 'Focus' in a 'FocusList' is 'NoFocus'.+--+-- >>> hasFocusFL $ singletonFL "hello"+-- True+--+-- >>> hasFocusFL emptyFL+-- False+--+-- /complexity/: @O(1)@+hasFocusFL :: FocusList a -> Bool+hasFocusFL = hasFocus . getFocusFL++-- | Get the 'Focus' from a 'FocusList'.+--+-- >>> getFocusFL $ singletonFL "hello"+-- Focus 0+--+-- >>> let Just fl = fromListFL (Focus 3) [0..9]+-- >>> getFocusFL fl+-- Focus 3+--+-- >>> getFocusFL emptyFL+-- NoFocus+--+-- /complexity/: @O(1)@+getFocusFL :: FocusList a -> Focus+getFocusFL FocusList{focusListFocus} = focusListFocus++-- | Unsafely get the value of the 'Focus' from a 'FocusList'.  If the 'Focus' is+-- 'NoFocus', this function returns 'error'.+--+-- This function is only safe if you already have knowledge that the 'FocusList'+-- has a 'Focus'.+--+-- Generally, 'getFocusItemFL' should be used instead of this function.+--+-- >>> let Just fl = fromListFL (Focus 0) ['a'..'c']+-- >>> unsafeGetFocusItemFL fl+-- 'a'+--+-- >>> unsafeGetFocusFL emptyFL+-- *** Exception: ...+-- ...+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is the 'Focus', and @n@+-- is the length of the 'FocusList'.+unsafeGetFocusItemFL :: FocusList a -> a+unsafeGetFocusItemFL fl =+  let focus = fl ^. lensFocusListFocus+  in+  case focus of+    NoFocus -> error "unsafeGetFocusItemFL: the focus list doesn't have a focus"+    Focus i ->+      let fls = fl ^. lensFocusList+      in+      case Sequence.lookup i fls of+        Nothing ->+          error $+            "unsafeGetFocusItemFL: internal error, i (" <>+            show i <>+            ") doesnt exist in sequence"+        Just a -> a++-- | Get the item the 'FocusList' is focusing on.  Return 'Nothing' if the+-- 'FocusList' is empty.+--+-- >>> let Just fl = fromListFL (Focus 0) ['a'..'c']+-- >>> getFocusItemFL fl+-- Just 'a'+--+-- >>> getFocusItemFL emptyFL+-- Nothing+--+-- This will always return 'Just' if there is a 'Focus'.+--+-- prop> hasFocusFL fl ==> isJust (getFocusItemFL fl)+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is the 'Focus', and @n@+-- is the length of the 'FocusList'.+getFocusItemFL :: FocusList a -> Maybe a+getFocusItemFL fl =+  let focus = fl ^. lensFocusListFocus+  in+  case focus of+    NoFocus -> Nothing+    Focus i ->+      let fls = fl ^. lensFocusList+      in+      case Sequence.lookup i fls of+        Nothing ->+          error $+            "getFocusItemFL: internal error, i (" <>+            show i <>+            ") doesnt exist in sequence"+        Just a -> Just a++-- | Lookup the element at the specified index, counting from 0.+--+-- >>> let Just fl = fromListFL (Focus 0) ['a'..'c']+-- >>> lookupFL 0 fl+-- Just 'a'+--+-- Returns 'Nothing' if the index is out of bounds.+--+-- >>> let Just fl = fromListFL (Focus 0) ['a'..'c']+-- >>> lookupFL 100 fl+-- Nothing+-- >>> lookupFL (-1) fl+-- Nothing+--+-- Always returns 'Nothing' if the 'FocusList' is empty.+--+-- prop> lookupFL i emptyFL == Nothing+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is the index you want to look up, and @n@+-- is the length of the 'FocusList'.+lookupFL+  :: Int  -- ^ Index to lookup.+  -> FocusList a+  -> Maybe a+lookupFL i fl = Sequence.lookup i (fl ^. lensFocusList)++-- | Insert a new value into the 'FocusList'.  The 'Focus' of the list is+-- changed appropriately.+--+-- Inserting an element into an empty 'FocusList' will set the 'Focus' on+-- that element.+--+-- >>> insertFL 0 "hello" emptyFL+-- FocusList (Focus 0) ["hello"]+--+-- The 'Focus' will not be changed if you insert a new element after the+-- current 'Focus'.+--+-- >>> insertFL 1 "hello" (singletonFL "bye")+-- FocusList (Focus 0) ["bye","hello"]+--+-- The 'Focus' will be bumped up by one if you insert a new element before+-- the current 'Focus'.+--+-- >>> insertFL 0 "hello" (singletonFL "bye")+-- FocusList (Focus 1) ["hello","bye"]+--+-- Behaves like @Data.Sequence.'Data.Sequence.insertAt'@. If the index is out of bounds, it will be+-- inserted at the nearest available index+--+-- >>> insertFL 100 "hello" emptyFL+-- FocusList (Focus 0) ["hello"]+--+-- >>> insertFL 100 "bye" (singletonFL "hello")+-- FocusList (Focus 0) ["hello","bye"]+--+-- >>> insertFL (-1) "bye" (singletonFL "hello")+-- FocusList (Focus 1) ["bye","hello"]+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is the index you want to insert at, and @n@+-- is the length of the 'FocusList'.+insertFL+  :: Int  -- ^ The index at which to insert the new element.+  -> a    -- ^ The new element.+  -> FocusList a+  -> FocusList a+insertFL _ a FocusList {focusListFocus = NoFocus} = singletonFL a+insertFL i a fl@FocusList{focusListFocus = Focus focus, focusList = fls} =+  if i > focus+    then+      fl+        { focusList = insertAt i a fls+        }+    else+      fl+        { focusList = insertAt i a fls+        , focusListFocus = Focus $ focus + 1+        }++-- | Remove an element from a 'FocusList'.+--+-- If the element to remove is not the 'Focus', then update the 'Focus'+-- accordingly.+--+-- For example, if the 'Focus' is on index 1, and we have removed index 2, then+-- the focus is not affected, so it is not changed.+--+-- >>> let focusList = unsafeFromListFL (Focus 1) ["cat","goat","dog","hello"]+-- >>> removeFL 2 focusList+-- Just (FocusList (Focus 1) ["cat","goat","hello"])+--+-- If the 'Focus' is on index 2 and we have removed index 1, then the 'Focus'+-- will be moved back one element to set to index 1.+--+-- >>> let focusList = unsafeFromListFL (Focus 2) ["cat","goat","dog","hello"]+-- >>> removeFL 1 focusList+-- Just (FocusList (Focus 1) ["cat","dog","hello"])+--+-- If we remove the 'Focus', then the next item is set to have the 'Focus'.+--+-- >>> let focusList = unsafeFromListFL (Focus 0) ["cat","goat","dog","hello"]+-- >>> removeFL 0 focusList+-- Just (FocusList (Focus 0) ["goat","dog","hello"])+--+-- If the element to remove is the only element in the list, then the 'Focus'+-- will be set to 'NoFocus'.+--+-- >>> let focusList = unsafeFromListFL (Focus 0) ["hello"]+-- >>> removeFL 0 focusList+-- Just (FocusList NoFocus [])+--+-- If the 'Int' for the index to remove is either less than 0 or greater then+-- the length of the list, then 'Nothing' is returned.+--+-- >>> let focusList = unsafeFromListFL (Focus 0) ["hello"]+-- >>> removeFL (-1) focusList+-- Nothing+--+-- >>> let focusList = unsafeFromListFL (Focus 1) ["hello","bye","cat"]+-- >>> removeFL 3 focusList+-- Nothing+--+-- If the 'FocusList' passed in is 'Empty', then 'Nothing' is returned.+--+-- >>> removeFL 0 emptyFL+-- Nothing+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is index of the element to remove, and @n@+-- is the length of the 'FocusList'.+removeFL+  :: Int          -- ^ Index of the element to remove from the 'FocusList'.+  -> FocusList a  -- ^ The 'FocusList' to remove an element from.+  -> Maybe (FocusList a)+removeFL i fl@FocusList{focusList = fls}+  | i < 0 || i >= (lengthFL fl) || isEmptyFL fl =+    -- Return Nothing if the removal position is out of bounds.+    Nothing+  | lengthFL fl == 1 =+    -- Return an empty focus list if there is currently only one element+    Just emptyFL+  | otherwise =+    let newFL = fl {focusList = deleteAt i fls}+        focus = unsafeGetFocusFL fl+    in+    if focus >= i && focus /= 0+      then Just $ newFL & lensFocusListFocus . _Focus -~ 1+      else Just newFL++-- | Find the index of the first element in the 'FocusList'.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "tree"]+-- >>> indexOfFL "hello" fl+-- Just 0+--+-- If more than one element exists, then return the index of the first one.+--+-- >>> let Just fl = fromListFL (Focus 1) ["dog", "cat", "cat"]+-- >>> indexOfFL "cat" fl+-- Just 1+--+-- If the element doesn't exist, then return 'Nothing'+--+-- >>> let Just fl = fromListFL (Focus 1) ["foo", "bar", "baz"]+-- >>> indexOfFL "hogehoge" fl+-- Nothing+indexOfFL :: Eq a => a -> FocusList a -> Maybe Int+indexOfFL a FocusList{focusList = fls} =+  elemIndexL a fls++-- | Delete an element from a 'FocusList'.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "tree"]+-- >>> deleteFL "bye" fl+-- FocusList (Focus 0) ["hello","tree"]+--+-- The focus will be updated if an item before it is deleted.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "tree"]+-- >>> deleteFL "hello" fl+-- FocusList (Focus 0) ["bye","tree"]+--+-- If there are multiple matching elements in the 'FocusList', remove them all.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "bye"]+-- >>> deleteFL "bye" fl+-- FocusList (Focus 0) ["hello"]+--+-- If there are no matching elements, return the original 'FocusList'.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "good", "bye"]+-- >>> deleteFL "frog" fl+-- FocusList (Focus 2) ["hello","good","bye"]+deleteFL+  :: forall a.+     (Eq a)+  => a+  -> FocusList a+  -> FocusList a+deleteFL item = go+  where+    go :: FocusList a -> FocusList a+    go fl =+      let maybeIndex = indexOfFL item fl+      in+      case maybeIndex of+        Nothing -> fl+        Just i ->+          let maybeNewFL = removeFL i fl+          in+          case maybeNewFL of+            Nothing -> fl+            Just newFL -> go newFL++-- | Set the 'Focus' for a 'FocusList'.+--+-- This is just like 'updateFocusFL', but doesn't return the newly focused item.+--+-- prop> setFocusFL i fl == fmap snd (updateFocusFL i fl)+--+-- /complexity/: @O(1)@+setFocusFL :: Int -> FocusList a -> Maybe (FocusList a)+setFocusFL i fl+  -- Can't set a 'Focus' for an empty 'FocusList'.+  | isEmptyFL fl = Nothing+  | otherwise =+    let len = lengthFL fl+    in+    if i < 0 || i >= len+      then Nothing+      else Just $ fl & lensFocusListFocus . _Focus .~ i++-- | Update the 'Focus' for a 'FocusList' and get the new focused element.+--+-- >>> updateFocusFL 1 =<< fromListFL (Focus 2) ["hello","bye","dog","cat"]+-- Just ("bye",FocusList (Focus 1) ["hello","bye","dog","cat"])+--+-- If the 'FocusList' is empty, then return 'Nothing'.+--+-- >>> updateFocusFL 1 emptyFL+-- Nothing+--+-- If the new focus is less than 0, or greater than or equal to the length of+-- the 'FocusList', then return 'Nothing'.+--+-- >>> updateFocusFL (-1) =<< fromListFL (Focus 2) ["hello","bye","dog","cat"]+-- Nothing+--+-- >>> updateFocusFL 4 =<< fromListFL (Focus 2) ["hello","bye","dog","cat"]+-- Nothing+--+-- /complexity/: @O(log(min(i, n - i)))@ where @i@ is the new index to put the 'Focus' on,+-- and @n@ -- is the length of the 'FocusList'.+updateFocusFL+  :: Int  -- ^ The new index to put the 'Focus' on.+  -> FocusList a+  -> Maybe (a, FocusList a)+  -- ^ A tuple of the new element that gets the 'Focus', and the new+  -- 'FocusList'.+updateFocusFL i fl+  | isEmptyFL fl = Nothing+  | otherwise =+    let len = lengthFL fl+    in+    if i < 0 || i >= len+      then Nothing+      else+        let newFL = fl & lensFocusListFocus . _Focus .~ i+        in Just (unsafeGetFocusItemFL newFL, newFL)++-- | Find a value in a 'FocusList'.  Similar to @Data.List.'Data.List.find'@.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "tree"]+-- >>> findFL (\a -> a == "hello") fl+-- Just "hello"+--+-- This will only find the first value.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "bye"]+-- >>> findFL (\a -> a == "bye") fl+-- Just "bye"+--+-- If no values match the comparison, this will return 'Nothing'.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "parrot"]+-- >>> findFL (\a -> a == "ball") fl+-- Nothing+--+-- /complexity/: @O(n)@ where @n@ is the length of the 'FocusList'.+findFL :: (a -> Bool) -> FocusList a -> Maybe (a)+findFL p fl =+  let fls = fl ^. lensFocusList+  in find p fls++-- | Move an existing item in a 'FocusList' to a new index.+--+-- The 'Focus' gets updated appropriately when moving items.+--+-- >>> let Just fl = fromListFL (Focus 1) ["hello", "bye", "parrot"]+-- >>> moveFromToFL 0 1 fl+-- Just (FocusList (Focus 0) ["bye","hello","parrot"])+--+-- The 'Focus' may not get updated if it is not involved.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "parrot"]+-- >>> moveFromToFL 1 2 fl+-- Just (FocusList (Focus 0) ["hello","parrot","bye"])+--+-- If the element with the 'Focus' is moved, then the 'Focus' will be updated+-- appropriately.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "parrot"]+-- >>> moveFromToFL 2 0 fl+-- Just (FocusList (Focus 0) ["parrot","hello","bye"])+--+-- If the index of the item to move is out bounds, then 'Nothing' will be returned.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "parrot"]+-- >>> moveFromToFL 3 0 fl+-- Nothing+--+-- If the new index is out of bounds, then 'Nothing' wil be returned.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "parrot"]+-- >>> moveFromToFL 1 (-1) fl+-- Nothing+--+-- /complexity/: @O(log n)@ where @n@ is the length of the 'FocusList'.+moveFromToFL+  :: Show a+  => Int  -- ^ Index of the item to move.+  -> Int  -- ^ New index for the item.+  -> FocusList a+  -> Maybe (FocusList a)+moveFromToFL oldPos newPos fl+  | oldPos < 0 || oldPos >= length fl = Nothing+  | newPos < 0 || newPos >= length fl = Nothing+  | otherwise =+    let oldFocus = fl ^. lensFocusListFocus+    in+    case lookupFL oldPos fl of+      Nothing -> error "moveFromToFL should have been able to lookup the item"+      Just item ->+        case removeFL oldPos fl of+          Nothing -> error "moveFromToFL should have been able to remove old position"+          Just flAfterRemove ->+            let flAfterInsert = insertFL newPos item flAfterRemove in+                if Focus oldPos == oldFocus+                  then+                    case setFocusFL newPos flAfterInsert of+                      Nothing -> error "moveFromToFL should have been able to reset the focus"+                      Just flWithUpdatedFocus -> Just flWithUpdatedFocus+                  else Just flAfterInsert++-- | Intersperse a new element between existing elements in the 'FocusList'.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "cat"]+-- >>> intersperseFL "foo" fl+-- FocusList (Focus 0) ["hello","foo","bye","foo","cat"]+--+-- The 'Focus' is updated accordingly.+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "cat", "goat"]+-- >>> intersperseFL "foo" fl+-- FocusList (Focus 4) ["hello","foo","bye","foo","cat","foo","goat"]+--+-- The item with the 'Focus' should never change after calling 'intersperseFL'.+--+-- prop> getFocusItemFL (fl :: FocusList Int) == getFocusItemFL (intersperseFL a fl)+--+-- 'intersperseFL' should not have any effect on a 'FocusList' with less than+-- two items.+--+-- prop> emptyFL == intersperseFL x emptyFL+-- prop> singletonFL a == intersperseFL x (singletonFL a)+--+-- /complexity/: @O(n)@ where @n@ is the length of the 'FocusList'.+intersperseFL :: a -> FocusList a -> FocusList a+intersperseFL _ FocusList{focusListFocus = NoFocus} = emptyFL+intersperseFL a FocusList{focusList = fls, focusListFocus = Focus foc} =+  let newFLS = intersperse a fls+  in+  FocusList+    { focusList = newFLS+    , focusListFocus = Focus (foc * 2)+    }++-- | Reverse a 'FocusList'.  The 'Focus' is updated accordingly.+--+-- >>> let Just fl = fromListFL (Focus 0) ["hello", "bye", "cat"]+-- >>> reverseFL fl+-- FocusList (Focus 2) ["cat","bye","hello"]+--+-- >>> let Just fl = fromListFL (Focus 2) ["hello", "bye", "cat", "goat"]+-- >>> reverseFL fl+-- FocusList (Focus 1) ["goat","cat","bye","hello"]+--+-- The item with the 'Focus' should never change after calling 'intersperseFL'.+--+-- prop> getFocusItemFL (fl :: FocusList Int) == getFocusItemFL (reverseFL fl)+--+-- Reversing twice should not change anything.+--+-- prop> (fl :: FocusList Int) == reverseFL (reverseFL fl)+--+-- Reversing empty lists and single lists should not do anything.+--+-- prop> emptyFL == reverseFL emptyFL+-- prop> singletonFL a == reverseFL (singletonFL a)+--+-- /complexity/: @O(n)@ where @n@ is the length of the 'FocusList'.+reverseFL :: FocusList a -> FocusList a+reverseFL FocusList{focusListFocus = NoFocus} = emptyFL+reverseFL FocusList{focusList = fls, focusListFocus = Focus foc} =+  let newFLS = reverse fls+      newFLSLen = length newFLS+  in+  FocusList+    { focusList = newFLS+    , focusListFocus = Focus (newFLSLen - foc - 1)+    }++-- | Sort a 'FocusList'.+--+-- The 'Focus' will stay with the element that has the 'Focus'.+--+-- >>> let Just fl = fromListFL (Focus 2) ["b", "c", "a"]+-- >>> sortByFL compare fl+-- FocusList (Focus 0) ["a","b","c"]+--+-- Nothing will happen if you try to sort an empty 'FocusList', or a+-- 'FocusList' with only one element.+--+-- prop> emptyFL == sortByFL compare emptyFL+-- prop> singletonFL a == sortByFL compare (singletonFL a)+--+-- The element with the 'Focus' should be the same before and after sorting.+--+-- prop> getFocusItemFL (fl :: FocusList Int) == getFocusItemFL (sortByFL compare fl)+--+-- Sorting a 'FocusList' and getting the underlying 'Seq' should be the same as+-- getting the underlying 'Seq' and then sorting it.+--+-- prop> toSeqFL (sortByFL compare (fl :: FocusList Int)) == sortBy compare (toSeqFL fl)+--+-- __WARNING__: The computational complexity for this is very bad. It should be+-- able to be done in @O(n * log n)@, but the current implementation is+-- @O(n^2)@ (or worse), where @n@ is the length of the 'FocusList'.  This+-- function could be implemented the same way+-- @Data.Sequence.'Data.Sequence.sortBy'@ is implemented.  However, a small+-- change needs to be added to that function to keep track of the 'Focus' in+-- the 'FocusList' and make sure it gets updated properly.  If you're+-- interested in fixing this, please send a PR.+sortByFL+  :: forall a+   . (a -> a -> Ordering) -- ^ The function to use to compare elements.+  -> FocusList a+  -> FocusList a+sortByFL _ FocusList{focusListFocus = NoFocus} = emptyFL+sortByFL cmpFunc FocusList{focusList = fls, focusListFocus = Focus foc} =+  let (res, maybeNewFoc) = go fls (Just foc)+  in+  case maybeNewFoc of+    Nothing -> error "sortByFL: A sequence should never lose its focus."+    Just newFoc ->+      FocusList+        { focusList = res+        , focusListFocus = Focus newFoc+        }+  where+    go+      :: Seq a -- ^ The sequence that needs to be sorted.+      -> Maybe Int+         -- ^ Whether or not we are tracking a 'Focus' that needs to be updated.+      -> (Seq a, Maybe Int)+    -- Trying to sort an empty sequence with a 'Focus'.  This should never happen.+    go Empty (Just _) =+      error "sortByFL: go: this should never happen, sort empty with focus."+    -- Trying to sort an empty sequence.+    go Empty Nothing = (Empty, Nothing)+    -- Trying to sort a non-empty sequence with no focus.+    go (a :<| as) Nothing =+      let res = go as Nothing+      in+      case res of+        (_, Just _) -> error "sortByFL: go: this should never happen, no focus case"+        (Empty, Nothing) -> (a :<| Empty, Nothing)+        (b :<| bs, Nothing) ->+          case cmpFunc a b of+            LT -> (a :<| b :<| bs, Nothing)+            EQ -> (a :<| b :<| bs, Nothing)+            GT -> (b :<| fst (go (a :<| bs) Nothing), Nothing)+    -- Trying to sort a non-empty sequence with the top element having the focus.+    go (a :<| as) (Just 0) =+      let res = go as Nothing+      in+      case res of+        (_, Just _) -> error "sortByFL: go: this should never happen, top elem has focus case"+        (Empty, Nothing) -> (a :<| Empty, Just 0)+        (b :<| bs, Nothing) ->+          case cmpFunc a b of+            LT -> (a :<| b :<| bs, Just 0)+            EQ -> (a :<| b :<| bs, Just 0)+            GT ->+              let (newSeq, maybeNewFoc) = go (a :<| bs) (Just 0)+              in+              case maybeNewFoc of+                Nothing -> error "sortByFL: go: this should never happen, lost the focus"+                Just newFoc -> (b :<| newSeq, Just (newFoc + 1))+    -- Trying to sort a non-empty sequence where some element other than the+    -- top element has the focus.+    go (a :<| as) (Just n) =+      let res = go as (Just (n - 1))+      in+      case res of+        (_, Nothing) -> error "sortByFL: go: this should never happen, no focus"+        (Empty, Just _) -> error "sortByFL: go: this should never happen, focus but no elems"+        (b :<| bs, Just newFoc) ->+          case cmpFunc a b of+            LT -> (a :<| b :<| bs, Just (newFoc + 1))+            EQ -> (a :<| b :<| bs, Just (newFoc + 1))+            GT ->+              case newFoc of+                0 -> (b :<| fst (go (a :<| bs) Nothing), Just 0)+                gt0 ->+                  let (newSeq, maybeNewFoc') = go (a :<| bs) (Just gt0)+                  in+                  case maybeNewFoc' of+                    Nothing -> error "sortByFL: go: this should never happen, lost the focus again"+                    Just newFoc' -> (b :<| newSeq, Just (newFoc' + 1))
+ test/DocTest.hs view
@@ -0,0 +1,12 @@++module Main where++import Build_doctests (flags, pkgs, module_sources)+import Test.DocTest (doctest)++main :: IO ()+main = do+  doctest args+  where+    args :: [String]+    args = flags ++ pkgs ++ module_sources
+ test/Test.hs view
@@ -0,0 +1,20 @@++module Main where++import Test.Tasty (TestTree, defaultMain, testGroup)++import Test.FocusList (focusListTestsIO)++main :: IO ()+main = do+  tests <- testsIO+  defaultMain tests++testsIO :: IO TestTree+testsIO = do+  focusListTests <- focusListTestsIO+  pure $+    testGroup+      "tests"+      [ focusListTests+      ]
+ test/Test/FocusList.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Test.FocusList where++import Data.GenValidity.Sequence ()+import Test.QuickCheck (Gen)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.Hedgehog (testProperty)+import Test.Tasty.Hspec (Spec, testSpec)+import Test.Validity (GenInvalid, GenUnchecked, GenValid(genValid), Validity(validate), check, eqSpec, genValidSpec)++import Data.FocusList (Focus, FocusList, genValidFL, invariantFL)++import Test.FocusList.Invariants (testInvariantsInFocusList)++instance GenUnchecked Focus++instance GenUnchecked a => GenUnchecked (FocusList a)++instance Validity (FocusList a) where+  validate fl = check (invariantFL fl) "the FocusList has been constructed correctly"++instance GenValid a => GenValid (FocusList a) where+  genValid :: Gen (FocusList a)+  genValid = genValidFL genValid++instance GenInvalid a => GenInvalid (FocusList a)++focusListTestsIO :: IO TestTree+focusListTestsIO = do+  specs <- testSpec "validity tests" validitySpec+  pure $+    testGroup+      "FocusList"+      [ testProperty "invariants in FocusList" testInvariantsInFocusList+      , specs+      ]++validitySpec :: Spec+validitySpec = do+  eqSpec @(FocusList String)+  genValidSpec @(FocusList String)
+ test/Test/FocusList/Invariants.hs view
@@ -0,0 +1,129 @@++module Test.FocusList.Invariants where++import Data.Maybe (catMaybes)+import Hedgehog+  ( Gen+  , Property+  , PropertyT+  , annotate+  , annotateShow+  , failure+  , forAll+  , property+  , success+  )+import Hedgehog.Gen (alphaNum, choice, int, string)+import Hedgehog.Range (constant, linear)++import Data.FocusList+  ( FocusList+  , deleteFL+  , emptyFL+  , insertFL+  , invariantFL+  , isEmptyFL+  , lengthFL+  , lookupFL+  , removeFL+  )++testInvariantsInFocusList :: Property+testInvariantsInFocusList =+  property $ do+    numOfActions <- forAll $ int (linear 1 200)+    let initialState = emptyFL+    let strGen = string (constant 0 25) alphaNum+    -- traceM "----------------------------------"+    -- traceM $ "starting bar, numOfActions: " <> show numOfActions+    runActions numOfActions strGen initialState++data Action a+  = InsertFL Int a+  | RemoveFL Int+  | DeleteFL a+  deriving (Eq, Show)++genInsertFL :: Gen a -> FocusList a -> Maybe (Gen (Action a))+genInsertFL valGen fl+  | isEmptyFL fl = Just $ do+      val <- valGen+      pure $ InsertFL 0 val+  | otherwise = Just $ do+      let len = lengthFL fl+      key <- int $ constant 0 len+      val <- valGen+      pure $ InsertFL key val++genRemoveFL :: FocusList a -> Maybe (Gen (Action a))+genRemoveFL fl+  | isEmptyFL fl = Nothing+  | otherwise = Just $ do+      let len = lengthFL fl+      keyToRemove <- int $ constant 0 (len - 1)+      pure $ RemoveFL keyToRemove++genDeleteFL :: Show a => FocusList a -> Maybe (Gen (Action a))+genDeleteFL fl+  | isEmptyFL fl = Nothing+  | otherwise = Just $ do+      let len = lengthFL fl+      keyForItemToDelete <- int $ constant 0 (len - 1)+      let maybeItemToDelete = lookupFL keyForItemToDelete fl+      case maybeItemToDelete of+        Nothing ->+          let msg =+                "Could not find item in focuslist even though " <>+                "it should be there." <>+                "\nkey: " <>+                show keyForItemToDelete <>+                "\nfocus list: " <>+                show fl+          in error msg+        Just item -> pure $ DeleteFL item++generateAction :: Show a => Gen a -> FocusList a -> Gen (Action a)+generateAction valGen fl = do+  let generators =+        catMaybes+          [ genInsertFL valGen fl+          , genRemoveFL fl+          , genDeleteFL fl+          ]+  case generators of+    [] ->+      let msg =+            "No generators available for fl:\n" <>+            show fl+      in error msg+    _ -> do+      choice generators++performAction :: Eq a => FocusList a -> Action a -> Maybe (FocusList a)+performAction fl (InsertFL key val) = Just $ insertFL key val fl+performAction fl (RemoveFL keyToRemove) = removeFL keyToRemove fl+performAction fl (DeleteFL valToDelete) = Just $ deleteFL valToDelete fl++runActions :: (Eq a, Monad m, Show a) => Int -> Gen a -> FocusList a -> PropertyT m ()+runActions i valGen startingFL+  | i <= 0 = success+  | otherwise = do+    action <- forAll $ generateAction valGen startingFL+    -- traceM $ "runActions, startingFL: " <> show startingFL+    -- traceM $ "runActions, action: " <> show action+    let maybeEndingFL = performAction startingFL action+    case maybeEndingFL of+      Nothing -> do+        annotate "Failed to perform action."+        annotateShow startingFL+        annotateShow action+        failure+      Just endingFL ->+        if invariantFL endingFL+          then runActions (i - 1) valGen endingFL+          else do+            annotate "Ending FocusList failed invariants."+            annotateShow startingFL+            annotateShow action+            annotateShow endingFL+            failure
+ test/readme/README.lhs view
@@ -0,0 +1,79 @@+FocusList+=========++[![Build Status](https://secure.travis-ci.org/cdepillabout/focuslist.svg)](http://travis-ci.org/cdepillabout/focuslist)+[![Hackage](https://img.shields.io/hackage/v/focuslist.svg)](https://hackage.haskell.org/package/focuslist)+[![Stackage LTS](http://stackage.org/package/focuslist/badge/lts)](http://stackage.org/lts/package/focuslist)+[![Stackage Nightly](http://stackage.org/package/focuslist/badge/nightly)](http://stackage.org/nightly/package/focuslist)+[![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)](./LICENSE)++A `FocusList` is a sequence of elements which has one element as its `Focus`. It+supports quick insertion and indexing by its implementation with+[`Seq`](http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Sequence.html#t:Seq).++## Example++Here is a short example of using `FocusList`.++```haskell+module Main where++import Data.FocusList+  ( Focus(Focus), FocusList, appendFL, fromListFL, getFocusItemFL, prependFL+  , singletonFL+  )+import Data.Foldable (toList)++-- | Create a new 'FocusList' from a list.  You must set the 'Focus' of the new+-- 'FocusList'.  The 'Focus' is counting from zero, so the @goat@ element should+-- have the 'Focus'.+--+-- If you try to specify a 'Focus' out of range from the input list,+-- 'fromListFL' will return 'Nothing'.+myFocusList :: Maybe (FocusList String)+myFocusList = fromListFL (Focus 2) ["hello", "bye", "goat", "dog"]++-- | You can get the focused element from an existing 'FocusList'+--+-- If the 'FocusList' is empty, this returns 'Nothing'.+myFocusElement :: FocusList String -> Maybe String+myFocusElement focuslist = getFocusItemFL focuslist++-- | You can append to either side of a 'FocusList'.+--+-- 'singletonFL' creates a 'FocusList' with a single element.+-- That single element will have the 'Focus'.+--+-- 'myFocusListAppended' will have a value of+-- @FocusList (Focus 1) ["bye", "hello", "foobar"]@+myFocusListAppended :: FocusList String+myFocusListAppended =+  prependFL "bye" (appendFL (singletonFL "hello") "foobar")++-- | 'FocusList' is an instance of 'Functor' and 'Foldable', so you can use+-- functions like 'fmap' and 'toList' on a 'FocusList'.+fmapAndConvertToList :: FocusList Int -> [String]+fmapAndConvertToList focuslist = toList (fmap show focuslist)++main :: IO ()+main = do+  putStrLn "myFocusList:"+  print myFocusList++  putStrLn "\nmyFocusListAppended:"+  print myFocusListAppended++  putStrLn "\nmyFocusElement myFocusListAppended:"+  print (myFocusElement myFocusListAppended)++  putStrLn "\nfmap length myFocusListAppended:"+  print (fmap length myFocusListAppended)++  putStrLn "\nfmapAndConvertToList (fmap length myFocusListAppended):"+  print (fmapAndConvertToList (fmap length myFocusListAppended))+```++## Maintainers++- [Grendel-Grendel-Grendel](https://github.com/Grendel-Grendel-Grendel)+- [cdepillabout](https://github.com/cdepillabout)