packages feed

recursive-zipper (empty) → 0.0.0.0

raw patch · 7 files changed

+290/−0 lines, 7 filesdep +basedep +comonaddep +containerssetup-changed

Dependencies added: base, comonad, containers, free, lens, mtl, recursion-schemes, transformers

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for recursion-zipper++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2021++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,6 @@+# recursive-zipper++A zipper-type for self-recursive types like ASTs or JSON.++Provide efficient access to a leaf node of such a structure, and a mechanism for+navigating around, folding, etc.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ recursive-zipper.cabal view
@@ -0,0 +1,45 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name:           recursive-zipper+version:        0.0.0.0+synopsis:       Zippers over recursive data structures.+description:    Please see the README on GitHub at <https://github.com/ChrisPenner/recursive-zipper#readme>+category:       Data+homepage:       https://github.com/ChrisPenner/recursive-zipper#readme+bug-reports:    https://github.com/ChrisPenner/recursive-zipper/issues+author:         Chris Penner+maintainer:     example@example.com+copyright:      2021 Chris Penner+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/ChrisPenner/recursive-zipper++library+  exposed-modules:+      Zipper.Recursive+      Zipper.Recursive.Internal+  other-modules:+      Paths_recursive_zipper+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , comonad+    , containers+    , free+    , lens+    , mtl+    , recursion-schemes+    , transformers+  default-language: Haskell2010
+ src/Zipper/Recursive.hs view
@@ -0,0 +1,36 @@+module Zipper.Recursive+  ( -- * Core type+    Z.Zipper,+    Z.Idx,++    -- * Constructing Zippers+    Z.zipper,+    Z.fromRecursive,+    Z.tagged,++    -- * Movement+    Z.down,+    Z.up,+    Z.sibling,+    Z.tug,++    -- * Folding and flattening+    Z.rezip,+    Z.flatten,+    Z.fold,++    -- * Getters+    Z.focus,+    Z.branches,+    Z.currentIndex,++    -- * Optics+    Z.focus_,+    Z.unwrapped_,+    Z.branches_,+    Z.children_,+    Z.ichildren_,+  )+  where++import Zipper.Recursive.Internal as Z
+ src/Zipper/Recursive/Internal.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++module Zipper.Recursive.Internal where++import qualified Control.Comonad as Comonad+import Control.Comonad.Cofree+import qualified Control.Comonad.Cofree as Cofree+import qualified Control.Comonad.Trans.Cofree as CofreeF+import Control.Lens hiding (children, (:<))+import Control.Monad+import Data.Functor.Classes+import qualified Data.Functor.Foldable as FF+import Data.Maybe++-- | Alias for constraints required for many zipper operations.+type Idx i f a = (Ixed (f (Cofree f a)), IxValue (f (Cofree f a)) ~ (Cofree f a), Index (f (Cofree f a)) ~ i)++-- | The core zipper type+data Zipper i (f :: * -> *) a = Zipper+  { parents :: [(i, Cofree f a)],+    focus :: Cofree f a+  }+  deriving (Functor)++deriving instance (Eq1 f, Eq i, Eq a) => Eq (Zipper i f a)++deriving instance (Ord1 f, Ord i, Ord a) => Ord (Zipper i f a)++-- | Get the location of the current selection within its parent if we have one.+-- @O(1)+currentIndex :: Zipper i f a -> Maybe i+currentIndex (Zipper ((i, _) : _) _) = Just i+currentIndex _ = Nothing++-- | Get the path to the current value from the root of the structure.+-- @O(depth)+currentPath :: Zipper i f a -> [i]+currentPath (Zipper parents _) = reverse $ fmap fst parents++-- | Focus the tag at the current position.+focus_ :: Lens' (Zipper i f a) a+focus_ f (Zipper parents foc) = Zipper parents <$> (foc & _extract %%~ f)++-- | Focus the currently selected sub-tree as a 'Cofree'+unwrapped_ :: Lens' (Zipper i f a) (Cofree f a)+unwrapped_ f (Zipper parents foc) = Zipper parents <$> f foc++-- TODO: implement proper comonad instance+-- extract :: Zipper i f a -> a+-- extract (Zipper _ (a :< _)) = a++-- instance Functor f => Comonad (Zipper i f) where+--   extract = extract . _focus+--   duplicate :: forall f a. (Zipper i f a) -> Zipper i f (Zipper i f a)+--   duplicate z@(Zipper parents foc) = Zipper (zipWith (\z (i,_) -> (i, z)) rezippedParents parents) (foc $> z)+--     where+--       rezippedParents :: [Zipper i f a]+--       rezippedParents = unfoldr go z+--       go current =+--         let x = up current+--          in liftA2 (,) x x+--       -- go (current, []) = Nothing+--       -- go :: (Zipper i f a, [(i, Cofree f a)]) -> Maybe (_, Cofree f a)+--       -- go = _++-- | A useful combinator for chaining operations which might fail.+-- If an operation fails, the original zipper is returned.+--+-- E.g.+--+-- >>> tug up z+tug :: (a -> Maybe a) -> a -> a+tug f a = fromMaybe a (f a)++-- | Create a zipper over a cofree structure+zipper :: Cofree f a -> Zipper i f a+zipper f = Zipper [] f++-- | Create a zipper from a recursive type, tagging it with '()'+fromRecursive :: FF.Recursive t => t -> Zipper i (FF.Base t) ()+fromRecursive t = zipper $ Cofree.unfold (((),) . FF.project) t++-- | Create a zipper from a recursive type, given a function to generate annotations.+tagged :: FF.Recursive t => (t -> a) -> t -> Zipper i (FF.Base t) a+tagged f t = zipper $ Cofree.unfold (\x -> (f x, FF.project x)) t++-- | Select the subtree at the given location.+-- @O(1)@+down :: (Idx i f a) => i -> Zipper i f a -> Maybe (Zipper i f a)+down i (Zipper parents current) = Zipper ((i, current) : parents) <$> (current ^? _unwrap . ix i)++-- | Select the parent of the current location.+-- @O(1)@+up :: Idx i f a => Zipper i f a -> Maybe (Zipper i f a)+up (Zipper ((i, p) : parents) current) = Just $ Zipper parents (p & _unwrap . ix i .~ current)+up _ = Nothing++-- | Re-zip the entire tree.+-- @O(d)@+rezip :: Idx i f a => Zipper i f a -> Cofree f a+rezip z = case up z of+  Nothing -> focus z+  Just p -> rezip p++-- | Rezip, forget all tags, and flatten the structure.+-- @O(d)@+flatten :: (FF.Corecursive f, Idx i (FF.Base f) a) => Zipper i (FF.Base f) a -> f+flatten = FF.cata alg . rezip+  where+    alg (_ CofreeF.:< fv) = FF.embed fv++-- | Move to the sibling which is located at 'i' in its parent.+-- @O(1)@+--+-- @@+-- sibling i = up >=> down i+-- @@+sibling :: Idx i f a => i -> Zipper i f a -> Maybe (Zipper i f a)+sibling i = up >=> down i++-- parentTags :: Traversal' (Zipper i f a) a+-- parentTags f (Zipper parents foc) = Zipper <$> (forwards (parents & traversed . _2 . _extract %%~ Backwards . f)) <*> pure foc++-- | Traversal over all subtrees of the current location.+children_ :: Traversable f => Traversal' (Zipper i f a) (Cofree f a)+children_ f (Zipper parents current) = Zipper parents <$> (current & _unwrap . traversed %%~ f)++-- | Indexed traversal over all subtrees of the current location.+ichildren_ :: TraversableWithIndex i f => IndexedTraversal' i (Zipper i f a) (Cofree f a)+ichildren_ f (Zipper parents current) = Zipper parents <$> (current & _unwrap . itraversed %%@~ \i a -> indexed f i a)++-- | Get the base-functor at the current location.+-- @O(1)+branches :: Zipper i f a -> f (Cofree f a)+branches (Zipper _ (_ :< cs)) = cs++-- | A lens over the base-functor at the current location.+branches_ :: Lens' (Zipper i f a) (f (Cofree f a))+branches_ = lens getter setter+  where+    getter (Zipper _ (_ :< f)) = f+    setter (Zipper p (a :< _)) f = (Zipper p (a :< f))++-- retag :: Functor f => (a -> f b -> b) -> Cofree f a -> Cofree f b+-- retag f (a :< fr) =+--   let cs = fmap (retag f) fr+--    in (f a $ fmap Comonad.extract cs) :< cs++-- | Fold a zipper from bottom to top.+-- @O(n)+fold :: (Functor f, Idx i f a) => (a -> f r -> r) -> Zipper i f a -> r+fold f = FF.cata (\(a CofreeF.:< fr) -> f a fr) . rezip