packages feed

parameterized-utils (empty) → 1.0.0

raw patch · 27 files changed

+6207/−0 lines, 27 filesdep +QuickCheckdep +basedep +containerssetup-changed

Dependencies added: QuickCheck, base, containers, deepseq, ghc-prim, hashable, hashtables, lens, mtl, parameterized-utils, tasty, tasty-ant-xml, tasty-hunit, tasty-quickcheck, template-haskell, text, th-abstraction, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013-2016 Galois Inc.+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 Galois, Inc. nor the names of its 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ parameterized-utils.cabal view
@@ -0,0 +1,100 @@+Name:          parameterized-utils+Version:       1.0.0+Author:        Galois Inc.+Maintainer:    jhendrix@galois.com+Build-type:    Simple+Cabal-version: >= 1.9.2+license: BSD3+license-file: LICENSE+category: Data Structures, Dependent Types+Synopsis: Classes and data structures for working with data-kind indexed types+Description:+  This packages contains collection classes and type representations+  used for working with values that have a single parameter.  It's+  intended for things like expression libraries where one wishes+  to leverage the Haskell type-checker to improve type-safety by encoding+  the object language type system into data kinds.++-- Many (but not all, sadly) uses of unsafe operations are+-- controlled by this compile flag.  When this flag is set+-- to False, alternate implementations are used to avoid+-- Unsafe.Coerce and Data.Coerce.  These alternate implementations+-- impose a significant performance hit.+flag unsafe-operations+  Description: Use unsafe operations to improve performance+  Default: True++source-repository head+  type: git+  location: https://github.com/GaloisInc/parameterized-utils++library+  build-depends:+    base >= 4.7 && < 4.11,+    th-abstraction >=0.1 && <0.3,+    containers,+    deepseq,+    ghc-prim,+    hashable,+    hashtables,+    lens,+    mtl,+    template-haskell,+    text,+    vector++  hs-source-dirs: src++  exposed-modules:+    Data.Parameterized+    Data.Parameterized.Classes+    Data.Parameterized.Context+    Data.Parameterized.Context.Safe+    Data.Parameterized.Context.Unsafe+    Data.Parameterized.Ctx+    Data.Parameterized.Ctx.Proofs+    Data.Parameterized.HashTable+    Data.Parameterized.List+    Data.Parameterized.Map+    Data.Parameterized.NatRepr+    Data.Parameterized.Nonce+    Data.Parameterized.Nonce.Transformers+    Data.Parameterized.Nonce.Unsafe+    Data.Parameterized.Some+    Data.Parameterized.SymbolRepr+    Data.Parameterized.Pair+    Data.Parameterized.TH.GADT+    Data.Parameterized.TraversableF+    Data.Parameterized.TraversableFC+    Data.Parameterized.Utils.BinTree++  ghc-options: -Wall++  if flag(unsafe-operations)+    cpp-options: -DUNSAFE_OPS+++test-suite parameterizedTests+  type: exitcode-stdio-1.0+  hs-source-dirs: test++  ghc-options: -Wall++  main-is:UnitTest.hs+  other-modules:+    Test.Context+    Test.NatRepr++  build-depends:+    base,+    hashable,+    hashtables,+    ghc-prim,+    lens,+    mtl,+    parameterized-utils,+    tasty,+    tasty-ant-xml,+    tasty-hunit,+    tasty-quickcheck >= 0.8.1,+    QuickCheck >= 2.7
+ src/Data/Parameterized.hs view
@@ -0,0 +1,19 @@+module Data.Parameterized+( module Data.Parameterized.Classes       +, module Data.Parameterized.Ctx           +, module Data.Parameterized.TraversableF  +, module Data.Parameterized.TraversableFC +, module Data.Parameterized.NatRepr       +, module Data.Parameterized.Pair          +, module Data.Parameterized.Some          +, module Data.Parameterized.SymbolRepr    +) where++import Data.Parameterized.Classes+import Data.Parameterized.Ctx+import Data.Parameterized.TraversableF+import Data.Parameterized.TraversableFC+import Data.Parameterized.NatRepr+import Data.Parameterized.Pair+import Data.Parameterized.Some+import Data.Parameterized.SymbolRepr    
+ src/Data/Parameterized/Classes.hs view
@@ -0,0 +1,280 @@+{-|+Copyright        : (c) Galois, Inc 2014-2015+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This module declares classes for working with types with the kind+@k -> *@ for any kind @k@.  These are generalizations of the+"Data.Functor.Classes" types as they work with any kind @k@, and are+not restricted to '*'.+-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+#if MIN_VERSION_base(4,9,0)+{-# LANGUAGE Safe #-}+#else+{-# LANGUAGE Trustworthy #-}+#endif+module Data.Parameterized.Classes+  ( -- * Equality exports+    Equality.TestEquality(..)+  , (Equality.:~:)(..)+  , EqF(..)+  , PolyEq(..)+    -- * Ordering generalization+  , OrdF(..)+  , lexCompareF+  , OrderingF(..)+  , joinOrderingF+  , orderingF_refl+  , toOrdering+  , fromOrdering+    -- * Typeclass generalizations+  , ShowF(..)+  , HashableF(..)+  , CoercibleF(..)+    -- * Optics generalizations+  , IndexF+  , IxValueF+  , IxedF(..)+  , IxedF'(..)+  , AtF(..)+    -- * KnownRepr+  , KnownRepr(..)+    -- * Re-exports+  , Data.Maybe.isJust+  ) where++import Data.Functor.Const+import Data.Hashable+import Data.Maybe (isJust)+import Data.Proxy+import Data.Type.Equality as Equality++-- We define these type alias here to avoid importing Control.Lens+-- modules, as this apparently causes problems with the safe Hasekll+-- checking.+type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s+type Traversal' s a = forall f. Applicative f => (a -> f a) -> s -> f s++------------------------------------------------------------------------+-- CoercibleF++-- | An instance of 'CoercibleF' gives a way to coerce between+--   all the types of a family.  We generally use this to witness+--   the fact that the type parameter to @rtp@ is a phantom type+--   by giving an implementation in terms of Data.Coerce.coerce.+class CoercibleF (rtp :: k -> *) where+  coerceF :: rtp a -> rtp b++instance CoercibleF (Const x) where+  coerceF (Const x) = Const x++------------------------------------------------------------------------+-- EqF++-- | @EqF@ provides a method @eqF@ for testing whether two parameterized+-- types are equal.+--+-- Unlike 'TestEquality', this only works when the type arguments are+-- the same, and does not provide a proof that the types have the same+-- type when they are equal. Thus this can be implemented over+-- parameterized types that are unable to provide evidence that their+-- type arguments are equal.+class EqF (f :: k -> *) where+  eqF :: f a -> f a -> Bool++instance Eq a => EqF (Const a) where+  eqF (Const x) (Const y) = x == y++------------------------------------------------------------------------+-- PolyEq++-- | A polymorphic equality operator that generalizes 'TestEquality'.+class PolyEq u v where+  polyEqF :: u -> v -> Maybe (u :~: v)++  polyEq :: u -> v -> Bool+  polyEq x y = isJust (polyEqF x y)++------------------------------------------------------------------------+-- Ordering++-- | Ordering over two distinct types with a proof they are equal.+data OrderingF x y where+  LTF :: OrderingF x y+  EQF :: OrderingF x x+  GTF :: OrderingF x y++orderingF_refl :: OrderingF x y -> Maybe (x :~: y)+orderingF_refl o =+  case o of+    LTF -> Nothing+    EQF -> Just Refl+    GTF -> Nothing++-- | Convert 'OrderingF' to standard ordering.+toOrdering :: OrderingF x y -> Ordering+toOrdering LTF = LT+toOrdering EQF = EQ+toOrdering GTF = GT++-- | Convert standard ordering to 'OrderingF'.+fromOrdering :: Ordering -> OrderingF x x+fromOrdering LT = LTF+fromOrdering EQ = EQF+fromOrdering GT = GTF++-- | `joinOrderingF x y` first compares on x, returning an equivalent+-- value if it is not `EQF`.  If it is EQF, it returns `y`.+joinOrderingF :: forall (a :: j) (b :: j) (c :: k) (d :: k)+              .  OrderingF a b+              -> (a ~ b => OrderingF c d)+              -> OrderingF c d+joinOrderingF EQF y = y+joinOrderingF LTF _ = LTF+joinOrderingF GTF _ = GTF++------------------------------------------------------------------------+-- OrdF++-- | A parameterized type that can be compared on distinct instances.+class TestEquality ktp => OrdF (ktp :: k -> *) where+  {-# MINIMAL compareF #-}++  -- | compareF compares two keys with different type parameters.+  -- Instances must ensure that keys are only equal if the type+  -- parameters are equal.+  compareF :: ktp x -> ktp y -> OrderingF x y++  leqF :: ktp x -> ktp y -> Bool+  leqF x y =+    case compareF x y of+      LTF -> True+      EQF -> True+      GTF -> False++  ltF :: ktp x -> ktp y -> Bool+  ltF x y =+    case compareF x y of+      LTF -> True+      EQF -> False+      GTF -> False++  geqF :: ktp x -> ktp y -> Bool+  geqF x y =+    case compareF x y of+      LTF -> False+      EQF -> True+      GTF -> True++  gtF :: ktp x -> ktp y -> Bool+  gtF x y =+    case compareF x y of+      LTF -> False+      EQF -> False+      GTF -> True++-- | Compare two values, and if they are equal compare the next values,+-- otherwise return LTF or GTF+lexCompareF :: forall (f :: j -> *) (a :: j) (b :: j) (c :: k) (d :: k)+             .  OrdF f+            => f a+            -> f b+            -> (a ~ b => OrderingF c d)+            -> OrderingF c d+lexCompareF x y = joinOrderingF (compareF x y)++------------------------------------------------------------------------+-- ShowF++-- | A parameterized type that can be shown on all instances.+--+-- To implement @'ShowF' g@, one should implement an instance @'Show'+-- (g tp)@ for all argument types @tp@, then write an empty instance+-- @instance 'ShowF' g@.+class ShowF (f :: k -> *) where+  -- | Provides a show instance for each type.+  withShow :: p f -> q tp -> (Show (f tp) => a) -> a++  default withShow :: Show (f tp) => p f -> q tp -> (Show (f tp) => a) -> a+  withShow _ _ x = x++  showF :: forall tp . f tp -> String+  showF x = withShow (Proxy :: Proxy f) (Proxy :: Proxy tp) (show x)++  showsF :: forall tp . f tp -> String -> String+  showsF x = withShow (Proxy :: Proxy f) (Proxy :: Proxy tp) (shows x)++instance Show x => ShowF (Const x)++------------------------------------------------------------------------+-- IxedF++type family IndexF       (m :: *) :: k -> *+type family IxValueF     (m :: *) :: k -> *++-- | Parameterized generalization of the lens @Ixed@ class.+class IxedF k m where+  -- | Given an index into a container, build a traversal that visits+  --   the given element in the container, if it exists.+  ixF :: forall (x :: k). IndexF m x -> Traversal' m (IxValueF m x)++-- | Parameterized generalization of the lens @Ixed@ class,+--   but with the guarantee that indexes exist in the container.+class IxedF k m => IxedF' k m where+  -- | Given an index into a container, build a lens that+  --   points into the given element in the container.+  ixF' :: forall (x :: k). IndexF m x -> Lens' m (IxValueF m x)++------------------------------------------------------------------------+-- AtF++-- | Parameterized generalization of the lens @At@ class.+class IxedF k m => AtF k m where+  -- | Given an index into a container, build a lens that points into+  --   the given position in the container, whether or not it currently+  --   exists.  Setting values of @atF@ to a @Just@ value will insert+  --   the value if it does not already exist.+  atF :: forall (x :: k). IndexF m x -> Lens' m (Maybe (IxValueF m x))++------------------------------------------------------------------------+-- HashableF++-- | A default salt used in the implementation of 'hash'.+defaultSalt :: Int+#if WORD_SIZE_IN_BITS == 64+defaultSalt = 0xdc36d1615b7400a4+#else+defaultSalt = 0x087fc72c+#endif+{-# INLINE defaultSalt #-}++-- | A parameterized type that is hashable on all instances.+class HashableF (f :: k -> *) where+  hashWithSaltF :: Int -> f tp -> Int++  -- | Hash with default salt.+  hashF :: f tp -> Int+  hashF = hashWithSaltF defaultSalt++instance Hashable a => HashableF (Const a) where+  hashWithSaltF s (Const x) = hashWithSalt s x++------------------------------------------------------------------------+-- KnownRepr++-- | This class is parameterized by a kind @k@ (typically a data+-- kind), a type constructor @f@ of kind @k -> *@ (typically a GADT of+-- singleton types indexed by @k@), and an index parameter @ctx@ of+-- kind @k@.+class KnownRepr (f :: k -> *) (ctx :: k) where+  knownRepr :: f ctx
+ src/Data/Parameterized/Context.hs view
@@ -0,0 +1,274 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.Context+-- Copyright        : (c) Galois, Inc 2014-16+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module reexports either "Data.Parameterized.Context.Safe"+-- or "Data.Parameterized.Context.Unsafe" depending on the+-- the unsafe-operations compile-time flag.+--+-- It also defines some utility typeclasses for transforming+-- between curried and uncurried versions of functions over contexts.+------------------------------------------------------------------------++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ViewPatterns #-}+module Data.Parameterized.Context+ (+#ifdef UNSAFE_OPS+   module Data.Parameterized.Context.Unsafe+#else+   module Data.Parameterized.Context.Safe+#endif+ , singleton+ , toVector+ , pattern (:>)+ , pattern Empty+   -- * Context extension and embedding utilities+ , CtxEmbedding(..)+ , ExtendContext(..)+ , ExtendContext'(..)+ , ApplyEmbedding(..)+ , ApplyEmbedding'(..)+ , identityEmbedding+ , extendEmbeddingRightDiff+ , extendEmbeddingRight+ , extendEmbeddingBoth+ , ctxeSize+ , ctxeAssignment++   -- * Static indexing and lenses for assignments+ , Idx+ , getCtx+ , setCtx+ , field+ , natIndex+ , natIndexProxy++   -- * Currying and uncurrying for assignments+ , CurryAssignment+ , CurryAssignmentClass(..)+ ) where++import Prelude hiding (null)++import GHC.TypeLits (Nat, type (-))++import Control.Lens hiding (Index, view, (:>), Empty)+import qualified Data.Vector as V+import qualified Data.Vector.Mutable as MV++#ifdef UNSAFE_OPS+import Data.Parameterized.Context.Unsafe+#else+import Data.Parameterized.Context.Safe+#endif++import Data.Parameterized.TraversableFC++-- | Create a single element context.+singleton :: f tp -> Assignment f (EmptyCtx ::> tp)+singleton = (empty :>)++-- | Convert the assignment to a vector.+toVector :: Assignment f tps -> (forall tp . f tp -> e) -> V.Vector e+toVector a f = V.create $ do+  vm <- MV.new (sizeInt (size a))+  forIndexM (size a) $ \i -> do+    MV.write vm (indexVal i) (f (a ! i))+  return vm+{-# INLINABLE toVector #-}++--------------------------------------------------------------------------------+-- | Context embedding.++-- This datastructure contains a proof that the first context is+-- embeddable in the second.  This is useful if we want to add extend+-- an existing term under a larger context.++data CtxEmbedding (ctx :: Ctx k) (ctx' :: Ctx k)+  = CtxEmbedding { _ctxeSize       :: Size ctx'+                 , _ctxeAssignment :: Assignment (Index ctx') ctx+                 }++-- Alternate encoding?+-- data CtxEmbedding ctx ctx' where+--   EIdentity  :: CtxEmbedding ctx ctx+--   ExtendBoth :: CtxEmbedding ctx ctx' -> CtxEmbedding (ctx ::> tp) (ctx' ::> tp)+--   ExtendOne  :: CtxEmbedding ctx ctx' -> CtxEmbedding ctx (ctx' ::> tp)++ctxeSize :: Simple Lens (CtxEmbedding ctx ctx') (Size ctx')+ctxeSize = lens _ctxeSize (\s v -> s { _ctxeSize = v })++ctxeAssignment :: Lens (CtxEmbedding ctx1 ctx') (CtxEmbedding ctx2 ctx')+                       (Assignment (Index ctx') ctx1) (Assignment (Index ctx') ctx2)+ctxeAssignment = lens _ctxeAssignment (\s v -> s { _ctxeAssignment = v })++class ApplyEmbedding (f :: Ctx k -> *) where+  applyEmbedding :: CtxEmbedding ctx ctx' -> f ctx -> f ctx'++class ApplyEmbedding' (f :: Ctx k -> k' -> *) where+  applyEmbedding' :: CtxEmbedding ctx ctx' -> f ctx v -> f ctx' v++class ExtendContext (f :: Ctx k -> *) where+  extendContext :: Diff ctx ctx' -> f ctx -> f ctx'++class ExtendContext' (f :: Ctx k -> k' -> *) where+  extendContext' :: Diff ctx ctx' -> f ctx v -> f ctx' v++instance ApplyEmbedding' Index where+  applyEmbedding' ctxe idx = (ctxe ^. ctxeAssignment) ! idx++instance ExtendContext' Index where+  extendContext' = extendIndex'++-- -- This is the inefficient way of doing things.  A better way is to+-- -- just have a map between indices.+-- applyEmbedding :: CtxEmbedding ctx ctx'+--                -> Index ctx tp -> Index ctx' tp+-- applyEmbedding ctxe idx = (ctxe ^. ctxeAssignment) ! idx++identityEmbedding :: Size ctx -> CtxEmbedding ctx ctx+identityEmbedding sz = CtxEmbedding sz (generate sz id)++-- emptyEmbedding :: CtxEmbedding EmptyCtx EmptyCtx+-- emptyEmbedding = identityEmbedding knownSize++extendEmbeddingRightDiff :: forall ctx ctx' ctx''.+                            Diff ctx' ctx''+                            -> CtxEmbedding ctx ctx'+                            -> CtxEmbedding ctx ctx''+extendEmbeddingRightDiff diff (CtxEmbedding sz' assgn) = CtxEmbedding (extSize sz' diff) updated+  where+    updated :: Assignment (Index ctx'') ctx+    updated = fmapFC (extendIndex' diff) assgn++extendEmbeddingRight :: CtxEmbedding ctx ctx' -> CtxEmbedding ctx (ctx' ::> tp)+extendEmbeddingRight = extendEmbeddingRightDiff knownDiff++extendEmbeddingBoth :: forall ctx ctx' tp. CtxEmbedding ctx ctx' -> CtxEmbedding (ctx ::> tp) (ctx' ::> tp)+extendEmbeddingBoth ctxe = updated & ctxeAssignment %~ flip extend (nextIndex (ctxe ^. ctxeSize))+  where+    updated :: CtxEmbedding ctx (ctx' ::> tp)+    updated = extendEmbeddingRight ctxe++-- | Pattern synonym for the empty assignment+pattern Empty :: () => ctx ~ EmptyCtx => Assignment f ctx+pattern Empty <- (view -> AssignEmpty)+  where Empty = empty++infixl :>++-- | Pattern synonym for extending an assignment on the right+pattern (:>) :: () => ctx' ~ (ctx ::> tp) => Assignment f ctx -> f tp -> Assignment f ctx'+pattern (:>) a v <- (view -> AssignExtend a v)+  where a :> v = extend a v++-- The COMPLETE pragma was not defined until ghc 8.2.*+#if MIN_VERSION_base(4,10,0)+{-# COMPLETE (:>), Empty :: Assignment  #-}+#endif++--------------------------------------------------------------------------------+-- Static indexing based on type-level naturals++-- | Get an element from an 'Assignment' by zero-based, left-to-right position.+-- The position must be specified using @TypeApplications@ for the @n@ parameter.+getCtx :: forall n ctx f r. Idx n ctx r => Assignment f ctx -> f r+getCtx asgn = asgn ! natIndex @n++setCtx :: forall n ctx f r. Idx n ctx r => f r -> Assignment f ctx -> Assignment f ctx+setCtx = update (natIndex @n)++-- | Get a lens for an position in an 'Assignment' by zero-based, left-to-right position.+-- The position must be specified using @TypeApplications@ for the @n@ parameter.+field :: forall n ctx f r. Idx n ctx r => Lens' (Assignment f ctx) (f r)+field f = adjustM f (natIndex @n)++-- | Constraint synonym used for getting an 'Index' into a 'Ctx'.+-- @n@ is the zero-based, left-counted index into the list of types+-- @ctx@ which has the type @r@.+type Idx n ctx r = (ValidIx n ctx, Idx' (FromLeft ctx n) ctx r)++-- | Compute an 'Index' value for a particular position in a 'Ctx'. The+-- @TypeApplications@ extension will be needed to disambiguate the choice+-- of the type @n@.+natIndex :: forall n ctx r. Idx n ctx r => Index ctx r+natIndex = natIndex' @_ @(FromLeft ctx n)++-- | This version of 'natIndex' is suitable for use without the @TypeApplications@+-- extension.+natIndexProxy :: forall n ctx r proxy. Idx n ctx r => proxy n -> Index ctx r+natIndexProxy _ = natIndex @n++------------------------------------------------------------------------+-- Implementation+------------------------------------------------------------------------++-- | Class for computing 'Index' values for positions in a 'Ctx'.+class KnownContext ctx => Idx' (n :: Nat) (ctx :: Ctx k) (r :: k) | n ctx -> r where+  natIndex' :: Index ctx r++-- | Base-case+instance KnownContext xs => Idx' 0 (xs '::> x) x where+  natIndex' = lastIndex knownSize++-- | Inductive-step+instance {-# Overlaps #-} (KnownContext xs, Idx' (n-1) xs r) =>+  Idx' n (xs '::> x) r where++  natIndex' = skip (natIndex' @_ @(n-1))+++--------------------------------------------------------------------------------+-- CurryAssignment++-- | This type family is used to define currying\/uncurrying operations+-- on assignments.  It is best understood by seeing its evaluation on+-- several examples:+--+-- > CurryAssignment EmptyCtx f x = x+-- > CurryAssignment (EmptyCtx ::> a) f x = f a -> x+-- > CurryAssignment (EmptyCtx ::> a ::> b) f x = f a -> f b -> x+-- > CurryAssignment (EmptyCtx ::> a ::> b ::> c) f x = f a -> f b -> f c -> x+type family CurryAssignment (ctx :: Ctx k) (f :: k -> *) (x :: *) :: * where+   CurryAssignment EmptyCtx    f x = x+   CurryAssignment (ctx ::> a) f x = CurryAssignment ctx f (f a -> x)++-- | This class implements two methods that witness the isomorphism between+--   curried and uncurried functions.+class CurryAssignmentClass (ctx :: Ctx k) where++  -- | Transform a function that accepts an assignment into one with a separate+  --   variable for each element of the assignment.+  curryAssignment   :: (Assignment f ctx -> x) -> CurryAssignment ctx f x++  -- | Transform a curried function into one that accepts an assignment value.+  uncurryAssignment :: CurryAssignment ctx f x -> (Assignment f ctx -> x)++instance CurryAssignmentClass EmptyCtx where+  curryAssignment k = k empty+  uncurryAssignment k _ = k++instance CurryAssignmentClass ctx => CurryAssignmentClass (ctx ::> a) where+  curryAssignment k = curryAssignment (\asgn a -> k (asgn :> a))+  uncurryAssignment k asgn =+    case view asgn of+      AssignExtend asgn' x -> uncurryAssignment k asgn' x
+ src/Data/Parameterized/Context/Safe.hs view
@@ -0,0 +1,961 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.Context.Safe+-- Copyright        : (c) Galois, Inc 2014-2015+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module defines type contexts as a data-kind that consists of+-- a list of types.  Indexes are defined with respect to these contexts.+-- In addition, finite dependent products (Assignments) are defined over+-- type contexts.  The elements of an assignment can be accessed using+-- appropriately-typed indexes.+--+-- This module is intended to export exactly the same API as module+-- "Data.Parameterized.Context.Unsafe", so that they can be used+-- interchangeably.+--+-- This implementation is entirely typesafe, and provides a proof that+-- the signature implemented by this module is consistent.  Contexts,+-- indexes, and assignments are represented naively by linear sequences.+--+-- Compared to the implementation in "Data.Parameterized.Context.Unsafe",+-- this one suffers from the fact that the operation of extending an+-- the context of an index is /not/ a no-op. We therefore cannot use+-- 'Data.Coerce.coerce' to understand indexes in a new context without+-- actually breaking things.+--------------------------------------------------------------------------+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+module Data.Parameterized.Context.Safe+  ( module Data.Parameterized.Ctx+  , Size+  , sizeInt+  , zeroSize+  , incSize+  , decSize+  , extSize+  , addSize+  , SizeView(..)+  , viewSize+  , KnownContext(..)+    -- * Diff+  , Diff+  , noDiff+  , extendRight+  , KnownDiff(..)+  , DiffView(..)+  , viewDiff+    -- * Indexing+  , Index+  , indexVal+  , base+  , skip+  , lastIndex+  , nextIndex+  , extendIndex+  , extendIndex'+  , forIndex+  , intIndex+    -- * Assignments+  , Assignment+  , size+  , replicate+  , generate+  , generateM+  , empty+  , null+  , extend+  , update+  , adjust+  , adjustM+  , init+  , AssignView(..)+  , view+  , decompose+  , (!)+  , (!^)+  , toList+  , zipWith+  , zipWithM+  , (<++>)+  , traverseWithIndex+  ) where++import qualified Control.Category as Cat+import Control.DeepSeq+import qualified Control.Lens as Lens+import Control.Monad.Identity (Identity(..))+import Data.Hashable+import Data.List (intercalate)+import Data.Maybe (listToMaybe)+import Data.Type.Equality+import Prelude hiding (init, map, null, replicate, succ, zipWith)++#if !MIN_VERSION_base(4,8,0)+import Data.Functor+import Control.Applicative (Applicative(..))+#endif++import Data.Parameterized.Classes+import Data.Parameterized.Ctx+import Data.Parameterized.Some+import Data.Parameterized.TraversableFC++------------------------------------------------------------------------+-- Size++-- | An indexed singleton type representing the size of a context.+data Size (ctx :: Ctx k) where+  SizeZero :: Size 'EmptyCtx+  SizeSucc :: Size ctx -> Size (ctx '::> tp)++-- | Convert a context size to an 'Int'.+sizeInt :: Size ctx -> Int+sizeInt SizeZero = 0+sizeInt (SizeSucc sz) = (+1) $! sizeInt sz++-- | The size of an empty context.+zeroSize :: Size 'EmptyCtx+zeroSize = SizeZero++-- | Increment the size to the next value.+incSize :: Size ctx -> Size (ctx '::> tp)+incSize sz = SizeSucc sz++decSize :: Size (ctx '::> tp) -> Size ctx+decSize (SizeSucc sz) = sz++-- | The total size of two concatenated contexts.+addSize :: Size x -> Size y -> Size (x <+> y)+addSize sx SizeZero = sx+addSize sx (SizeSucc sy) = SizeSucc (addSize sx sy)++-- | Allows interpreting a size.+data SizeView (ctx :: Ctx k) where+  ZeroSize :: SizeView 'EmptyCtx+  IncSize :: !(Size ctx) -> SizeView (ctx '::> tp)++-- | View a size as either zero or a smaller size plus one.+viewSize :: Size ctx -> SizeView ctx+viewSize SizeZero = ZeroSize+viewSize (SizeSucc s) = IncSize s++------------------------------------------------------------------------+-- Size++-- | A context that can be determined statically at compiler time.+class KnownContext (ctx :: Ctx k) where+  knownSize :: Size ctx++instance KnownContext 'EmptyCtx where+  knownSize = zeroSize++instance KnownContext ctx => KnownContext (ctx '::> tp) where+  knownSize = incSize knownSize++------------------------------------------------------------------------+-- Diff++-- | Difference in number of elements between two contexts.+-- The first context must be a sub-context of the other.+data Diff (l :: Ctx k) (r :: Ctx k) where+  DiffHere :: Diff ctx ctx+  DiffThere :: Diff ctx1 ctx2 -> Diff ctx1 (ctx2 '::> tp)++-- | The identity difference.+noDiff :: Diff l l+noDiff = DiffHere++-- | Extend the difference to a sub-context of the right side.+extendRight :: Diff l r -> Diff l (r '::> tp)+extendRight diff = DiffThere diff++composeDiff :: Diff a b -> Diff b c -> Diff a c+composeDiff x DiffHere = x+composeDiff x (DiffThere y) = DiffThere (composeDiff x y)++instance Cat.Category Diff where+  id = DiffHere+  d1 . d2 = composeDiff d2 d1++-- | Extend the size by a given difference.+extSize :: Size l -> Diff l r -> Size r+extSize sz DiffHere = sz+extSize sz (DiffThere diff) = incSize (extSize sz diff)++data DiffView a b where+  NoDiff :: DiffView a a+  ExtendRightDiff :: Diff a b -> DiffView a (b ::> r)++viewDiff :: Diff a b -> DiffView a b+viewDiff DiffHere = NoDiff+viewDiff (DiffThere diff) = ExtendRightDiff diff++------------------------------------------------------------------------+-- KnownDiff++-- | A difference that can be automatically inferred at compile time.+class KnownDiff (l :: Ctx k) (r :: Ctx k) where+  knownDiff :: Diff l r++instance KnownDiff l l where+  knownDiff = noDiff++instance KnownDiff l r => KnownDiff l (r '::> tp) where+  knownDiff = extendRight knownDiff++------------------------------------------------------------------------+-- Index++-- | An index is a reference to a position with a particular type in a+-- context.+data Index (ctx :: Ctx k) (tp :: k) where+  IndexHere :: Size ctx -> Index (ctx '::> tp) tp+  IndexThere :: Index ctx tp -> Index (ctx '::> tp') tp++-- | Convert an index to an 'Int', where the index of the left-most type in the context is 0.+indexVal :: Index ctx tp -> Int+indexVal (IndexHere sz) = sizeInt sz+indexVal (IndexThere idx) = indexVal idx++instance Eq (Index ctx tp) where+  idx1 == idx2 = isJust (testEquality idx1 idx2)++instance TestEquality (Index ctx) where+  testEquality (IndexHere _) (IndexHere _) = Just Refl+  testEquality (IndexHere _) (IndexThere _) = Nothing+  testEquality (IndexThere _) (IndexHere _) = Nothing+  testEquality (IndexThere idx1) (IndexThere idx2) =+     case testEquality idx1 idx2 of+         Just Refl -> Just Refl+         Nothing -> Nothing++instance Ord (Index ctx tp) where+  compare i j = toOrdering (compareF i j)++instance OrdF (Index ctx) where+  compareF (IndexHere _) (IndexHere _) = EQF+  compareF (IndexThere _) (IndexHere _) = LTF+  compareF (IndexHere _) (IndexThere _) = GTF+  compareF (IndexThere idx1) (IndexThere idx2) = lexCompareF idx1 idx2 $ EQF++-- | Index for first element in context.+base :: Index ('EmptyCtx '::> tp) tp+base = IndexHere SizeZero++-- | Increase context while staying at same index.+skip :: Index ctx x -> Index (ctx '::> y) x+skip idx = IndexThere idx++-- | Return the index of an element one past the size.+nextIndex :: Size ctx -> Index (ctx '::> tp) tp+nextIndex sz = IndexHere sz++-- | Return the last index of a element.+lastIndex :: Size (ctx ::> tp) -> Index (ctx ::> tp) tp+lastIndex (SizeSucc s) = IndexHere s++{-# INLINE extendIndex #-}+extendIndex :: KnownDiff l r => Index l tp -> Index r tp+extendIndex = extendIndex' knownDiff++{-# INLINE extendIndex' #-}+extendIndex' :: Diff l r -> Index l tp -> Index r tp+extendIndex' DiffHere idx = idx+extendIndex' (DiffThere diff) idx = IndexThere (extendIndex' diff idx)++-- | Given a size @n@, an initial value @v0@, and a function @f@,+-- @forIndex n v0 f@ calls @f@ on each index less than @n@ starting+-- from @0@ and @v0@, with the value @v@ obtained from the last call.+forIndex :: forall ctx r+          . Size ctx+         -> (forall tp . r -> Index ctx tp -> r)+         -> r+         -> r+forIndex sz_top f = go id sz_top+ where go :: forall ctx'. (forall tp. Index ctx' tp -> Index ctx tp) -> Size ctx' -> r -> r+       go _ SizeZero = id+       go g (SizeSucc sz) = \r -> go (\i -> g (IndexThere i)) sz  $ f r (g (IndexHere sz))+++indexList :: forall ctx. Size ctx -> [Some (Index ctx)]+indexList sz_top = go id [] sz_top+ where go :: (forall tp. Index ctx' tp -> Index ctx tp)+          -> [Some (Index ctx)]+          -> Size ctx'+          -> [Some (Index ctx)]+       go _ ls SizeZero       = ls+       go g ls (SizeSucc sz)  = go (\i -> g (IndexThere i)) (Some (g (IndexHere sz)) : ls) sz++-- | Return index at given integer or nothing if integer is out of bounds.+intIndex :: Int -> Size ctx -> Maybe (Some (Index ctx))+intIndex n sz = listToMaybe $ drop n $ indexList sz++instance Show (Index ctx tp) where+   show = show . indexVal++instance ShowF (Index ctx)++------------------------------------------------------------------------+-- Assignment++-- | An assignment is a sequence that maps each index with type 'tp' to+-- a value of type 'f tp'.+data Assignment (f :: k -> *) (ctx :: Ctx k) where+  AssignmentEmpty :: Assignment f EmptyCtx+  AssignmentExtend :: Assignment f ctx -> f tp -> Assignment f (ctx ::> tp)++-- | View an assignment as either empty or an assignment with one appended.+data AssignView (f :: k -> *) (ctx :: Ctx k) where+  AssignEmpty :: AssignView f EmptyCtx+  AssignExtend :: Assignment f ctx -> f tp -> AssignView f (ctx::>tp)++view :: forall f ctx . Assignment f ctx -> AssignView f ctx+view AssignmentEmpty = AssignEmpty+view (AssignmentExtend asgn x) = AssignExtend asgn x++decompose :: Assignment f (ctx ::> tp) -> (Assignment f ctx, f tp)+decompose (AssignmentExtend a v) = (a,v)++instance NFData (Assignment f ctx) where+  rnf AssignmentEmpty = ()+  rnf (AssignmentExtend asgn x) = rnf asgn `seq` x `seq` ()++-- | Return number of elements in assignment.+size :: Assignment f ctx -> Size ctx+size AssignmentEmpty = SizeZero+size (AssignmentExtend asgn _) = SizeSucc (size asgn)++-- | Generate an assignment+generate :: forall ctx f+          . Size ctx+         -> (forall tp . Index ctx tp -> f tp)+         -> Assignment f ctx+generate sz_top f = go id sz_top+ where go :: forall ctx'+           . (forall tp. Index ctx' tp -> Index ctx tp)+          -> Size ctx'+          -> Assignment f ctx'+       go _ SizeZero = AssignmentEmpty+       go g (SizeSucc sz) =+            let ctx = go (\i -> g (IndexThere i)) sz+                x = f (g (IndexHere sz))+             in AssignmentExtend ctx x++-- | Generate an assignment+generateM :: forall ctx m f+           . Applicative m+          => Size ctx+          -> (forall tp . Index ctx tp -> m (f tp))+          -> m (Assignment f ctx)+generateM sz_top f = go id sz_top+ where go :: forall ctx'. (forall tp. Index ctx' tp -> Index ctx tp) -> Size ctx' -> m (Assignment f ctx')+       go _ SizeZero = pure AssignmentEmpty+       go g (SizeSucc sz) =+             AssignmentExtend <$> (go (\i -> g (IndexThere i)) sz) <*> f (g (IndexHere sz))++-- | @replicate n@ make a context with different copies of the same+-- polymorphic value.+replicate :: Size ctx -> (forall tp . f tp) -> Assignment f ctx+replicate n c = generate n (\_ -> c)++-- | Create empty indexec vector.+empty :: Assignment f 'EmptyCtx+empty = AssignmentEmpty++-- | Return true if assignment is empty.+null :: Assignment f ctx -> Bool+null AssignmentEmpty = True+null _ = False++extend :: Assignment f ctx -> f tp -> Assignment f (ctx '::> tp)+extend asgn e = AssignmentExtend asgn e++update :: Index ctx tp -> f tp -> Assignment f ctx -> Assignment f ctx+update idx e asgn = adjust (\_ -> e) idx asgn++adjust :: forall f ctx tp. (f tp -> f tp) -> Index ctx tp -> Assignment f ctx -> Assignment f ctx+adjust f idx m = runIdentity (adjustM (Identity . f) idx m)++adjustM :: forall m f ctx tp. Functor m => (f tp -> m (f tp)) -> Index ctx tp -> Assignment f ctx -> m (Assignment f ctx)+adjustM f = go (\x -> x)+ where+  go :: (forall tp'. g tp' -> f tp') -> Index ctx' tp -> Assignment g ctx' -> m (Assignment f ctx')+  go g (IndexHere _)     (AssignmentExtend asgn x) = AssignmentExtend (map g asgn) <$> f (g x)+  go g (IndexThere idx)  (AssignmentExtend asgn x) = flip AssignmentExtend (g x)   <$> go g idx asgn+#if !MIN_VERSION_base(4,9,0)+-- GHC 7.10.3 and early does not recognize that the above definition is complete,+-- and so need the equation below.  GHC 8.0.1 does not require the additional+-- equation.+  go _ _ _ = error "SafeTypeContext.adjustM: impossible!"+#endif++type instance IndexF   (Assignment (f :: k -> *) ctx) = Index ctx+type instance IxValueF (Assignment (f :: k -> *) ctx) = f++instance forall (f :: k -> *) ctx. IxedF k (Assignment f ctx) where+  ixF :: Index ctx x -> Lens.Lens' (Assignment f ctx) (f x)+  ixF idx f = adjustM f idx++instance forall (f :: k -> *) ctx. IxedF' k (Assignment f ctx) where+  ixF' :: Index ctx x -> Lens.Lens' (Assignment f ctx) (f x)+  ixF' idx f = adjustM f idx+++-- | Return assignment with all but the last block.+init :: Assignment f (ctx '::> tp) -> Assignment f ctx+init (AssignmentExtend asgn _) = asgn++idxlookup :: (forall tp. a tp -> b tp) -> Assignment a ctx -> forall tp. Index ctx tp -> b tp+idxlookup f (AssignmentExtend _   x) (IndexHere _) = f x+idxlookup f (AssignmentExtend ctx _) (IndexThere idx) = idxlookup f ctx idx+idxlookup _ AssignmentEmpty _ = error "Data.Parameterized.Context.Safe.lookup: impossible case"++-- | Return value of assignment.+(!) :: Assignment f ctx -> Index ctx tp -> f tp+(!) asgn idx = idxlookup id asgn idx++-- | Return value of assignment, where the index is into an+--   initial sequence of the assignment.+(!^) :: KnownDiff l r => Assignment f r -> Index l tp -> f tp+a !^ i = a ! extendIndex i++instance TestEquality f => Eq (Assignment f ctx) where+  x == y = isJust (testEquality x y)++testEq :: (forall x y. f x -> f y -> Maybe (x :~: y))+       -> Assignment f cxt1 -> Assignment f cxt2 -> Maybe (cxt1 :~: cxt2)+testEq _ AssignmentEmpty AssignmentEmpty = Just Refl+testEq test (AssignmentExtend ctx1 x1) (AssignmentExtend ctx2 x2) =+     case testEq test ctx1 ctx2 of+       Nothing -> Nothing+       Just Refl ->+          case test x1 x2 of+             Nothing -> Nothing+             Just Refl -> Just Refl+testEq _ AssignmentEmpty AssignmentExtend{} = Nothing+testEq _ AssignmentExtend{} AssignmentEmpty = Nothing++instance TestEqualityFC Assignment where+   testEqualityFC = testEq+instance TestEquality f => TestEquality (Assignment f) where+   testEquality x y = testEq testEquality x y+instance TestEquality f => PolyEq (Assignment f x) (Assignment f y) where+  polyEqF x y = fmap (\Refl -> Refl) $ testEquality x y++compareAsgn :: (forall x y. f x -> f y -> OrderingF x y)+            -> Assignment f ctx1 -> Assignment f ctx2 -> OrderingF ctx1 ctx2+compareAsgn _ AssignmentEmpty AssignmentEmpty = EQF+compareAsgn _ AssignmentEmpty _ = GTF+compareAsgn _ _ AssignmentEmpty = LTF+compareAsgn test (AssignmentExtend ctx1 x) (AssignmentExtend ctx2 y) =+  case compareAsgn test ctx1 ctx2 of+    LTF -> LTF+    GTF -> GTF+    EQF -> case test x y of+              LTF -> LTF+              GTF -> GTF+              EQF -> EQF++instance OrdFC Assignment where+  compareFC = compareAsgn++instance OrdF f => OrdF (Assignment f) where+  compareF = compareAsgn compareF++instance OrdF f => Ord (Assignment f ctx) where+  compare x y = toOrdering (compareF x y)+++instance Hashable (Index ctx tp) where+  hashWithSalt = hashWithSaltF+instance HashableF (Index ctx) where+  hashWithSaltF s i = hashWithSalt s (indexVal i)++instance HashableF f => HashableF (Assignment f) where+  hashWithSaltF = hashWithSalt++instance HashableF f => Hashable (Assignment f ctx) where+  hashWithSalt s AssignmentEmpty = s+  hashWithSalt s (AssignmentExtend asgn x) = (s `hashWithSalt` asgn) `hashWithSaltF` x++instance ShowF f => Show (Assignment f ctx) where+  show a = "[" ++ intercalate ", " (toList showF a) ++ "]"++instance ShowF f => ShowF (Assignment f)++instance FunctorFC Assignment where+  fmapFC = fmapFCDefault++instance FoldableFC Assignment where+  foldMapFC = foldMapFCDefault++instance TraversableFC Assignment where+  traverseFC = traverseF++-- | Map assignment+map :: (forall tp . f tp -> g tp) -> Assignment f c -> Assignment g c+map = fmapFC++traverseF :: forall (f:: k -> *) (g::k -> *) (m:: * -> *) (c::Ctx k)+           . Applicative m+          => (forall tp . f tp -> m (g tp))+          -> Assignment f c+          -> m (Assignment g c)+traverseF _ AssignmentEmpty = pure AssignmentEmpty+traverseF f (AssignmentExtend asgn x) = pure AssignmentExtend <*> traverseF f asgn <*> f x++-- | Convert assignment to list.+toList :: (forall tp . f tp -> a)+       -> Assignment f c+       -> [a]+toList = toListFC++zipWithM :: Applicative m+         => (forall tp . f tp -> g tp -> m (h tp))+         -> Assignment f c+         -> Assignment g c+         -> m (Assignment h c)+zipWithM f x y = go x y+ where go AssignmentEmpty AssignmentEmpty = pure AssignmentEmpty+       go (AssignmentExtend asgn1 x1) (AssignmentExtend asgn2 x2) =+             AssignmentExtend <$> (zipWithM f asgn1 asgn2) <*> (f x1 x2)++zipWith :: (forall x . f x -> g x -> h x)+        -> Assignment f a+        -> Assignment g a+        -> Assignment h a+zipWith f = \x y -> runIdentity $ zipWithM (\u v -> pure (f u v)) x y+{-# INLINE zipWith #-}++traverseWithIndex :: Applicative m+                  => (forall tp . Index ctx tp -> f tp -> m (g tp))+                  -> Assignment f ctx+                  -> m (Assignment g ctx)+traverseWithIndex f a = generateM (size a) $ \i -> f i (a ! i)++(<++>) :: Assignment f x -> Assignment f y -> Assignment f (x <+> y)+x <++> AssignmentEmpty = x+x <++> AssignmentExtend y t = AssignmentExtend (x <++> y) t++------------------------------------------------------------------------+-- KnownRepr instances++instance (KnownRepr (Assignment f) ctx, KnownRepr f bt)+      => KnownRepr (Assignment f) (ctx ::> bt) where+  knownRepr = knownRepr `extend` knownRepr++instance KnownRepr (Assignment f) EmptyCtx where+  knownRepr = empty++--------------------------------------------------------------------------------------+-- lookups and update for lenses++data MyNat where+  MyZ :: MyNat+  MyS :: MyNat -> MyNat++type MyZ = 'MyZ+type MyS = 'MyS++data MyNatRepr :: MyNat -> * where+  MyZR :: MyNatRepr MyZ+  MySR :: MyNatRepr n -> MyNatRepr (MyS n)++type family StrongCtxUpdate (n::MyNat) (ctx::Ctx k) (z::k) :: Ctx k where+  StrongCtxUpdate n       EmptyCtx     z = EmptyCtx+  StrongCtxUpdate MyZ     (ctx::>x)    z = ctx ::> z+  StrongCtxUpdate (MyS n) (ctx::>x)    z = (StrongCtxUpdate n ctx z) ::> x++type family MyNatLookup (n::MyNat) (ctx::Ctx k) (f::k -> *) :: * where+  MyNatLookup n       EmptyCtx  f = ()+  MyNatLookup MyZ     (ctx::>x) f = f x+  MyNatLookup (MyS n) (ctx::>x) f = MyNatLookup n ctx f++mynat_lookup :: MyNatRepr n -> Assignment f ctx -> MyNatLookup n ctx f+mynat_lookup _   AssignmentEmpty = ()+mynat_lookup MyZR     (AssignmentExtend _    x) = x+mynat_lookup (MySR n) (AssignmentExtend asgn _) = mynat_lookup n asgn++strong_ctx_update :: MyNatRepr n -> Assignment f ctx -> f tp -> Assignment f (StrongCtxUpdate n ctx tp)+strong_ctx_update _        AssignmentEmpty           _ = AssignmentEmpty+strong_ctx_update MyZR     (AssignmentExtend asgn _) z = AssignmentExtend asgn z+strong_ctx_update (MySR n) (AssignmentExtend asgn x) z = AssignmentExtend (strong_ctx_update n asgn z) x++------------------------------------------------------------------------+-- 1 field lens combinators++type Assignment1 f x1 = Assignment f ('EmptyCtx '::> x1)++instance Lens.Field1 (Assignment1 f t) (Assignment1 f u) (f t) (f u) where++  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 2 field lens combinators++type Assignment2 f x1 x2+   = Assignment f ('EmptyCtx '::> x1 '::> x2)++instance Lens.Field1 (Assignment2 f t x2) (Assignment2 f u x2) (f t) (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field2 (Assignment2 f x1 t) (Assignment2 f x1 u) (f t) (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR+++------------------------------------------------------------------------+-- 3 field lens combinators++type Assignment3 f x1 x2 x3+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3)++instance Lens.Field1 (Assignment3 f t x2 x3)+                     (Assignment3 f u x2 x3)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment3 f x1 t x3)+                     (Assignment3 f x1 u x3)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field3 (Assignment3 f x1 x2 t)+                     (Assignment3 f x1 x2 u)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 4 field lens combinators++type Assignment4 f x1 x2 x3 x4+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4)++instance Lens.Field1 (Assignment4 f t x2 x3 x4)+                     (Assignment4 f u x2 x3 x4)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment4 f x1 t x3 x4)+                     (Assignment4 f x1 u x3 x4)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment4 f x1 x2 t x4)+                     (Assignment4 f x1 x2 u x4)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field4 (Assignment4 f x1 x2 x3 t)+                     (Assignment4 f x1 x2 x3 u)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR+++------------------------------------------------------------------------+-- 5 field lens combinators++type Assignment5 f x1 x2 x3 x4 x5+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5)++instance Lens.Field1 (Assignment5 f t x2 x3 x4 x5)+                     (Assignment5 f u x2 x3 x4 x5)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment5 f x1 t x3 x4 x5)+                     (Assignment5 f x1 u x3 x4 x5)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment5 f x1 x2 t x4 x5)+                     (Assignment5 f x1 x2 u x4 x5)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field4 (Assignment5 f x1 x2 x3 t x5)+                     (Assignment5 f x1 x2 x3 u x5)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field5 (Assignment5 f x1 x2 x3 x4 t)+                     (Assignment5 f x1 x2 x3 x4 u)+                     (f t)+                     (f u) where+  _5 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 6 field lens combinators++type Assignment6 f x1 x2 x3 x4 x5 x6+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6)++instance Lens.Field1 (Assignment6 f t x2 x3 x4 x5 x6)+                     (Assignment6 f u x2 x3 x4 x5 x6)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment6 f x1 t x3 x4 x5 x6)+                     (Assignment6 f x1 u x3 x4 x5 x6)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment6 f x1 x2 t x4 x5 x6)+                     (Assignment6 f x1 x2 u x4 x5 x6)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field4 (Assignment6 f x1 x2 x3 t x5 x6)+                     (Assignment6 f x1 x2 x3 u x5 x6)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field5 (Assignment6 f x1 x2 x3 x4 t x6)+                     (Assignment6 f x1 x2 x3 x4 u x6)+                     (f t)+                     (f u) where+  _5 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field6 (Assignment6 f x1 x2 x3 x4 x5 t)+                     (Assignment6 f x1 x2 x3 x4 x5 u)+                     (f t)+                     (f u) where+  _6 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 7 field lens combinators++type Assignment7 f x1 x2 x3 x4 x5 x6 x7+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7)++instance Lens.Field1 (Assignment7 f t x2 x3 x4 x5 x6 x7)+                     (Assignment7 f u x2 x3 x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment7 f x1 t x3 x4 x5 x6 x7)+                     (Assignment7 f x1 u x3 x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment7 f x1 x2 t x4 x5 x6 x7)+                     (Assignment7 f x1 x2 u x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field4 (Assignment7 f x1 x2 x3 t x5 x6 x7)+                     (Assignment7 f x1 x2 x3 u x5 x6 x7)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field5 (Assignment7 f x1 x2 x3 x4 t x6 x7)+                     (Assignment7 f x1 x2 x3 x4 u x6 x7)+                     (f t)+                     (f u) where+  _5 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field6 (Assignment7 f x1 x2 x3 x4 x5 t x7)+                     (Assignment7 f x1 x2 x3 x4 x5 u x7)+                     (f t)+                     (f u) where+  _6 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field7 (Assignment7 f x1 x2 x3 x4 x5 x6 t)+                     (Assignment7 f x1 x2 x3 x4 x5 x6 u)+                     (f t)+                     (f u) where+  _7 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 8 field lens combinators++type Assignment8 f x1 x2 x3 x4 x5 x6 x7 x8+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7 '::> x8)++instance Lens.Field1 (Assignment8 f t x2 x3 x4 x5 x6 x7 x8)+                     (Assignment8 f u x2 x3 x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR+++instance Lens.Field2 (Assignment8 f x1 t x3 x4 x5 x6 x7 x8)+                     (Assignment8 f x1 u x3 x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment8 f x1 x2 t x4 x5 x6 x7 x8)+                     (Assignment8 f x1 x2 u x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field4 (Assignment8 f x1 x2 x3 t x5 x6 x7 x8)+                     (Assignment8 f x1 x2 x3 u x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field5 (Assignment8 f x1 x2 x3 x4 t x6 x7 x8)+                     (Assignment8 f x1 x2 x3 x4 u x6 x7 x8)+                     (f t)+                     (f u) where+  _5 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field6 (Assignment8 f x1 x2 x3 x4 x5 t x7 x8)+                     (Assignment8 f x1 x2 x3 x4 x5 u x7 x8)+                     (f t)+                     (f u) where+  _6 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field7 (Assignment8 f x1 x2 x3 x4 x5 x6 t x8)+                     (Assignment8 f x1 x2 x3 x4 x5 x6 u x8)+                     (f t)+                     (f u) where+  _7 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field8 (Assignment8 f x1 x2 x3 x4 x5 x6 x7 t)+                     (Assignment8 f x1 x2 x3 x4 x5 x6 x7 u)+                     (f t)+                     (f u) where+  _8 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR++------------------------------------------------------------------------+-- 9 field lens combinators++type Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 x9+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7 '::> x8 '::> x9)+++instance Lens.Field1 (Assignment9 f t x2 x3 x4 x5 x6 x7 x8 x9)+                     (Assignment9 f u x2 x3 x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _1 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field2 (Assignment9 f x1 t x3 x4 x5 x6 x7 x8 x9)+                     (Assignment9 f x1 u x3 x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _2 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field3 (Assignment9 f x1 x2 t x4 x5 x6 x7 x8 x9)+                     (Assignment9 f x1 x2 u x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _3 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field4 (Assignment9 f x1 x2 x3 t x5 x6 x7 x8 x9)+                     (Assignment9 f x1 x2 x3 u x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _4 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field5 (Assignment9 f x1 x2 x3 x4 t x6 x7 x8 x9)+                     (Assignment9 f x1 x2 x3 x4 u x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _5 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MySR $ MyZR++instance Lens.Field6 (Assignment9 f x1 x2 x3 x4 x5 t x7 x8 x9)+                     (Assignment9 f x1 x2 x3 x4 x5 u x7 x8 x9)+                     (f t)+                     (f u) where+  _6 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MySR $ MyZR++instance Lens.Field7 (Assignment9 f x1 x2 x3 x4 x5 x6 t x8 x9)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 u x8 x9)+                     (f t)+                     (f u) where+  _7 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MySR $ MyZR++instance Lens.Field8 (Assignment9 f x1 x2 x3 x4 x5 x6 x7 t x9)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 x7 u x9)+                     (f t)+                     (f u) where+  _8 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MySR $ MyZR++instance Lens.Field9 (Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 t)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 u)+                     (f t)+                     (f u) where+  _9 = Lens.lens (mynat_lookup n) (strong_ctx_update n)+        where n = MyZR
+ src/Data/Parameterized/Context/Unsafe.hs view
@@ -0,0 +1,1278 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.Parameterized.Context.Unsafe+  ( module Data.Parameterized.Ctx+  , Size+  , sizeInt+  , zeroSize+  , incSize+  , decSize+  , extSize+  , addSize+  , SizeView(..)+  , viewSize+  , KnownContext(..)+    -- * Diff+  , Diff+  , noDiff+  , extendRight+  , KnownDiff(..)+  , DiffView(..)+  , viewDiff+    -- * Indexing+  , Index+  , indexVal+  , base+  , skip+  , lastIndex+  , nextIndex+  , extendIndex+  , extendIndex'+  , forIndex+  , forIndexRange+  , forIndexM+  , intIndex+    -- ** IndexRange+  , IndexRange+  , allRange+  , indexOfRange+  , dropHeadRange+  , dropTailRange+    -- * Assignments+  , Assignment+  , size+  , replicate+  , generate+  , generateM+  , generateSome+  , generateSomeM+  , empty+  , null+  , extend+  , update+  , adjust+  , adjustM+  , init+  , last+  , AssignView(..)+  , view+  , decompose+  , fromList+  , (!)+  , (!^)+  , zipWith+  , zipWithM+  , (<++>)+  , traverseWithIndex+  ) where++import           Control.Applicative hiding (empty)+import qualified Control.Category as Cat+import           Control.DeepSeq+import           Control.Exception+import qualified Control.Lens as Lens+import           Control.Monad.Identity (Identity(..))+import           Data.Bits+import           Data.Coerce+import           Data.Hashable+import           Data.List (intercalate)+import           Data.Proxy+import           Unsafe.Coerce++import           Prelude hiding (init, last, map, null, replicate, succ, zipWith, (++))+import qualified Prelude++import           Data.Parameterized.Classes+import           Data.Parameterized.Ctx+import           Data.Parameterized.Ctx.Proofs+import           Data.Parameterized.Some+import           Data.Parameterized.TraversableFC++------------------------------------------------------------------------+-- Size++-- | Represents the size of a context.+newtype Size (ctx :: Ctx k) = Size { sizeInt :: Int }++-- | The size of an empty context.+zeroSize :: Size 'EmptyCtx+zeroSize = Size 0++-- | Increment the size to the next value.+incSize :: Size ctx -> Size (ctx '::> tp)+incSize (Size n) = Size (n+1)++decSize :: Size (ctx '::> tp) -> Size ctx+decSize (Size n) = assert (n > 0) (Size (n-1))++-- | Allows interpreting a size.+data SizeView (ctx :: Ctx k) where+  ZeroSize :: SizeView 'EmptyCtx+  IncSize :: !(Size ctx) -> SizeView (ctx '::> tp)++-- | Project a size+viewSize :: Size ctx -> SizeView ctx+viewSize (Size 0) = unsafeCoerce ZeroSize+viewSize (Size n) = assert (n > 0) (unsafeCoerce (IncSize (Size (n-1))))++instance Show (Size ctx) where+  show (Size i) = show i++-- | A context that can be determined statically at compiler time.+class KnownContext (ctx :: Ctx k) where+  knownSize :: Size ctx++instance KnownContext 'EmptyCtx where+  knownSize = zeroSize++instance KnownContext ctx => KnownContext (ctx '::> tp) where+  knownSize = incSize knownSize++------------------------------------------------------------------------+-- Diff++-- | Difference in number of elements between two contexts.+-- The first context must be a sub-context of the other.+newtype Diff (l :: Ctx k) (r :: Ctx k)+      = Diff { _contextExtSize :: Int }++-- | The identity difference.+noDiff :: Diff l l+noDiff = Diff 0++-- | Extend the difference to a sub-context of the right side.+extendRight :: Diff l r -> Diff l (r '::> tp)+extendRight (Diff i) = Diff (i+1)++instance Cat.Category Diff where+  id = Diff 0+  Diff j . Diff i = Diff (i + j)++-- | Extend the size by a given difference.+extSize :: Size l -> Diff l r -> Size r+extSize (Size i) (Diff j) = Size (i+j)++-- | The total size of two concatenated contexts.+addSize :: Size x -> Size y -> Size (x <+> y)+addSize (Size x) (Size y) = Size (x + y)+++data DiffView a b where+  NoDiff :: DiffView a a+  ExtendRightDiff :: Diff a b -> DiffView a (b ::> r)++viewDiff :: Diff a b -> DiffView a b+viewDiff (Diff i)+  | i == 0 = unsafeCoerce NoDiff+  | otherwise  = assert (i > 0) $ unsafeCoerce $ ExtendRightDiff (Diff (i-1))++------------------------------------------------------------------------+-- KnownDiff++-- | A difference that can be automatically inferred at compile time.+class KnownDiff (l :: Ctx k) (r :: Ctx k) where+  knownDiff :: Diff l r++instance KnownDiff l l where+  knownDiff = noDiff++instance {-# INCOHERENT #-} KnownDiff l r => KnownDiff l (r '::> tp) where+  knownDiff = extendRight knownDiff++------------------------------------------------------------------------+-- Index++-- | An index is a reference to a position with a particular type in a+-- context.+newtype Index (ctx :: Ctx k) (tp :: k) = Index { indexVal :: Int }++instance Eq (Index ctx tp) where+  Index i == Index j = i == j++instance TestEquality (Index ctx) where+  testEquality (Index i) (Index j)+    | i == j = Just (unsafeCoerce Refl)+    | otherwise = Nothing++instance Ord (Index ctx tp) where+  Index i `compare` Index j = compare i j++instance OrdF (Index ctx) where+  compareF (Index i) (Index j)+    | i < j = LTF+    | i == j = unsafeCoerce EQF+    | otherwise = GTF++-- | Index for first element in context.+base :: Index ('EmptyCtx '::> tp) tp+base = Index 0++-- | Increase context while staying at same index.+skip :: Index ctx x -> Index (ctx '::> y) x+skip (Index i) = Index i++-- | Return the index of a element one past the size.+nextIndex :: Size ctx -> Index (ctx ::> tp) tp+nextIndex n = Index (sizeInt n)++-- | Return the last index of a element.+lastIndex :: Size (ctx ::> tp) -> Index (ctx ::> tp) tp+lastIndex n = Index (sizeInt n - 1)++{-# INLINE extendIndex #-}+extendIndex :: KnownDiff l r => Index l tp -> Index r tp+extendIndex = extendIndex' knownDiff++{-# INLINE extendIndex' #-}+extendIndex' :: Diff l r -> Index l tp -> Index r tp+extendIndex' _ = unsafeCoerce++-- | Given a size 'n', an initial value 'v0', and a function 'f', 'forIndex n v0 f'+-- is equivalent to 'v0' when 'n' is zero, and 'f (forIndex (n-1) v0) (n-1)' otherwise.+forIndex :: forall ctx r+          . Size ctx+         -> (forall tp . r -> Index ctx tp -> r)+         -> r+         -> r+forIndex n f r =+  case viewSize n of+    ZeroSize -> r+    IncSize p -> f (forIndex p (coerce f) r) (nextIndex p)++-- | Given an index 'i', size 'n', a function 'f', value 'v', and a function 'f',+-- 'forIndex i n f v' is equivalent to 'v' when 'i >= sizeInt n', and+-- 'f i (forIndexRange (i+1) n v0)' otherwise.+forIndexRange :: forall ctx r+               . Int+              -> Size ctx+              -> (forall tp . Index ctx tp -> r -> r)+              -> r+              -> r+forIndexRange i (Size n) f r+  | i >= n = r+  | otherwise = f (Index i) (forIndexRange (i+1) (Size n) f r)++-- |'forIndexM sz f' calls 'f' on indices '[0..sz-1]'.+forIndexM :: forall ctx m+           . Applicative m+          => Size ctx+          -> (forall tp . Index ctx tp -> m ())+          -> m ()+forIndexM sz f = forIndexRange 0 sz (\i r -> f i *> r) (pure ())++-- | Return index at given integer or nothing if integer is out of bounds.+intIndex :: Int -> Size ctx -> Maybe (Some (Index ctx))+intIndex i n | 0 <= i && i < sizeInt n = Just (Some (Index i))+             | otherwise = Nothing++instance Show (Index ctx tp) where+   show = show . indexVal++instance ShowF (Index ctx)++------------------------------------------------------------------------+-- IndexRange++-- | This represents a contiguous range of indices.+data IndexRange (ctx :: Ctx k) (sub :: Ctx k)+   = IndexRange {-# UNPACK #-} !Int+                {-# UNPACK #-} !Int++-- | Return a range containing all indices in the context.+allRange :: Size ctx -> IndexRange ctx ctx+allRange (Size n) = IndexRange 0 n++-- | `indexOfRange` returns the only index in a range.+indexOfRange :: IndexRange ctx (EmptyCtx ::> e) -> Index ctx e+indexOfRange (IndexRange i n) = assert (n == 1) $ Index i++-- | `dropTailRange r n` drops the last `n` elements in `r`.+dropTailRange :: IndexRange ctx (x <+> y) -> Size y -> IndexRange ctx x+dropTailRange (IndexRange i n) (Size j) = assert (n >= j) $ IndexRange i (n - j)++-- | `dropHeadRange r n` drops the first `n` elements in `r`.+dropHeadRange :: IndexRange ctx (x <+> y) -> Size x -> IndexRange ctx y+dropHeadRange (IndexRange i n) (Size j) = assert (i' >= i && n >= j) $ IndexRange i' (n - j)+  where i' = i + j++------------------------------------------------------------------------+-- Height++data Height = Zero | Succ Height++type family Pred (k :: Height) :: Height+type instance Pred ('Succ h) = h++------------------------------------------------------------------------+-- BalancedTree++-- | A balanced tree where all leaves are at the same height.+--+-- The first parameter is the height of the tree.+-- The second is the parameterized value.+data BalancedTree h (f :: k -> *) (p :: Ctx k) where+  BalLeaf :: !(f x) -> BalancedTree 'Zero f (SingleCtx x)+  BalPair :: !(BalancedTree h f x)+          -> !(BalancedTree h f y)+          -> BalancedTree ('Succ h) f (x <+> y)++bal_size :: BalancedTree h f p -> Int+bal_size (BalLeaf _) = 1+bal_size (BalPair x y) = bal_size x + bal_size y+++instance TestEqualityFC (BalancedTree h) where+  testEqualityFC test (BalLeaf x) (BalLeaf y) = do+    Refl <- test x y+    return Refl+  testEqualityFC test (BalPair x1 x2) (BalPair y1 y2) = do+    Refl <- testEqualityFC test x1 y1+    Refl <- testEqualityFC test x2 y2+    return Refl+#if !MIN_VERSION_base(4,9,0)+  testEqualityFC _ _ _ = Nothing+#endif++instance OrdFC (BalancedTree h) where+  compareFC test (BalLeaf x) (BalLeaf y) =+    joinOrderingF (test x y) $ EQF+#if !MIN_VERSION_base(4,9,0)+  compareFC _ BalLeaf{} _ = LTF+  compareFC _ _ BalLeaf{} = GTF+#endif+  compareFC test (BalPair x1 x2) (BalPair y1 y2) =+    joinOrderingF (compareFC test x1 y1) $+    joinOrderingF (compareFC test x2 y2) $+    EQF++instance HashableF f => HashableF (BalancedTree h f) where+  hashWithSaltF s t =+    case t of+      BalLeaf x -> s `hashWithSaltF` x+      BalPair x y -> s `hashWithSaltF` x `hashWithSaltF` y++fmap_bal :: (forall tp . f tp -> g tp)+         -> BalancedTree h f c+         -> BalancedTree h g c+fmap_bal = go+  where go :: (forall tp . f tp -> g tp)+              -> BalancedTree h f c+              -> BalancedTree h g c+        go f (BalLeaf x) = BalLeaf (f x)+        go f (BalPair x y) = BalPair (go f x) (go f y)+{-# INLINABLE fmap_bal #-}++traverse_bal :: Applicative m+             => (forall tp . f tp -> m (g tp))+             -> BalancedTree h f c+             -> m (BalancedTree h g c)+traverse_bal = go+  where go :: Applicative m+              => (forall tp . f tp -> m (g tp))+              -> BalancedTree h f c+              -> m (BalancedTree h g c)+        go f (BalLeaf x) = BalLeaf <$> f x+        go f (BalPair x y) = BalPair <$> go f x <*> go f y+{-# INLINABLE traverse_bal #-}++instance ShowF f => Show (BalancedTree h f tp) where+  show (BalLeaf x) = showF x+  show (BalPair x y) = "BalPair " Prelude.++ show x Prelude.++ " " Prelude.++ show y++instance ShowF f => ShowF (BalancedTree h f)++unsafe_bal_generate :: forall ctx h f t+                     . Int -- ^ Height of tree to generate+                    -> Int -- ^ Starting offset for entries.+                    -> (forall tp . Index ctx tp -> f tp)+                    -> BalancedTree h f t+unsafe_bal_generate h o f+  | h <  0 = error "unsafe_bal_generate given negative height"+  | h == 0 = unsafeCoerce $ BalLeaf (f (Index o))+  | otherwise =+    let l = unsafe_bal_generate (h-1) o f+        o' = o + 1 `shiftL` (h-1)+        u = assert (o + bal_size l == o') $ unsafe_bal_generate (h-1) o' f+     in unsafeCoerce $ BalPair l u++unsafe_bal_generateM :: forall m ctx h f t+                      . Applicative m+                     => Int -- ^ Height of tree to generate+                     -> Int -- ^ Starting offset for entries.+                     -> (forall x . Index ctx x -> m (f x))+                     -> m (BalancedTree h f t)+unsafe_bal_generateM h o f+  | h == 0 = unsafeCoerce . BalLeaf <$> f (Index o)+  | otherwise =+    let o' = o + 1 `shiftL` (h-1)+        g lv uv = assert (o' == o + bal_size lv) $+           unsafeCoerce (BalPair lv uv)+      in g <$> unsafe_bal_generateM (h-1) o  f+           <*> unsafe_bal_generateM (h-1) o' f++-- | Lookup index in tree.+unsafe_bal_index :: BalancedTree h f a -- ^ Tree to lookup.+                 -> Int -- ^ Index to lookup.+                 -> Int  -- ^ Height of tree+                 -> f tp+unsafe_bal_index _ j i+  | seq j $ seq i $ False = error "bad unsafe_bal_index"+unsafe_bal_index (BalLeaf u) _ i = assert (i == 0) $ unsafeCoerce u+unsafe_bal_index (BalPair x y) j i+  | j `testBit` (i-1) = unsafe_bal_index y j $! (i-1)+  | otherwise         = unsafe_bal_index x j $! (i-1)++-- | Update value at index in tree.+unsafe_bal_adjust :: Functor m+                  => (f x -> m (f y))+                  -> BalancedTree h f a -- ^ Tree to update+                  -> Int -- ^ Index to lookup.+                  -> Int  -- ^ Height of tree+                  -> m (BalancedTree h f b)+unsafe_bal_adjust f (BalLeaf u) _ i = assert (i == 0) $+  (unsafeCoerce . BalLeaf <$> (f (unsafeCoerce u)))+unsafe_bal_adjust f (BalPair x y) j i+  | j `testBit` (i-1) = (unsafeCoerce . BalPair x      <$> (unsafe_bal_adjust f y j (i-1)))+  | otherwise         = (unsafeCoerce . flip BalPair y <$> (unsafe_bal_adjust f x j (i-1)))++{-# SPECIALIZE unsafe_bal_adjust+     :: (f x -> Identity (f y))+     -> BalancedTree h f a+     -> Int+     -> Int+     -> Identity (BalancedTree h f b)+  #-}++-- | Zip two balanced trees together.+bal_zipWithM :: Applicative m+             => (forall x . f x -> g x -> m (h x))+             -> BalancedTree u f a+             -> BalancedTree u g a+             -> m (BalancedTree u h a)+bal_zipWithM f (BalLeaf x) (BalLeaf y) = BalLeaf <$> f x y+bal_zipWithM f (BalPair x1 x2) (BalPair y1 y2) =+  BalPair <$> bal_zipWithM f x1 (unsafeCoerce y1)+          <*> bal_zipWithM f x2 (unsafeCoerce y2)+#if !MIN_VERSION_base(4,9,0)+bal_zipWithM _ _ _ = error "ilegal args to bal_zipWithM"+#endif+{-# INLINABLE bal_zipWithM #-}++------------------------------------------------------------------------+-- BinomialTree++data BinomialTree (h::Height) (f :: k -> *) :: Ctx k -> * where+  Empty :: BinomialTree h f EmptyCtx++  -- Contains size of the subtree, subtree, then element.+  PlusOne  :: !Int+           -> !(BinomialTree ('Succ h) f x)+           -> !(BalancedTree h f y)+           -> BinomialTree h f (x <+> y)++  -- Contains size of the subtree, subtree, then element.+  PlusZero  :: !Int+            -> !(BinomialTree ('Succ h) f x)+            -> BinomialTree h f x++tsize :: BinomialTree h f a -> Int+tsize Empty = 0+tsize (PlusOne s _ _) = 2*s+1+tsize (PlusZero  s _) = 2*s++t_cnt_size :: BinomialTree h f a -> Int+t_cnt_size Empty = 0+t_cnt_size (PlusOne _ l r) = t_cnt_size l + bal_size r+t_cnt_size (PlusZero  _ l) = t_cnt_size l++-- | Concatenate a binomial tree and a balanced tree.+append :: BinomialTree h f x+       -> BalancedTree h f y+       -> BinomialTree h f (x <+> y)+append Empty y = PlusOne 0 Empty y+append (PlusOne _ t x) y =+  case assoc t x y of+    Refl ->+      let t' = append t (BalPair x y)+       in PlusZero (tsize t') t'+append (PlusZero s t) x = PlusOne s t x++instance TestEqualityFC (BinomialTree h) where+  testEqualityFC _ Empty Empty = return Refl+  testEqualityFC test (PlusZero _ x1) (PlusZero _ y1) = do+    Refl <- testEqualityFC test x1 y1+    return Refl+  testEqualityFC test (PlusOne _ x1 x2) (PlusOne _ y1 y2) = do+    Refl <- testEqualityFC test x1 y1+    Refl <- testEqualityFC test x2 y2+    return Refl+  testEqualityFC _ _ _ = Nothing++instance OrdFC (BinomialTree h) where+  compareFC _ Empty Empty = EQF+  compareFC _ Empty _ = LTF+  compareFC _ _ Empty = GTF++  compareFC test (PlusZero _ x1) (PlusZero _ y1) =+    joinOrderingF (compareFC test x1 y1) $ EQF+  compareFC _ PlusZero{} _ = LTF+  compareFC _ _ PlusZero{} = GTF++  compareFC test (PlusOne _ x1 x2) (PlusOne _ y1 y2) =+    joinOrderingF (compareFC test x1 y1) $+    joinOrderingF (compareFC test x2 y2) $+    EQF++instance HashableF f => HashableF (BinomialTree h f) where+  hashWithSaltF s t =+    case t of+      Empty -> s+      PlusZero _ x   -> s `hashWithSaltF` x+      PlusOne  _ x y -> s `hashWithSaltF` x `hashWithSaltF` y++-- | Map over a binary tree.+fmap_bin :: (forall tp . f tp -> g tp)+         -> BinomialTree h f c+         -> BinomialTree h g c+fmap_bin _ Empty = Empty+fmap_bin f (PlusOne s t x) = PlusOne s (fmap_bin f t) (fmap_bal f x)+fmap_bin f (PlusZero s t)  = PlusZero s (fmap_bin f t)+{-# INLINABLE fmap_bin #-}++traverse_bin :: Applicative m+             => (forall tp . f tp -> m (g tp))+             -> BinomialTree h f c+             -> m (BinomialTree h g c)+traverse_bin _ Empty = pure Empty+traverse_bin f (PlusOne s t x) = PlusOne s  <$> traverse_bin f t <*> traverse_bal f x+traverse_bin f (PlusZero s t)  = PlusZero s <$> traverse_bin f t+{-# INLINABLE traverse_bin #-}++unsafe_bin_generate :: forall h f ctx t+                     . Int -- ^ Size of tree to generate+                    -> Int -- ^ Height of each element.+                    -> (forall x . Index ctx x -> f x)+                    -> BinomialTree h f t+unsafe_bin_generate sz h f+  | sz == 0 = unsafeCoerce Empty+  | sz `testBit` 0 =+    let s = sz `shiftR` 1+        t = unsafe_bin_generate s (h+1) f+        o = s * 2^(h+1)+        u = assert (o == t_cnt_size t) $ unsafe_bal_generate h o f+     in unsafeCoerce (PlusOne s t u)+  | otherwise =+    let s = sz `shiftR` 1+        t = unsafe_bin_generate (sz `shiftR` 1) (h+1) f+        r :: BinomialTree h f t+        r = PlusZero s t+    in r++unsafe_bin_generateM :: forall m h f ctx t+                      . Applicative m+                     => Int -- ^ Size of tree to generate+                     -> Int -- ^ Height of each element.+                     -> (forall x . Index ctx x -> m (f x))+                     -> m (BinomialTree h f t)+unsafe_bin_generateM sz h f+  | sz == 0 = pure (unsafeCoerce Empty)+  | sz `testBit` 0 =+    let s = sz `shiftR` 1+        t = unsafe_bin_generateM s (h+1) f+        -- Next offset+        o = s * 2^(h+1)+        u = unsafe_bal_generateM h o f+        r = unsafeCoerce (PlusOne s) <$> t <*> u+     in r+  | otherwise =+    let s = sz `shiftR` 1+        t = unsafe_bin_generateM s (h+1) f+        r :: m (BinomialTree h f t)+        r = PlusZero s <$> t+     in r++------------------------------------------------------------------------+-- Dropping++type family InitCtx (x :: Ctx k) :: Ctx k+type instance InitCtx (x ::> y) = x++type family LastCtx (x :: Ctx k) :: k+type instance LastCtx (x ::> y) = y++--+data DropResult f (ctx :: Ctx k) where+  DropEmpty :: DropResult f EmptyCtx+  DropExt   :: BinomialTree 'Zero f (InitCtx ctx)+            -> f (LastCtx ctx)+            -> DropResult f ctx++-- | 'bal_drop x y' returns the tree formed 'append x (init y)'+bal_drop :: forall h f x y+          . BinomialTree h f x+            -- ^ Bina+         -> BalancedTree h f y+         -> DropResult f (x <+> y)+bal_drop t (BalLeaf e) = DropExt t e+bal_drop t (BalPair x y) =+  unsafeCoerce (bal_drop (PlusOne (tsize t) (unsafeCoerce t) x) y)++bin_drop :: forall h f ctx+          . BinomialTree h f ctx+         -> DropResult f ctx+bin_drop Empty = DropEmpty+bin_drop (PlusZero _ u) = bin_drop u+bin_drop (PlusOne s t u) =+  let m = case t of+            Empty -> Empty+            _ -> PlusZero s t+   in bal_drop m u++------------------------------------------------------------------------+-- Indexing++-- | Lookup value in tree.+unsafe_bin_index :: BinomialTree h f a -- ^ Tree to lookup in.+                 -> Int+                 -> Int -- ^ Size of tree+                 -> f u+unsafe_bin_index _ _ i+  | seq i False = error "bad unsafe_bin_index"+unsafe_bin_index Empty _ _ = error "unsafe_bin_index reached end of list"+unsafe_bin_index (PlusOne sz t u) j i+  | sz == j `shiftR` (1+i) = unsafe_bal_index u j i+  | otherwise = unsafe_bin_index t j $! (1+i)+unsafe_bin_index (PlusZero sz t) j i+  | sz == j `shiftR` (1+i) = error "unsafe_bin_index stopped at PlusZero"+  | otherwise = unsafe_bin_index t j $! (1+i)++-- | Lookup value in tree.+unsafe_bin_adjust :: forall m h f x y a b+                   . Functor m+                  => (f x -> m (f y))+                  -> BinomialTree h f a -- ^ Tree to lookup in.+                  -> Int+                  -> Int -- ^ Size of tree+                  -> m (BinomialTree h f b)+unsafe_bin_adjust _ Empty _ _ = error "unsafe_bin_adjust reached end of list"+unsafe_bin_adjust f (PlusOne sz t u) j i+  | sz == j `shiftR` (1+i) =+    unsafeCoerce . PlusOne sz t        <$> (unsafe_bal_adjust f u j i)+  | otherwise =+    unsafeCoerce . flip (PlusOne sz) u <$> (unsafe_bin_adjust f t j (i+1))+unsafe_bin_adjust f (PlusZero sz t) j i+  | sz == j `shiftR` (1+i) = error "unsafe_bin_adjust stopped at PlusZero"+  | otherwise = PlusZero sz <$> (unsafe_bin_adjust f t j (i+1))+++{-# SPECIALIZE unsafe_bin_adjust+     :: (f x -> Identity (f y))+     -> BinomialTree h f a+     -> Int+     -> Int+     -> Identity (BinomialTree h f b)+  #-}++tree_zipWithM :: Applicative m+             => (forall x . f x -> g x -> m (h x))+             -> BinomialTree u f a+             -> BinomialTree u g a+             -> m (BinomialTree u h a)+tree_zipWithM _ Empty Empty = pure Empty+tree_zipWithM f (PlusOne s x1 x2) (PlusOne _ y1 y2) =+  PlusOne s <$> tree_zipWithM f x1 (unsafeCoerce y1)+            <*> bal_zipWithM  f x2 (unsafeCoerce y2)+tree_zipWithM f (PlusZero s x1) (PlusZero _ y1) =+  PlusZero s <$> tree_zipWithM f x1 y1+tree_zipWithM _ _ _ = error "ilegal args to tree_zipWithM"+{-# INLINABLE tree_zipWithM #-}++------------------------------------------------------------------------+-- Assignment++type role Assignment representational nominal++-- | An assignment is a sequence that maps each index with type 'tp' to+-- a value of type 'f tp'.+newtype Assignment (f :: k -> *) (ctx :: Ctx k)+      = Assignment (BinomialTree 'Zero f ctx)++instance NFData (Assignment f ctx) where+  rnf a = seq a ()++-- | Return number of elements in assignment.+size :: Assignment f ctx -> Size ctx+size (Assignment t) = Size (tsize t)++-- | Generate an assignment with some context type that is not known.+generateSome :: forall f+              . Int+             -> (Int -> Some f)+             -> Some (Assignment f)+generateSome n f = go n+  where go :: Int -> Some (Assignment f)+        go 0 = Some empty+        go i = (\(Some a) (Some e) -> Some (a `extend` e)) (go (i-1)) (f (i-1))++-- | Generate an assignment with some context type that is not known.+generateSomeM :: forall m f+              .  Applicative m+              => Int+              -> (Int -> m (Some f))+              -> m (Some (Assignment f))+generateSomeM n f = go n+  where go :: Int -> m (Some (Assignment f))+        go 0 = pure (Some empty)+        go i = (\(Some a) (Some e) -> Some (a `extend` e)) <$> go (i-1) <*> f (i-1)++-- | @replicate n@ make a context with different copies of the same+-- polymorphic value.+replicate :: Size ctx -> (forall tp . f tp) -> Assignment f ctx+replicate n c = generate n (\_ -> c)++-- | Generate an assignment+generate :: Size ctx+         -> (forall tp . Index ctx tp -> f tp)+         -> Assignment f ctx+generate n f  = Assignment r+  where r = unsafe_bin_generate (sizeInt n) 0 f+{-# NOINLINE generate #-}++-- | Generate an assignment+generateM :: Applicative m+          => Size ctx+          -> (forall tp . Index ctx tp -> m (f tp))+          -> m (Assignment f ctx)+generateM n f = Assignment <$> unsafe_bin_generateM (sizeInt n) 0 f+{-# NOINLINE generateM #-}++-- | Return empty assignment+empty :: Assignment f EmptyCtx+empty = Assignment Empty++-- | Return true if assignment is empty.+null :: Assignment f ctx -> Bool+null (Assignment Empty) = True+null (Assignment _) = False++extend :: Assignment f ctx -> f x -> Assignment f (ctx ::> x)+extend (Assignment x) y = Assignment $ append x (BalLeaf y)++-- | Unexported index that returns an arbitrary type of expression.+unsafeIndex :: proxy u -> Int -> Assignment f ctx -> f u+unsafeIndex _ idx (Assignment t) = seq t $ unsafe_bin_index t idx 0++-- | Return value of assignment.+(!) :: Assignment f ctx -> Index ctx tp -> f tp+a ! Index i = assert (0 <= i && i < sizeInt (size a)) $+              unsafeIndex Proxy i a++-- | Return value of assignment, where the index is into an+--   initial sequence of the assignment.+(!^) :: KnownDiff l r => Assignment f r -> Index l tp -> f tp+a !^ i = a ! extendIndex i++instance TestEqualityFC Assignment where+   testEqualityFC test (Assignment x) (Assignment y) = do+     Refl <- testEqualityFC test x y+     return Refl++instance TestEquality f => TestEquality (Assignment f) where+  testEquality = testEqualityFC testEquality++instance TestEquality f => Eq (Assignment f ctx) where+  x == y = isJust (testEquality x y)++instance OrdFC Assignment where+  compareFC test (Assignment x) (Assignment y) =+     joinOrderingF (compareFC test x y) $ EQF++instance OrdF f => OrdF (Assignment f) where+  compareF = compareFC compareF++instance OrdF f => Ord (Assignment f ctx) where+  compare x y = toOrdering (compareF x y)++instance HashableF (Index ctx) where+  hashWithSaltF s i = hashWithSalt s (indexVal i)++instance Hashable (Index ctx tp) where+  hashWithSalt = hashWithSaltF++instance HashableF f => Hashable (Assignment f ctx) where+  hashWithSalt s (Assignment a) = hashWithSaltF s a++instance HashableF f => HashableF (Assignment f) where+  hashWithSaltF = hashWithSalt++instance ShowF f => Show (Assignment f ctx) where+  show a = "[" Prelude.++ intercalate ", " (toListFC showF a) Prelude.++ "]"++instance ShowF f => ShowF (Assignment f)++-- | Modify the value of an assignment at a particular index.+adjustM :: Functor m => (f tp -> m (f tp)) -> Index ctx tp -> Assignment f ctx -> m (Assignment f ctx)+adjustM f (Index i) (Assignment a) = Assignment <$> (unsafe_bin_adjust f a i 0)+{-# SPECIALIZE adjustM :: (f tp -> Identity (f tp)) -> Index ctx tp -> Assignment f ctx -> Identity (Assignment f ctx) #-}++type instance IndexF       (Assignment f ctx) = Index ctx+type instance IxValueF     (Assignment f ctx) = f++instance forall (f :: k -> *) ctx. IxedF k (Assignment f ctx) where+  ixF :: Index ctx x -> Lens.Lens' (Assignment f ctx) (f x)+  ixF idx f = adjustM f idx++instance forall (f :: k -> *) ctx. IxedF' k (Assignment f ctx) where+  ixF' :: Index ctx x -> Lens.Lens' (Assignment f ctx) (f x)+  ixF' idx f = adjustM f idx+++-- | Modify the value of an assignment at a particular index.+adjust :: (f tp -> f tp) -> Index ctx tp -> Assignment f ctx -> Assignment f ctx+adjust f idx asgn = runIdentity (adjustM (Identity . f) idx asgn)++-- | Update the assignment at a particular index.+update :: Index ctx tp -> f tp -> Assignment f ctx -> Assignment f ctx+update i v a = adjust (\_ -> v) i a++-- This is an unsafe version of update that changes the type of the expression.+unsafeUpdate :: Int -> Assignment f ctx -> f u -> Assignment f ctx'+unsafeUpdate i (Assignment a) e = Assignment (runIdentity (unsafe_bin_adjust (\_ -> Identity e) a i 0))++-- | View an assignment as either empty or an assignment with one appended.+data AssignView f ctx where+  AssignEmpty :: AssignView f EmptyCtx+  AssignExtend :: Assignment f ctx+               -> f tp+               -> AssignView f (ctx::>tp)++-- | View an assignment as either empty or an assignment with one appended.+view :: forall f ctx . Assignment f ctx -> AssignView f ctx+view (Assignment x) =+  case bin_drop x of+    DropEmpty -> AssignEmpty+    DropExt t v -> unsafeCoerce $ AssignExtend (Assignment (unsafeCoerce t)) v++-- | Return assignment with all but the last block.+init :: Assignment f (ctx '::> tp) -> Assignment f ctx+init (Assignment x) =+  case bin_drop x of+    DropExt t _ -> Assignment t++-- | Return the last element in the assignment.+last :: Assignment f (ctx '::> tp) -> f tp+last x =+  case view x of+    AssignExtend _ e -> e++decompose :: Assignment f (ctx ::> tp) -> (Assignment f ctx, f tp)+decompose x = case view x of AssignExtend a v -> (a,v)++zipWith :: (forall x . f x -> g x -> h x)+        -> Assignment f a+        -> Assignment g a+        -> Assignment h a+zipWith f = \x y -> runIdentity $ zipWithM (\u v -> pure (f u v)) x y+{-# INLINE zipWith #-}++zipWithM :: Applicative m+         => (forall x . f x -> g x -> m (h x))+         -> Assignment f a+         -> Assignment g a+         -> m (Assignment h a)+zipWithM f (Assignment x) (Assignment y) = Assignment <$> tree_zipWithM f x y+{-# INLINABLE zipWithM #-}++instance FunctorFC Assignment where+  fmapFC = \f (Assignment x) -> Assignment (fmap_bin f x)+  {-# INLINE fmapFC #-}++instance FoldableFC Assignment where+  foldMapFC = foldMapFCDefault+  {-# INLINE foldMapFC #-}++instance TraversableFC Assignment where+  traverseFC = \f (Assignment x) -> Assignment <$> traverse_bin f x+  {-# INLINE traverseFC #-}++traverseWithIndex :: Applicative m+                  => (forall tp . Index ctx tp -> f tp -> m (g tp))+                  -> Assignment f ctx+                  -> m (Assignment g ctx)+traverseWithIndex f a = generateM (size a) $ \i -> f i (a ! i)++-- | Create an assignment from a list of values.+fromList :: [Some f] -> Some (Assignment f)+fromList = go empty+  where go :: Assignment f ctx -> [Some f] -> Some (Assignment f)+        go prev [] = Some prev+        go prev (Some g:next) = (go $! prev `extend` g) next++------------------------------------------------------------------------+-- Appending++appendBal :: Assignment f x -> BalancedTree h f y -> Assignment f (x <+> y)+appendBal x (BalLeaf a) = x `extend` a+appendBal x (BalPair y z) =+  case assoc x y z of+    Refl -> x `appendBal` y `appendBal` z++appendBin :: Assignment f x -> BinomialTree h f y -> Assignment f (x <+> y)+appendBin x Empty = x+appendBin x (PlusOne _ y z) =+  case assoc x y z of+    Refl -> x `appendBin` y `appendBal` z+appendBin x (PlusZero _ y) = x `appendBin` y++(<++>) :: Assignment f x -> Assignment f y -> Assignment f (x <+> y)+x <++> Assignment y = x `appendBin` y++------------------------------------------------------------------------+-- KnownRepr instances++instance (KnownRepr (Assignment f) ctx, KnownRepr f bt)+      => KnownRepr (Assignment f) (ctx ::> bt) where+  knownRepr = knownRepr `extend` knownRepr++instance KnownRepr (Assignment f) EmptyCtx where+  knownRepr = empty++------------------------------------------------------------------------+-- Lens combinators++unsafeLens :: Int -> Lens.Lens (Assignment f ctx) (Assignment f ctx') (f tp) (f u)+unsafeLens idx =+  Lens.lens (unsafeIndex Proxy idx) (unsafeUpdate idx)++------------------------------------------------------------------------+-- 1 field lens combinators++type Assignment1 f x1 = Assignment f ('EmptyCtx '::> x1)++instance Lens.Field1 (Assignment1 f t) (Assignment1 f u) (f t) (f u) where+  _1 = unsafeLens 0++------------------------------------------------------------------------+-- 2 field lens combinators++type Assignment2 f x1 x2+   = Assignment f ('EmptyCtx '::> x1 '::> x2)++instance Lens.Field1 (Assignment2 f t x2) (Assignment2 f u x2) (f t) (f u) where+  _1 = unsafeLens 0++instance Lens.Field2 (Assignment2 f x1 t) (Assignment2 f x1 u) (f t) (f u) where+  _2 = unsafeLens 1++------------------------------------------------------------------------+-- 3 field lens combinators++type Assignment3 f x1 x2 x3+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3)++instance Lens.Field1 (Assignment3 f t x2 x3)+                     (Assignment3 f u x2 x3)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0+++instance Lens.Field2 (Assignment3 f x1 t x3)+                     (Assignment3 f x1 u x3)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment3 f x1 x2 t)+                     (Assignment3 f x1 x2 u)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++------------------------------------------------------------------------+-- 4 field lens combinators++type Assignment4 f x1 x2 x3 x4+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4)++instance Lens.Field1 (Assignment4 f t x2 x3 x4)+                     (Assignment4 f u x2 x3 x4)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0+++instance Lens.Field2 (Assignment4 f x1 t x3 x4)+                     (Assignment4 f x1 u x3 x4)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment4 f x1 x2 t x4)+                     (Assignment4 f x1 x2 u x4)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment4 f x1 x2 x3 t)+                     (Assignment4 f x1 x2 x3 u)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++------------------------------------------------------------------------+-- 5 field lens combinators++type Assignment5 f x1 x2 x3 x4 x5+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5)++instance Lens.Field1 (Assignment5 f t x2 x3 x4 x5)+                     (Assignment5 f u x2 x3 x4 x5)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0++instance Lens.Field2 (Assignment5 f x1 t x3 x4 x5)+                     (Assignment5 f x1 u x3 x4 x5)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment5 f x1 x2 t x4 x5)+                     (Assignment5 f x1 x2 u x4 x5)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment5 f x1 x2 x3 t x5)+                     (Assignment5 f x1 x2 x3 u x5)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++instance Lens.Field5 (Assignment5 f x1 x2 x3 x4 t)+                     (Assignment5 f x1 x2 x3 x4 u)+                     (f t)+                     (f u) where+  _5 = unsafeLens 4++------------------------------------------------------------------------+-- 6 field lens combinators++type Assignment6 f x1 x2 x3 x4 x5 x6+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6)++instance Lens.Field1 (Assignment6 f t x2 x3 x4 x5 x6)+                     (Assignment6 f u x2 x3 x4 x5 x6)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0+++instance Lens.Field2 (Assignment6 f x1 t x3 x4 x5 x6)+                     (Assignment6 f x1 u x3 x4 x5 x6)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment6 f x1 x2 t x4 x5 x6)+                     (Assignment6 f x1 x2 u x4 x5 x6)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment6 f x1 x2 x3 t x5 x6)+                     (Assignment6 f x1 x2 x3 u x5 x6)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++instance Lens.Field5 (Assignment6 f x1 x2 x3 x4 t x6)+                     (Assignment6 f x1 x2 x3 x4 u x6)+                     (f t)+                     (f u) where+  _5 = unsafeLens 4++instance Lens.Field6 (Assignment6 f x1 x2 x3 x4 x5 t)+                     (Assignment6 f x1 x2 x3 x4 x5 u)+                     (f t)+                     (f u) where+  _6 = unsafeLens 5++------------------------------------------------------------------------+-- 7 field lens combinators++type Assignment7 f x1 x2 x3 x4 x5 x6 x7+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7)++instance Lens.Field1 (Assignment7 f t x2 x3 x4 x5 x6 x7)+                     (Assignment7 f u x2 x3 x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0+++instance Lens.Field2 (Assignment7 f x1 t x3 x4 x5 x6 x7)+                     (Assignment7 f x1 u x3 x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment7 f x1 x2 t x4 x5 x6 x7)+                     (Assignment7 f x1 x2 u x4 x5 x6 x7)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment7 f x1 x2 x3 t x5 x6 x7)+                     (Assignment7 f x1 x2 x3 u x5 x6 x7)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++instance Lens.Field5 (Assignment7 f x1 x2 x3 x4 t x6 x7)+                     (Assignment7 f x1 x2 x3 x4 u x6 x7)+                     (f t)+                     (f u) where+  _5 = unsafeLens 4++instance Lens.Field6 (Assignment7 f x1 x2 x3 x4 x5 t x7)+                     (Assignment7 f x1 x2 x3 x4 x5 u x7)+                     (f t)+                     (f u) where+  _6 = unsafeLens 5++instance Lens.Field7 (Assignment7 f x1 x2 x3 x4 x5 x6 t)+                     (Assignment7 f x1 x2 x3 x4 x5 x6 u)+                     (f t)+                     (f u) where+  _7 = unsafeLens 6++------------------------------------------------------------------------+-- 8 field lens combinators++type Assignment8 f x1 x2 x3 x4 x5 x6 x7 x8+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7 '::> x8)++instance Lens.Field1 (Assignment8 f t x2 x3 x4 x5 x6 x7 x8)+                     (Assignment8 f u x2 x3 x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0+++instance Lens.Field2 (Assignment8 f x1 t x3 x4 x5 x6 x7 x8)+                     (Assignment8 f x1 u x3 x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment8 f x1 x2 t x4 x5 x6 x7 x8)+                     (Assignment8 f x1 x2 u x4 x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment8 f x1 x2 x3 t x5 x6 x7 x8)+                     (Assignment8 f x1 x2 x3 u x5 x6 x7 x8)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++instance Lens.Field5 (Assignment8 f x1 x2 x3 x4 t x6 x7 x8)+                     (Assignment8 f x1 x2 x3 x4 u x6 x7 x8)+                     (f t)+                     (f u) where+  _5 = unsafeLens 4++instance Lens.Field6 (Assignment8 f x1 x2 x3 x4 x5 t x7 x8)+                     (Assignment8 f x1 x2 x3 x4 x5 u x7 x8)+                     (f t)+                     (f u) where+  _6 = unsafeLens 5++instance Lens.Field7 (Assignment8 f x1 x2 x3 x4 x5 x6 t x8)+                     (Assignment8 f x1 x2 x3 x4 x5 x6 u x8)+                     (f t)+                     (f u) where+  _7 = unsafeLens 6++instance Lens.Field8 (Assignment8 f x1 x2 x3 x4 x5 x6 x7 t)+                     (Assignment8 f x1 x2 x3 x4 x5 x6 x7 u)+                     (f t)+                     (f u) where+  _8 = unsafeLens 7++------------------------------------------------------------------------+-- 9 field lens combinators++type Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 x9+   = Assignment f ('EmptyCtx '::> x1 '::> x2 '::> x3 '::> x4 '::> x5 '::> x6 '::> x7 '::> x8 '::> x9)+++instance Lens.Field1 (Assignment9 f t x2 x3 x4 x5 x6 x7 x8 x9)+                     (Assignment9 f u x2 x3 x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _1 = unsafeLens 0++instance Lens.Field2 (Assignment9 f x1 t x3 x4 x5 x6 x7 x8 x9)+                     (Assignment9 f x1 u x3 x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _2 = unsafeLens 1++instance Lens.Field3 (Assignment9 f x1 x2 t x4 x5 x6 x7 x8 x9)+                     (Assignment9 f x1 x2 u x4 x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _3 = unsafeLens 2++instance Lens.Field4 (Assignment9 f x1 x2 x3 t x5 x6 x7 x8 x9)+                     (Assignment9 f x1 x2 x3 u x5 x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _4 = unsafeLens 3++instance Lens.Field5 (Assignment9 f x1 x2 x3 x4 t x6 x7 x8 x9)+                     (Assignment9 f x1 x2 x3 x4 u x6 x7 x8 x9)+                     (f t)+                     (f u) where+  _5 = unsafeLens 4++instance Lens.Field6 (Assignment9 f x1 x2 x3 x4 x5 t x7 x8 x9)+                     (Assignment9 f x1 x2 x3 x4 x5 u x7 x8 x9)+                     (f t)+                     (f u) where+  _6 = unsafeLens 5++instance Lens.Field7 (Assignment9 f x1 x2 x3 x4 x5 x6 t x8 x9)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 u x8 x9)+                     (f t)+                     (f u) where+  _7 = unsafeLens 6++instance Lens.Field8 (Assignment9 f x1 x2 x3 x4 x5 x6 x7 t x9)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 x7 u x9)+                     (f t)+                     (f u) where+  _8 = unsafeLens 7++instance Lens.Field9 (Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 t)+                     (Assignment9 f x1 x2 x3 x4 x5 x6 x7 x8 u)+                     (f t)+                     (f u) where+  _9 = unsafeLens 8
+ src/Data/Parameterized/Ctx.hs view
@@ -0,0 +1,99 @@+{-|+Description      : Type-level lists.+Copyright        : (c) Galois, Inc 2015+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This module defines type-level lists used for representing the type of+variables in a context.+-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.Parameterized.Ctx+  ( type Ctx(..)+  , EmptyCtx+  , SingleCtx+  , (::>)+  , type (<+>)++    -- * Type context manipulation+  , CtxSize+  , CtxLookup+  , CtxUpdate+  , CtxLookupRight+  , CtxUpdateRight+  , CheckIx+  , ValidIx+  , FromLeft+  ) where++import Data.Kind (Constraint)+import GHC.TypeLits (Nat, type (+), type (-), type (<=?), TypeError, ErrorMessage(..))++------------------------------------------------------------------------+-- Ctx++type EmptyCtx = 'EmptyCtx+type (c :: Ctx k) ::> (a::k) = c '::> a++type SingleCtx x = EmptyCtx ::> x++-- | Kind @'Ctx' k@ comprises lists of types of kind @k@.+data Ctx k+  = EmptyCtx+  | Ctx k ::> k++-- | Append two type-level contexts.+type family (<+>) (x :: Ctx k) (y :: Ctx k) :: Ctx k where+  x <+> EmptyCtx = x+  x <+> (y ::> e) = (x <+> y) ::> e+++-- | This type family computes the number of elements in a 'Ctx'+type family CtxSize (a :: Ctx k) :: Nat where+  CtxSize 'EmptyCtx   = 0+  CtxSize (xs '::> x) = 1 + CtxSize xs++-- | Helper type family used to generate descriptive error messages when+-- an index is larger than the length of the 'Ctx' being indexed.+type family CheckIx (ctx :: Ctx k) (n :: Nat) (b :: Bool) :: Constraint where+  CheckIx ctx n 'True = ()+  CheckIx ctx n 'False = TypeError ('Text "Index "            ':<>: 'ShowType n+                              ':<>: 'Text " out of range in " ':<>: 'ShowType ctx)++-- | A constraint that checks that the nat @n@ is a valid index into the+--   context @ctx@, and raises a type error if not.+type ValidIx (n :: Nat) (ctx :: Ctx k)+  = CheckIx ctx n (n+1 <=? CtxSize ctx)++-- | 'Ctx' is a snoc-list. In order to use the more intuitive left-to-right+-- ordering of elements the desired index is subtracted from the total+-- number of elements.+type FromLeft ctx n = CtxSize ctx - 1 - n++-- | Lookup the value in a context by number, from the right+type family CtxLookupRight (n :: Nat) (ctx :: Ctx k) :: k where+  CtxLookupRight 0 (ctx '::> r) = r+  CtxLookupRight n (ctx '::> r) = CtxLookupRight (n-1) ctx++-- | Update the value in a context by number, from the right.  If the index+--   is out of range, the context is unchanged.+type family CtxUpdateRight (n :: Nat) (x::k) (ctx :: Ctx k) :: Ctx k where+  CtxUpdateRight n x 'EmptyCtx      = 'EmptyCtx+  CtxUpdateRight 0 x (ctx '::> old) = ctx '::> x+  CtxUpdateRight n x (ctx '::> y)   = CtxUpdateRight (n-1) x ctx '::> y++-- | Lookup the value in a context by number, from the left.+--   Produce a type error if the index is out of range.+type CtxLookup (n :: Nat) (ctx :: Ctx k)+  = CtxLookupRight (FromLeft ctx n) ctx++-- | Update the value in a context by number, from the left.  If the index+--   is out of range, the context is unchanged.+type CtxUpdate (n :: Nat) (x :: k) (ctx :: Ctx k)+  = CtxUpdateRight (FromLeft ctx n) x ctx
+ src/Data/Parameterized/Ctx/Proofs.hs view
@@ -0,0 +1,23 @@+{-|+Copyright        : (c) Galois, Inc 2015+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This reflects type level proofs involving contexts.+-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeOperators #-}+module Data.Parameterized.Ctx.Proofs+  ( leftId+  , assoc+  ) where++import Data.Type.Equality+import Unsafe.Coerce++import Data.Parameterized.Ctx++leftId :: p x -> (EmptyCtx <+> x) :~: x+leftId _ = unsafeCoerce Refl++assoc :: p x -> q y -> r z -> x <+> (y <+> z) :~: (x <+> y) <+> z+assoc _ _ _ = unsafeCoerce Refl
+ src/Data/Parameterized/HashTable.hs view
@@ -0,0 +1,97 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.HashTable+-- Copyright        : (c) Galois, Inc 2014+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module provides a ST-based hashtable for parameterized keys and values.+--+-- NOTE: This API makes use of unsafeCoerce to implement the parameterized+-- hashtable abstraction.  This should be typesafe provided the+-- 'TestEquality' instance on the key type is implemented soundly.+------------------------------------------------------------------------+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE Trustworthy #-}+module Data.Parameterized.HashTable+  ( HashTable+  , new+  , newSized+  , clone+  , lookup+  , insert+  , member+  , delete+  , clear+  , Data.Parameterized.Classes.HashableF(..)+  , Control.Monad.ST.RealWorld+  ) where++import Control.Applicative+import Control.Monad.ST+import qualified Data.HashTable.ST.Cuckoo as H+import GHC.Exts (Any)+import Unsafe.Coerce++import Prelude hiding (lookup)++import Data.Parameterized.Classes+import Data.Parameterized.Some++-- | A hash table mapping nonces to values.+newtype HashTable s (key :: k -> *) (val :: k -> *)+      = HashTable (H.HashTable s (Some key) Any)++-- | Create a new empty table.+new :: ST s (HashTable s key val)+new = HashTable <$> H.new++-- | Create a new empty table to hold 'n' elements.+newSized :: Int -> ST s (HashTable s k v)+newSized n = HashTable <$> H.newSized n++-- | Create a hash table that is a copy of the current one.+clone :: (HashableF key, TestEquality key)+      => HashTable s key val+      -> ST s (HashTable s key val)+clone (HashTable tbl) = do+  -- Create a new table+  r <- H.new+  -- Insert existing elements in+  H.mapM_ (uncurry (H.insert r)) tbl+  -- Return table+  return $! HashTable r++-- | Lookup value of key in table.+lookup :: (HashableF key, TestEquality key)+       => HashTable s key val+       -> key tp+       -> ST s (Maybe (val tp))+lookup (HashTable h) k = fmap unsafeCoerce <$> H.lookup h (Some k)+{-# INLINE lookup #-}++-- | Insert new key and value mapping into table.+insert :: (HashableF key, TestEquality key)+       => HashTable s (key :: k -> *) (val :: k -> *)+       -> key tp+       -> val tp+       -> ST s ()+insert (HashTable h) k v = H.insert h (Some k) (unsafeCoerce v)++-- | Return true if the key is in the hash table.+member :: (HashableF key, TestEquality key)+       => HashTable s (key :: k -> *) (val :: k -> *)+       -> key (tp :: k)+       -> ST s Bool+member (HashTable h) k = isJust <$> H.lookup h (Some k)++-- | Delete an element from the hash table.+delete :: (HashableF key, TestEquality key)+       => HashTable s (key :: k -> *) (val :: k -> *)+       -> key (tp :: k)+       -> ST s ()+delete (HashTable h) k = H.delete h (Some k)++clear :: (HashableF key, TestEquality key)+      => HashTable s (key :: k -> *) (val :: k -> *) -> ST s ()+clear (HashTable h) = H.mapM_ (\(k,_) -> H.delete h k) h
+ src/Data/Parameterized/List.hs view
@@ -0,0 +1,220 @@+{-|+Copyright        : (c) Galois, Inc 2017+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This module defines a list over two parameters.  The first+is a fixed type-level function @k -> *@ for some kind @k@, and the+second is a list of types with kind k that provide the indices for+the values in the list.++This type is closely related to the @Context@ type in+@Data.Parameterized.Context@.+-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeOperators #-}+module Data.Parameterized.List+  ( List(..)+  , Index(..)+  , indexValue+  , (!!)+  , update+  , indexed+  , imap+  , ifoldr+  , itraverse+    -- * Constants+  , index0+  , index1+  , index2+  , index3+  ) where++import qualified Control.Lens as Lens+import Prelude hiding ((!!))++import Data.Parameterized.Classes+import Data.Parameterized.TraversableFC++-- | Parameterized list of elements.+data List :: (k -> *) -> [k] -> * where+  Nil  :: List f '[]+  (:<) :: f tp -> List f tps -> List f (tp : tps)++infixr 5 :<++instance ShowF f => Show (List f sh) where+  show Nil = "Nil"+  show (elt :< rest) = showF elt ++ " :< " ++ show rest++instance ShowF f => ShowF (List f)++instance FunctorFC List where+  fmapFC _ Nil = Nil+  fmapFC f (x :< xs) = f x :< fmapFC f xs++instance FoldableFC List where+  foldrFC _ z Nil = z+  foldrFC f z (x :< xs) = f x (foldrFC f z xs)++instance TraversableFC List where+  traverseFC _ Nil = pure Nil+  traverseFC f (h :< r) = (:<) <$> f h <*> traverseFC f r++instance TestEquality f => TestEquality (List f) where+  testEquality Nil Nil = Just Refl+  testEquality (xh :< xl) (yh :< yl) = do+    Refl <- testEquality xh yh+    Refl <- testEquality xl yl+    pure Refl+  testEquality _ _ = Nothing++instance OrdF f => OrdF (List f) where+  compareF Nil Nil = EQF+  compareF Nil _ = LTF+  compareF _ Nil = GTF+  compareF (xh :< xl) (yh :< yl) =+    lexCompareF xh yh $+    lexCompareF xl yl $+    EQF+++instance KnownRepr (List f) '[] where+  knownRepr = Nil++instance (KnownRepr f s, KnownRepr (List f) sh) => KnownRepr (List f) (s ': sh) where+  knownRepr = knownRepr :< knownRepr++--------------------------------------------------------------------------------+-- Indexed operations+++-- | Represents an index into a type-level list. Used in place of integers to+--   1. ensure that the given index *does* exist in the list+--   2. guarantee that it has the given kind+data Index :: [k] -> k -> *  where+  IndexHere :: Index (x:r) x+  IndexThere :: !(Index r y) -> Index (x:r) y++deriving instance Eq (Index l x)+deriving instance Show  (Index l x)++instance ShowF (Index l)++instance TestEquality (Index l) where+  testEquality IndexHere IndexHere = Just Refl+  testEquality (IndexThere x) (IndexThere y) = testEquality x y+  testEquality _ _ = Nothing++instance OrdF (Index l) where+  compareF IndexHere IndexHere = EQF+  compareF IndexHere IndexThere{} = LTF+  compareF IndexThere{} IndexHere = GTF+  compareF (IndexThere x) (IndexThere y) = compareF x y++instance Ord (Index sh x) where+  x `compare` y = toOrdering $ x `compareF` y++-- | Return the index as an integer.+indexValue :: Index l tp -> Integer+indexValue = go 0+  where go :: Integer -> Index l tp -> Integer+        go i IndexHere = i+        go i (IndexThere x) = seq j $ go j x+          where j = i+1++-- | Index 0+index0 :: Index (x:r) x+index0 = IndexHere++-- | Index 1+index1 :: Index (x0:x1:r) x1+index1 = IndexThere index0++-- | Index 2+index2 :: Index (x0:x1:x2:r) x2+index2 = IndexThere index1++-- | Index 3+index3 :: Index (x0:x1:x2:x3:r) x3+index3 = IndexThere index2++-- | Return the value in a list at a given index+(!!) :: List f l -> Index l x -> f x+l !! (IndexThere i) =+  case l of+    _ :< r -> r !! i+l !! IndexHere =+  case l of+    (h :< _) -> h++-- | Update the 'List' at an index+update :: List f l -> Index l s -> (f s -> f s) -> List f l+update vals IndexHere upd =+  case vals of+    x :< rest -> upd x :< rest+update vals (IndexThere th) upd =+  case vals of+    x :< rest -> x :< update rest th upd++-- | Provides a lens for manipulating the element at the given index.+indexed :: Index l x -> Lens.Simple Lens.Lens (List f l) (f x)+indexed IndexHere      f (x :< rest) = (:< rest) <$> f x+indexed (IndexThere i) f (x :< rest) = (x :<) <$> indexed i f rest++--------------------------------------------------------------------------------+-- Indexed operations++-- | Map over the elements in the list, and provide the index into+-- each element along with the element itself.+imap :: forall f g l+     . (forall x . Index l x -> f x -> g x)+     -> List f l+     -> List g l+imap f = go id+  where+    go :: forall l'+        . (forall tp . Index l' tp -> Index l tp)+       -> List f l'+       -> List g l'+    go g l =+      case l of+        Nil -> Nil+        e :< rest -> f (g IndexHere) e :< go (g . IndexThere) rest++-- | Right-fold with an additional index.+ifoldr :: forall sh a b . (forall tp . Index sh tp -> a tp -> b -> b) -> b -> List a sh -> b+ifoldr f seed0 l = go id l seed0+  where+    go :: forall tps+        . (forall tp . Index tps tp -> Index sh tp)+       -> List a tps+       -> b+       -> b+    go g ops b =+      case ops of+        Nil -> b+        a :< rest -> f (g IndexHere) a (go (\ix -> g (IndexThere ix)) rest b)++-- | Traverse with an additional index.+itraverse :: forall a b sh t+          . Applicative t+          => (forall tp . Index sh tp -> a tp -> t (b tp))+          -> List a sh+          -> t (List b sh)+itraverse f = go id+  where+    go :: forall tps . (forall tp . Index tps tp -> Index sh tp)+       -> List a tps+       -> t (List b tps)+    go g l =+      case l of+        Nil -> pure Nil+        e :< rest -> (:<) <$> f (g IndexHere) e <*> go (\ix -> g (IndexThere ix)) rest
+ src/Data/Parameterized/Map.hs view
@@ -0,0 +1,546 @@+{-|+Copyright        : (c) Galois, Inc 2014-2017++This module defines finite maps where the key and value types are+parameterized by an arbitrary kind.++Some code was adapted from containers.+-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+module Data.Parameterized.Map+  ( MapF+    -- * Construction+  , Data.Parameterized.Map.empty+  , singleton+  , insert+  , insertWith+  , delete+  , union+    -- * Query+  , null+  , lookup+  , member+  , notMember+  , size+    -- * Conversion+  , keys+  , elems+  , fromList+  , toList+  , fromKeys+  , fromKeysM+   -- * Filter+  , filterGt+  , filterLt+    -- * Folds+  , foldrWithKey+    -- * Traversal+  , map+  , mapMaybe+  , traverseWithKey+  , traverseWithKey_+    -- * Complex interface.+  , UpdateRequest(..)+  , Updated(..)+  , updatedValue+  , updateAtKey+  , mergeWithKeyM+  , module Data.Parameterized.Classes+    -- * Pair+  , Pair(..)+  ) where++import Control.Applicative hiding (empty)+import Control.Lens (Traversal', Lens')+import Control.Monad.Identity+import Data.List (intercalate, foldl')+import Data.Maybe ()++import Data.Parameterized.Classes+import Data.Parameterized.Some+import Data.Parameterized.Pair ( Pair(..) )+import Data.Parameterized.TraversableF+import Data.Parameterized.Utils.BinTree+  ( MaybeS(..)+  , fromMaybeS+  , Updated(..)+  , updatedValue+  , TreeApp(..)+  , bin+  , IsBinTree(..)+  , balanceL+  , balanceR+  , glue+  )+import qualified Data.Parameterized.Utils.BinTree as Bin++#if MIN_VERSION_base(4,8,0)+import Prelude hiding (lookup, map, traverse, null)+#else+import Prelude hiding (lookup, map, null)+#endif++------------------------------------------------------------------------+-- Pair++comparePairKeys :: OrdF k => Pair k a -> Pair k a -> Ordering+comparePairKeys (Pair x _) (Pair y _) = toOrdering (compareF x y)+{-# INLINABLE comparePairKeys #-}++------------------------------------------------------------------------+-- MapF++-- | A map from parameterized keys to values with the same paramter type.+data MapF (k :: v -> *) (a :: v -> *) where+  Bin :: {-# UNPACK #-}+         !Size -- Number of elements in tree.+      -> !(k x)+      -> !(a x)+      -> !(MapF k a)+      -> !(MapF k a)+      -> MapF k a+  Tip :: MapF k a++type Size = Int++-- | Return empty map+empty :: MapF k a+empty = Tip++-- | Return true if map is empty+null :: MapF k a -> Bool+null Tip = True+null Bin{} = False++-- | Return map containing a single element+singleton :: k tp -> a tp -> MapF k a+singleton k x = Bin 1 k x Tip Tip++instance Bin.IsBinTree (MapF k a) (Pair k a) where+  asBin (Bin _ k v l r) = BinTree (Pair k v) l r+  asBin Tip = TipTree++  tip = Tip+  bin (Pair k v) l r = Bin (size l + size r + 1) k v l r++  size Tip              = 0+  size (Bin sz _ _ _ _) = sz++instance (TestEquality k, EqF a) => Eq (MapF k a) where+  x == y = size x == size y && toList x == toList y++------------------------------------------------------------------------+-- Traversals++#ifdef __GLASGOW_HASKELL__+{-# NOINLINE [1] map #-}+{-# NOINLINE [1] traverse #-}+{-# RULES+"map/map" forall (f :: (forall tp . f tp -> g tp)) (g :: (forall tp . g tp -> h tp)) xs+               . map g (map f xs) = map (g . f) xs+"map/traverse" forall (f :: (forall tp . f tp -> m (g tp))) (g :: (forall tp . g tp -> h tp)) xs+               . fmap (map g) (traverse f xs) = traverse (\v -> g <$> f v) xs+"traverse/map"+  forall (f :: (forall tp . f tp -> g tp)) (g :: (forall tp . g tp -> m (h tp))) xs+       . traverse g (map f xs) = traverse (\v -> g (f v)) xs+"traverse/traverse"+  forall (f :: (forall tp . f tp -> m (g tp))) (g :: (forall tp . g tp -> m (h tp))) xs+       . traverse f xs >>= traverse g = traverse (\v -> f v >>= g) xs+ #-}+#endif++-- | Modify elements in a map+map :: (forall tp . f tp -> g tp) -> MapF ktp f -> MapF ktp g+map _ Tip = Tip+map f (Bin sx kx x l r) = Bin sx kx (f x) (map f l) (map f r)++-- | Run partial map over elements.+mapMaybe :: (forall tp . f tp -> Maybe (g tp)) -> MapF ktp f -> MapF ktp g+mapMaybe _ Tip = Tip+mapMaybe f (Bin _ k x l r) =+  case f x of+    Just y -> Bin.link (Pair k y) (mapMaybe f l) (mapMaybe f r)+    Nothing -> Bin.merge (mapMaybe f l) (mapMaybe f r)++-- | Traverse elements in a map+traverse :: Applicative m => (forall tp . f tp -> m (g tp)) -> MapF ktp f -> m (MapF ktp g)+traverse _ Tip = pure Tip+traverse f (Bin sx kx x l r) = Bin sx kx <$> f x <*> traverse f l <*> traverse f r++-- | Traverse elements in a map+traverseWithKey+  :: Applicative m+  => (forall tp . ktp tp -> f tp -> m (g tp))+  -> MapF ktp f+  -> m (MapF ktp g)+traverseWithKey _ Tip = pure Tip+traverseWithKey f (Bin sx kx x l r) =+   Bin sx kx <$> f kx x <*> traverseWithKey f l <*> traverseWithKey f r++-- | Traverse elements in a map without returning result.+traverseWithKey_+  :: Applicative m+  => (forall tp . ktp tp -> f tp -> m ())+  -> MapF ktp f+  -> m ()+traverseWithKey_ _ Tip = pure ()+traverseWithKey_ f (Bin _ kx x l r) = f kx x *> traverseWithKey_ f l *> traverseWithKey_ f r+++type instance IndexF   (MapF k v) = k+type instance IxValueF (MapF k v) = v++-- | Turn a map key into a traversal that visits the indicated element in the map, if it exists.+instance forall (k:: a -> *) v. OrdF k => IxedF a (MapF k v) where+  ixF :: k x -> Traversal' (MapF k v) (v x)+  ixF i f m = updatedValue <$> updateAtKey i (pure Nothing) (\x -> Set <$> f x) m++-- | Turn a map key into a lens that points into the indicated position in the map.+instance forall (k:: a -> *) v. OrdF k => AtF a (MapF k v) where+  atF :: k x -> Lens' (MapF k v) (Maybe (v x))+  atF i f m = updatedValue <$> updateAtKey i (f Nothing) (\x -> maybe Delete Set <$> f (Just x)) m+++-- | Lookup value in map.+lookup :: OrdF k => k tp -> MapF k a -> Maybe (a tp)+lookup k0 = seq k0 (go k0)+  where+    go :: OrdF k => k tp -> MapF k a -> Maybe (a tp)+    go _ Tip = Nothing+    go k (Bin _ kx x l r) =+      case compareF k kx of+        LTF -> go k l+        GTF -> go k r+        EQF -> Just x+{-# INLINABLE lookup #-}++-- | Return true if key is bound in map.+member :: OrdF k => k tp -> MapF k a -> Bool+member k0 = seq k0 (go k0)+  where+    go :: OrdF k => k tp -> MapF k a -> Bool+    go _ Tip = False+    go k (Bin _ kx _ l r) =+      case compareF k kx of+        LTF -> go k l+        GTF -> go k r+        EQF -> True+{-# INLINABLE member #-}++-- | Return true if key is not bound in map.+notMember :: OrdF k => k tp -> MapF k a -> Bool+notMember k m = not $ member k m+{-# INLINABLE notMember #-}++instance FunctorF (MapF ktp) where+  fmapF = map++instance FoldableF (MapF ktp) where+  foldrF f z = go z+    where go z' Tip             = z'+          go z' (Bin _ _ x l r) = go (f x (go z' r)) l++instance TraversableF (MapF ktp) where+  traverseF = traverse++instance (ShowF ktp, ShowF rtp) => Show (MapF ktp rtp) where+  show m = showMap showF showF m++-- | Return all keys of the map in ascending order.+keys :: MapF k a -> [Some k]+keys = foldrWithKey (\k _ l -> Some k : l) []++-- | Return all elements of the map in the ascending order of their keys.+elems :: MapF k a -> [Some a]+elems = foldrF (\e l -> Some e : l) []++-- | Perform a fold with the key also provided.+foldrWithKey :: (forall s . k s -> a s -> b -> b) -> b -> MapF k a -> b+foldrWithKey f z = go z+  where+    go z' Tip = z'+    go z' (Bin _ kx x l r) = go (f kx x (go z' r)) l++showMap :: (forall tp . ktp tp -> String)+        -> (forall tp . rtp tp -> String)+        -> MapF ktp rtp+        -> String+showMap ppk ppv m = "{ " ++ intercalate ", " l ++ " }"+  where l = foldrWithKey (\k a l0 -> (ppk k ++ " -> " ++ ppv a) : l0) [] m++------------------------------------------------------------------------+-- filter++compareKeyPair :: OrdF k => k tp -> Pair k a -> Ordering+compareKeyPair k = \(Pair x _) -> toOrdering (compareF k x)++-- | @filterGt k m@ returns submap of @m@ that only contains entries+-- that are larger than @k@.+filterGt :: OrdF k => k tp -> MapF k v -> MapF k v+filterGt k m = fromMaybeS m (Bin.filterGt (compareKeyPair k) m)+{-# INLINABLE filterGt #-}++-- | @filterLt k m@ returns submap of @m@ that only contains entries+-- that are smaller than @k@.+filterLt :: OrdF k => k tp -> MapF k v -> MapF k v+filterLt k m = fromMaybeS m (Bin.filterLt (compareKeyPair k) m)+{-# INLINABLE filterLt #-}++------------------------------------------------------------------------+-- User operations++-- | Insert a binding into the map, replacing the existing binding if needed.+insert :: OrdF k => k tp -> a tp -> MapF k a -> MapF k a+insert = \k v m -> seq k $ updatedValue (Bin.insert comparePairKeys (Pair k v) m)+{-# INLINABLE insert #-}+-- {-# SPECIALIZE Bin.insert :: OrdF k => Pair k a -> MapF k a -> Updated (MapF k a) #-}++-- | Insert a binding into the map, replacing the existing binding if needed.+insertWithImpl :: OrdF k => (a tp -> a tp -> a tp) -> k tp -> a tp -> MapF k a -> Updated (MapF k a)+insertWithImpl f k v t = seq k $+  case t of+    Tip -> Bin.Updated (Bin 1 k v Tip Tip)+    Bin sz yk yv l r ->+      case compareF k yk of+        LTF ->+          case insertWithImpl f k v l of+            Bin.Updated l'   -> Bin.Updated   (Bin.balanceL (Pair yk yv) l' r)+            Bin.Unchanged l' -> Bin.Unchanged (Bin sz yk yv l' r)+        GTF ->+          case insertWithImpl f k v r of+            Bin.Updated r'   -> Bin.Updated   (Bin.balanceR (Pair yk yv) l r')+            Bin.Unchanged r' -> Bin.Unchanged (Bin sz yk yv l r')+        EQF -> Bin.Unchanged (Bin sz yk (f v yv) l r)+{-# INLINABLE insertWithImpl #-}++-- | @insertWith f new m@ inserts the binding into @m@.+--+-- It inserts @f new old@ if @m@ already contains an equivaltn value+-- @old@, and @new@ otherwise.  It returns an Unchanged value if the+-- map stays the same size and an updated value if a new entry was+-- inserted.+insertWith :: OrdF k => (a tp -> a tp -> a tp) -> k tp -> a tp -> MapF k a -> MapF k a+insertWith = \f k v t -> seq k $ updatedValue (insertWithImpl f k v t)+{-# INLINABLE insertWith #-}++-- | Delete a value from the map if present.+delete :: OrdF k => k tp -> MapF k a -> MapF k a+delete = \k m -> seq k $ fromMaybeS m $ Bin.delete (p k) m+  where p :: OrdF k => k tp -> Pair k a -> Ordering+        p k (Pair kx _) = toOrdering (compareF k kx)+{-# INLINABLE delete #-}+{-# SPECIALIZE Bin.delete :: (Pair k a -> Ordering) -> MapF k a -> MaybeS (MapF k a) #-}++-- | Union two sets+union :: OrdF k => MapF k a -> MapF k a -> MapF k a+union t1 t2 = Bin.union comparePairKeys t1 t2+{-# INLINABLE union #-}+-- {-# SPECIALIZE Bin.union compare :: OrdF k => MapF k a -> MapF k a -> MapF k a #-}++------------------------------------------------------------------------+-- updateAtKey++-- | Update request tells when to do with value+data UpdateRequest v+   = -- | Keep the current value.+     Keep+     -- | Set the value to a new value.+   | Set !v+     -- | Delete a value.+   | Delete++data AtKeyResult k a where+  AtKeyUnchanged :: AtKeyResult k a+  AtKeyInserted :: MapF k a -> AtKeyResult k a+  AtKeyModified :: MapF k a -> AtKeyResult k a+  AtKeyDeleted  :: MapF k a -> AtKeyResult k a++atKey' :: (OrdF k, Functor f)+       => k tp+       -> f (Maybe (a tp)) -- ^ Function to call if no element is found.+       -> (a tp -> f (UpdateRequest (a tp)))+       -> MapF k a+       -> f (AtKeyResult k a)+atKey' k onNotFound onFound t =+  case asBin t of+    TipTree -> ins <$> onNotFound+      where ins Nothing  = AtKeyUnchanged+            ins (Just v) = AtKeyInserted (singleton k v)+    BinTree yp@(Pair kx y) l r ->+      case compareF k kx of+        LTF -> ins <$> atKey' k onNotFound onFound l+          where ins AtKeyUnchanged = AtKeyUnchanged+                ins (AtKeyInserted l') = AtKeyInserted (balanceL yp l' r)+                ins (AtKeyModified l') = AtKeyModified (bin      yp l' r)+                ins (AtKeyDeleted  l') = AtKeyDeleted  (balanceR yp l' r)+        GTF -> ins <$> atKey' k onNotFound onFound r+          where ins AtKeyUnchanged = AtKeyUnchanged+                ins (AtKeyInserted r') = AtKeyInserted (balanceR yp l r')+                ins (AtKeyModified r') = AtKeyModified (bin      yp l r')+                ins (AtKeyDeleted  r') = AtKeyDeleted  (balanceL yp l r')+        EQF -> ins <$> onFound y+          where ins Keep    = AtKeyUnchanged+                ins (Set x) = AtKeyModified (bin (Pair kx x) l r)+                ins Delete  = AtKeyDeleted (glue l r)+{-# INLINABLE atKey' #-}++-- | Log-time algorithm that allows a value at a specific key to be added, replaced,+-- or deleted.+updateAtKey :: (OrdF k, Functor f)+            => k tp -- ^ Key to update+            -> f (Maybe (a tp))+               -- ^ Action to call if nothing is found+            -> (a tp -> f (UpdateRequest (a tp)))+               -- ^ Action to call if value is found.+            -> MapF k a+               -- ^ Map to update+            -> f (Updated (MapF k a))+updateAtKey k onNotFound onFound t = ins <$> atKey' k onNotFound onFound t+  where ins AtKeyUnchanged = Unchanged t+        ins (AtKeyInserted t') = Updated t'+        ins (AtKeyModified t') = Updated t'+        ins (AtKeyDeleted  t') = Updated t'+{-# INLINABLE updateAtKey #-}++-- | Create a Map from a list of pairs.+fromList :: OrdF k => [Pair k a] -> MapF k a+fromList = foldl' (\m (Pair k a) -> insert k a m) Data.Parameterized.Map.empty++toList :: MapF k a -> [Pair k a]+toList = foldrWithKey (\k x m -> Pair k x : m) []++-- | Generate a map from a foldable collection of keys and a+-- function from keys to values.+fromKeys :: forall m (t :: * -> *) (a :: k -> *) (v :: k -> *)+          .  (Monad m, Foldable t, OrdF a)+            => (forall tp . a tp -> m (v tp))+            -- ^ Function for evaluating a register value.+            -> t (Some a)+               -- ^ Set of X86 registers+            -> m (MapF a v)+fromKeys f = foldM go empty+  where go :: MapF a v -> Some a -> m (MapF a v)+        go m (Some k) = (\v -> insert k v m) <$> f k++-- | Generate a map from a foldable collection of keys and a monadic+-- function from keys to values.+fromKeysM :: forall m (t :: * -> *) (a :: k -> *) (v :: k -> *)+          .  (Monad m, Foldable t, OrdF a)+           => (forall tp . a tp -> m (v tp))+           -- ^ Function for evaluating a register value.+           -> t (Some a)+           -- ^ Set of X86 registers+           -> m (MapF a v)+fromKeysM f = foldM go empty+  where go :: MapF a v -> Some a -> m (MapF a v)+        go m (Some k) = (\v -> insert k v m) <$> f k++filterGtMaybe :: OrdF k => MaybeS (k x) -> MapF k a -> MapF k a+filterGtMaybe NothingS m = m+filterGtMaybe (JustS k) m = filterGt k m++filterLtMaybe :: OrdF k => MaybeS (k x) -> MapF k a -> MapF k a+filterLtMaybe NothingS m = m+filterLtMaybe (JustS k) m = filterLt k m++-- | Merge bindings in two maps to get a third.+mergeWithKeyM :: forall k a b c m+               . (Applicative m, OrdF k)+              => (forall tp . k tp -> a tp -> b tp -> m (Maybe (c tp)))+              -> (MapF k a -> m (MapF k c))+              -> (MapF k b -> m (MapF k c))+              -> MapF k a+              -> MapF k b+              -> m (MapF k c)+mergeWithKeyM f g1 g2 = go+  where+    go Tip t2 = g2 t2+    go t1 Tip = g1 t1+    go t1 t2 = hedgeMerge NothingS NothingS t1 t2++    hedgeMerge :: MaybeS (k x) -> MaybeS (k y) -> MapF k a -> MapF k b -> m (MapF k c)+    hedgeMerge _   _   t1  Tip = g1 t1+    hedgeMerge blo bhi Tip (Bin _ kx x l r) =+      g2 $ Bin.link (Pair kx x) (filterGtMaybe blo l) (filterLtMaybe bhi r)+    hedgeMerge blo bhi (Bin _ kx x l r) t2 =+        let Bin.PairS found trim_t2 = trimLookupLo kx bhi t2+            resolve_g1 :: MapF k c -> MapF k c -> MapF k c -> MapF k c+            resolve_g1 Tip = Bin.merge+            resolve_g1 (Bin _ k' x' Tip Tip) = Bin.link (Pair k' x')+            resolve_g1 _ = error "mergeWithKey: Bad function g1"+            resolve_f Nothing = Bin.merge+            resolve_f (Just x') = Bin.link (Pair kx x')+         in case found of+              Nothing ->+                resolve_g1 <$> g1 (singleton kx x)+                           <*> hedgeMerge blo bmi l (trim blo bmi t2)+                           <*> hedgeMerge bmi bhi r trim_t2+              Just x2 ->+                resolve_f <$> f kx x x2+                          <*> hedgeMerge blo bmi l (trim blo bmi t2)+                          <*> hedgeMerge bmi bhi r trim_t2+      where bmi = JustS kx+{-# INLINABLE mergeWithKeyM #-}++{--------------------------------------------------------------------+  [trim blo bhi t] trims away all subtrees that surely contain no+  values between the range [blo] to [bhi]. The returned tree is either+  empty or the key of the root is between @blo@ and @bhi@.+--------------------------------------------------------------------}+trim :: OrdF k => MaybeS (k x) -> MaybeS (k y) -> MapF k a -> MapF k a+trim NothingS   NothingS   t = t+trim (JustS lk) NothingS   t = filterGt lk t+trim NothingS   (JustS hk) t = filterLt hk t+trim (JustS lk) (JustS hk) t = filterMiddle lk hk t++-- | Returns only entries that are strictly between the two keys.+filterMiddle :: OrdF k => k x -> k y -> MapF k a -> MapF k a+filterMiddle lo hi (Bin _ k _ _ r)+  | k `leqF` lo = filterMiddle lo hi r+filterMiddle lo hi (Bin _ k _ l _)+  | k `geqF` hi = filterMiddle lo hi l+filterMiddle _  _  t = t+{-# INLINABLE filterMiddle #-}++++-- Helper function for 'mergeWithKey'. The @'trimLookupLo' lk hk t@ performs both+-- @'trim' (JustS lk) hk t@ and @'lookup' lk t@.++-- See Note: Type of local 'go' function+trimLookupLo :: OrdF k => k tp -> MaybeS (k y) -> MapF k a -> Bin.PairS (Maybe (a tp)) (MapF k a)+trimLookupLo lk NothingS t = greater lk t+  where greater :: OrdF k => k tp -> MapF k a -> Bin.PairS (Maybe (a tp)) (MapF k a)+        greater lo t'@(Bin _ kx x l r) =+           case compareF lo kx of+             LTF -> Bin.PairS (lookup lo l) t'+             EQF -> Bin.PairS (Just x) r+             GTF -> greater lo r+        greater _ Tip = Bin.PairS Nothing Tip+trimLookupLo lk (JustS hk) t = middle lk hk t+  where middle :: OrdF k => k tp -> k y -> MapF k a -> Bin.PairS (Maybe (a tp)) (MapF k a)+        middle lo hi t'@(Bin _ kx x l r) =+          case compareF lo kx of+            LTF | kx `ltF` hi -> Bin.PairS (lookup lo l) t'+                | otherwise -> middle lo hi l+            EQF -> Bin.PairS (Just x) (lesser hi r)+            GTF -> middle lo hi r+        middle _ _ Tip = Bin.PairS Nothing Tip++        lesser :: OrdF k => k y -> MapF k a -> MapF k a+        lesser hi (Bin _ k _ l _) | k `geqF` hi = lesser hi l+        lesser _ t' = t'
+ src/Data/Parameterized/NatRepr.hs view
@@ -0,0 +1,479 @@+{-|+Copyright        : (c) Galois, Inc 2014-2015+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This defines a type 'NatRepr' for representing a type-level natural+at runtime.  This can be used to branch on a type-level value.  For+each @n@, @NatRepr n@ contains a single value containing the vlaue+@n@.  This can be used to help use type-level variables on code+with data dependendent types.++The 'TestEquality' instance for 'NatRepr' is implemented using+'unsafeCoerce', as is the `isZeroNat` function. This should be+typesafe because we maintain the invariant that the integer value+contained in a NatRepr value matches its static type.+-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Trustworthy #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+#endif+module Data.Parameterized.NatRepr+  ( NatRepr+  , natValue+  , knownNat+  , withKnownNat+  , IsZeroNat(..)+  , isZeroNat+  , NatComparison(..)+  , compareNat+  , decNat+  , predNat+  , incNat+  , addNat+  , subNat+  , halfNat+  , withDivModNat+  , natMultiply+  , someNat+  , maxNat+  , natRec+  , natForEach+  , NatCases(..)+  , testNatCases+    -- * Bitvector utilities+  , widthVal+  , minUnsigned+  , maxUnsigned+  , minSigned+  , maxSigned+  , toUnsigned+  , toSigned+  , unsignedClamp+  , signedClamp+    -- * LeqProof+  , LeqProof(..)+  , testLeq+  , testStrictLeq+  , leqRefl+  , leqTrans+  , leqAdd2+  , leqSub2+  , leqMulCongr+    -- * LeqProof combinators+  , leqProof+  , withLeqProof+  , isPosNat+  , leqAdd+  , leqSub+  , leqMulPos+  , addIsLeq+  , withAddLeq+  , addPrefixIsLeq+  , withAddPrefixLeq+  , addIsLeqLeft1+  , dblPosIsPos+    -- * Arithmetic proof+  , plusComm+  , plusMinusCancel+  , withAddMulDistribRight+    -- * Re-exports typelists basics+--  , NatK+  , type (+)+  , type (-)+  , type (*)+  , type (<=)+  , Equality.TestEquality(..)+  , (Equality.:~:)(..)+  , Data.Parameterized.Some.Some+  ) where++import Data.Bits ((.&.))+import Data.Hashable+import Data.Proxy as Proxy+import Data.Type.Equality as Equality+import GHC.TypeLits as TypeLits+import Unsafe.Coerce++import Data.Parameterized.Classes+import Data.Parameterized.Some++maxInt :: Integer+maxInt = toInteger (maxBound :: Int)++------------------------------------------------------------------------+-- Nat++-- | A runtime presentation of a type-level 'Nat'.+--+-- This can be used for performing dynamic checks on a type-level natural+-- numbers.+newtype NatRepr (n::Nat) = NatRepr { natValue :: Integer+                                     -- ^ The underlying integer value of the number.+                                   }+  deriving (Hashable)++-- | Return the value of the nat representation.+widthVal :: NatRepr n -> Int+widthVal (NatRepr i) | i < maxInt = fromInteger i+                     | otherwise = error "Width is too large."++instance Eq (NatRepr m) where+  _ == _ = True++instance TestEquality NatRepr where+  testEquality (NatRepr m) (NatRepr n)+    | m == n = Just (unsafeCoerce Refl)+    | otherwise = Nothing++-- | Result of comparing two numbers.+data NatComparison m n where+  -- First number is less than second.+  NatLT :: !(NatRepr y) -> NatComparison x (x+(y+1))+  NatEQ :: NatComparison x x+  -- First number is greater than second.+  NatGT :: !(NatRepr y) -> NatComparison (x+(y+1)) x++compareNat :: NatRepr m -> NatRepr n -> NatComparison m n+compareNat m n =+  case compare (natValue m) (natValue n) of+    LT -> unsafeCoerce $ NatLT (NatRepr (natValue n - natValue m - 1))+    EQ -> unsafeCoerce $ NatEQ+    GT -> unsafeCoerce $ NatGT (NatRepr (natValue m - natValue n - 1))++instance OrdF NatRepr where+  compareF x y =+    case compareNat x y of+      NatLT _ -> LTF+      NatEQ -> EQF+      NatGT _ -> GTF++instance PolyEq (NatRepr m) (NatRepr n) where+  polyEqF x y = fmap (\Refl -> Refl) $ testEquality x y++instance Show (NatRepr n) where+  show (NatRepr n) = show n++instance ShowF NatRepr++instance HashableF NatRepr where+  hashWithSaltF = hashWithSalt++-- | This generates a NatRepr from a type-level context.+knownNat :: forall n . KnownNat n => NatRepr n+knownNat = NatRepr (natVal (Proxy :: Proxy n))++instance (KnownNat n) => KnownRepr NatRepr n where+  knownRepr = knownNat++{-# DEPRECATED withKnownNat "This function is potentially unsafe and is schedueled to be removed." #-}+withKnownNat :: forall n r. NatRepr n -> (KnownNat n => r) -> r+withKnownNat (NatRepr nVal) v =+  case someNatVal nVal of+    Just (SomeNat (Proxy :: Proxy n')) ->+      case unsafeCoerce (Refl :: 0 :~: 0) :: n :~: n' of+        Refl -> v+    Nothing -> error "withKnownNat: inner value in NatRepr is not a natural"++data IsZeroNat n where+  ZeroNat    :: IsZeroNat 0+  NonZeroNat :: IsZeroNat (n+1)++isZeroNat :: NatRepr n -> IsZeroNat n+isZeroNat (NatRepr 0) = unsafeCoerce ZeroNat+isZeroNat (NatRepr _) = unsafeCoerce NonZeroNat++-- | Decrement a @NatRepr@+decNat :: (1 <= n) => NatRepr n -> NatRepr (n-1)+decNat (NatRepr i) = NatRepr (i-1)++-- | Get the predicessor of a nat+predNat :: NatRepr (n+1) -> NatRepr n+predNat (NatRepr i) = NatRepr (i-1)++-- | Increment a @NatRepr@+incNat :: NatRepr n -> NatRepr (n+1)+incNat (NatRepr x) = NatRepr (x+1)++halfNat :: NatRepr (n+n) -> NatRepr n+halfNat (NatRepr x) = NatRepr (x `div` 2)++addNat :: NatRepr m -> NatRepr n -> NatRepr (m+n)+addNat (NatRepr m) (NatRepr n) = NatRepr (m+n)++subNat :: (n <= m) => NatRepr m -> NatRepr n -> NatRepr (m-n)+subNat (NatRepr m) (NatRepr n) = NatRepr (m-n)++withDivModNat :: forall n m a.+                 NatRepr n+              -> NatRepr m+              -> (forall div mod. (n ~ ((div * m) + mod)) =>+                  NatRepr div -> NatRepr mod -> a)+              -> a+withDivModNat n m f =+  case ( Some (NatRepr divPart), Some (NatRepr modPart)) of+     ( Some (divn :: NatRepr div), Some (modn :: NatRepr mod) )+       -> case unsafeCoerce (Refl :: 0 :~: 0) of+            (Refl :: (n :~: ((div * m) + mod))) -> f divn modn+  where+    (divPart, modPart) = divMod (natValue n) (natValue m)++natMultiply :: NatRepr n -> NatRepr m -> NatRepr (n * m)+natMultiply (NatRepr n) (NatRepr m) = NatRepr (n * m)++------------------------------------------------------------------------+-- Operations for using NatRepr as a bitwidth.++-- | Return minimum unsigned value for bitvector with given width (always 0).+minUnsigned :: NatRepr w -> Integer+minUnsigned _ = 0++-- | Return maximum unsigned value for bitvector with given width.+maxUnsigned :: NatRepr w -> Integer+maxUnsigned w = 2^(natValue w) - 1++-- | Return minimum value for bitvector in 2s complement with given width.+minSigned :: (1 <= w) => NatRepr w -> Integer+minSigned w = negate (2^(natValue w - 1))++-- | Return maximum value for bitvector in 2s complement with given width.+maxSigned :: (1 <= w) => NatRepr w -> Integer+maxSigned w = 2^(natValue w - 1) - 1++-- | @toUnsigned w i@ maps @i@ to a @i `mod` 2^w@.+toUnsigned :: NatRepr w -> Integer -> Integer+toUnsigned w i = maxUnsigned w .&. i++-- | @toSigned w i@ interprets the least-significant @w@ bits in @i@ as a+-- signed number in two's complement notation and returns that value.+toSigned :: (1 <= w) => NatRepr w -> Integer -> Integer+toSigned w i0+    | i > maxSigned w = i - 2^(natValue w)+    | otherwise       = i+  where i = i0 .&. maxUnsigned w++-- | @unsignedClamp w i@ rounds @i@ to the nearest value between+-- @0@ and @2^w-i@ (inclusive).+unsignedClamp :: NatRepr w -> Integer -> Integer+unsignedClamp w i+  | i < minUnsigned w = minUnsigned w+  | i > maxUnsigned w = maxUnsigned w+  | otherwise         = i++-- | @signedClamp w i@ rounds @i@ to the nearest value between+-- @-2^(w-1)@ and @2^(w-1)-i@ (inclusive).+signedClamp :: (1 <= w) => NatRepr w -> Integer -> Integer+signedClamp w i+  | i < minSigned w = minSigned w+  | i > maxSigned w = maxSigned w+  | otherwise       = i++------------------------------------------------------------------------+-- Some NatRepr++someNat :: Integer -> Maybe (Some NatRepr)+someNat n | 0 <= n && n <= toInteger maxInt = Just (Some (NatRepr (fromInteger n)))+          | otherwise = Nothing++-- | Return the maximum of two nat representations.+maxNat :: NatRepr m -> NatRepr n -> Some NatRepr+maxNat x y+  | natValue x >= natValue y = Some x+  | otherwise = Some y++------------------------------------------------------------------------+-- Arithmetic++-- | Produce evidence that + is commutative.+plusComm :: forall f m g n . f m -> g n -> m+n :~: n+m+plusComm _ _ = unsafeCoerce (Refl :: m+n :~: m+n)++-- | Cancel an add followed b a subtract+plusMinusCancel :: forall f m g n . f m -> g n -> (m + n) - n :~: m+plusMinusCancel _ _ = unsafeCoerce (Refl :: m :~: m)++withAddMulDistribRight :: forall n m p f g h a. f n -> g m -> h p+                    -> ( (((n * p) + (m * p)) ~ ((n + m) * p)) => a) -> a+withAddMulDistribRight _n _m _p f =+  case unsafeCoerce (Refl :: 0 :~: 0) of+    (Refl :: (((n * p) + (m * p)) :~: ((n + m) * p)) ) -> f++------------------------------------------------------------------------+-- LeqProof++-- | @LeqProof m n@ is a type whose values are only inhabited when @m@+-- is less than or equal to @n@.+data LeqProof m n where+  LeqProof :: (m <= n) => LeqProof m n++testStrictLeq :: forall m n+               . (m <= n)+              => NatRepr m+              -> NatRepr n+              -> Either (LeqProof (m+1) n) (m :~: n)+testStrictLeq (NatRepr m) (NatRepr n)+  | m < n = Left (unsafeCoerce (LeqProof :: LeqProof 0 0))+  | otherwise = Right (unsafeCoerce (Refl :: m :~: m))+{-# NOINLINE testStrictLeq #-}++-- As for NatComparison above, but works with LeqProof+data NatCases m n where+  -- First number is less than second.+  NatCaseLT :: LeqProof (m+1) n -> NatCases m n+  NatCaseEQ :: NatCases m m+  -- First number is greater than second.+  NatCaseGT :: LeqProof (n+1) m -> NatCases m n++testNatCases ::  forall m n+              . NatRepr m+             -> NatRepr n+             -> NatCases m n+testNatCases m n =+  case compare (natValue m) (natValue n) of+    LT -> NatCaseLT (unsafeCoerce (LeqProof :: LeqProof 0 0))+    EQ -> unsafeCoerce $ (NatCaseEQ :: NatCases m m)+    GT -> NatCaseGT (unsafeCoerce (LeqProof :: LeqProof 0 0))+{-# NOINLINE testNatCases #-}++-- | @x `testLeq` y@ checks whether @x@ is less than or equal to @y@.+testLeq :: forall m n . NatRepr m -> NatRepr n -> Maybe (LeqProof m n)+testLeq (NatRepr m) (NatRepr n)+   | m <= n    = Just (unsafeCoerce (LeqProof :: LeqProof 0 0))+   | otherwise = Nothing+{-# NOINLINE testLeq #-}++-- | Apply reflexivity to LeqProof+leqRefl :: forall f n . f n -> LeqProof n n+leqRefl _ = LeqProof+++-- | Apply transitivity to LeqProof+leqTrans :: LeqProof m n -> LeqProof n p -> LeqProof m p+leqTrans LeqProof LeqProof = unsafeCoerce (LeqProof :: LeqProof 0 0)+{-# NOINLINE leqTrans #-}++-- | Add both sides of two inequalities+leqAdd2 :: LeqProof x_l x_h -> LeqProof y_l y_h -> LeqProof (x_l + y_l) (x_h + y_h)+leqAdd2 x y = seq x $ seq y $ unsafeCoerce (LeqProof :: LeqProof 0 0)+{-# NOINLINE leqAdd2 #-}++-- | Subtract sides of two inequalities.+leqSub2 :: LeqProof x_l x_h+        -> LeqProof y_l y_h+        -> LeqProof (x_l-y_h) (x_h-y_l)+leqSub2 LeqProof LeqProof = unsafeCoerce (LeqProof :: LeqProof 0 0)+{-# NOINLINE leqSub2 #-}++------------------------------------------------------------------------+-- LeqProof combinators++-- | Create a leqProof using two proxies+leqProof :: (m <= n) => f m -> f n -> LeqProof m n+leqProof _ _ = LeqProof++withLeqProof :: LeqProof m n -> ((m <= n) => a) -> a+withLeqProof p a =+  case p of+    LeqProof -> a++-- | Test whether natural number is positive.+isPosNat :: NatRepr n -> Maybe (LeqProof 1 n)+isPosNat = testLeq (knownNat :: NatRepr 1)++-- | Congruence rule for multiplication+leqMulCongr :: LeqProof a x+            -> LeqProof b y+            -> LeqProof (a*b) (x*y)+leqMulCongr LeqProof LeqProof = unsafeCoerce (LeqProof :: LeqProof 1 1)+{-# NOINLINE leqMulCongr #-}++-- | Multiplying two positive numbers results in a positive number.+leqMulPos :: forall p q x y+          .  (1 <= x, 1 <= y)+          => p x+          -> q y+          -> LeqProof 1 (x*y)+leqMulPos _ _ = leqMulCongr (LeqProof :: LeqProof 1 x) (LeqProof :: LeqProof 1 y)++-- | Produce proof that adding a value to the larger element in an LeqProof+-- is larger+leqAdd :: forall f m n p . LeqProof m n -> f p -> LeqProof m (n+p)+leqAdd x _ = leqAdd2 x (LeqProof :: LeqProof 0 p)++-- | Produce proof that subtracting a value from the smaller element is smaller.+leqSub :: forall m n p . LeqProof m n -> LeqProof p m -> LeqProof (m-p) n+leqSub x _ = leqSub2 x (LeqProof :: LeqProof 0 p)++addIsLeq :: f n -> g m -> LeqProof n (n + m)+addIsLeq n m = leqAdd (leqRefl n) m++addPrefixIsLeq :: f m -> g n -> LeqProof n (m + n)+addPrefixIsLeq m n =+  case plusComm n m of+    Refl -> addIsLeq n m++dblPosIsPos :: forall n . LeqProof 1 n -> LeqProof 1 (n+n)+dblPosIsPos x = leqAdd x Proxy++addIsLeqLeft1 :: forall n n' m . LeqProof (n + n') m -> LeqProof n m+addIsLeqLeft1 p =+    case plusMinusCancel n n' of+      Refl -> leqSub p le+  where n :: Proxy n+        n = Proxy+        n' :: Proxy n'+        n' = Proxy+        le :: LeqProof n' (n + n')+        le = addPrefixIsLeq n n'++{-# INLINE withAddPrefixLeq #-}+withAddPrefixLeq :: NatRepr n -> NatRepr m -> ((m <= n + m) => a) -> a+withAddPrefixLeq n m = withLeqProof (addPrefixIsLeq n m)++withAddLeq :: forall n m a. NatRepr n -> NatRepr m -> ((n <= n + m) => NatRepr (n + m) -> a) -> a+withAddLeq n m f = withLeqProof (addIsLeq n m) (f (addNat n m))++natForEach' :: forall l h a+            . NatRepr l+            -> NatRepr h+            -> (forall n. LeqProof l n -> LeqProof n h -> NatRepr n -> a)+            -> [a]+natForEach' l h f+  | Just LeqProof  <- testLeq l h =+    let f' :: forall n. LeqProof (l + 1) n -> LeqProof n h -> NatRepr n -> a+        f' = \lp hp -> f (addIsLeqLeft1 lp) hp+     in f LeqProof LeqProof l : natForEach' (incNat l) h f'+  | otherwise             = []++-- | Apply a function to each element in a range; return the list of values+-- obtained.+natForEach :: forall l h a+            . NatRepr l+           -> NatRepr h+           -> (forall n. (l <= n, n <= h) => NatRepr n -> a)+           -> [a]+natForEach l h f = natForEach' l h (\LeqProof LeqProof -> f)++-- | Recursor for natural numbeers.+natRec :: forall m f+       .  NatRepr m+       -> f 0+       -> (forall n. NatRepr n -> f n -> f (n + 1))+       -> f m+natRec n f0 ih = go n+  where+    go :: forall n'. NatRepr n' -> f n'+    go n' = case isZeroNat n' of+              ZeroNat    -> f0+              NonZeroNat -> let n'' = predNat n' in ih n'' (go n'')
+ src/Data/Parameterized/Nonce.hs view
@@ -0,0 +1,158 @@+{-|+Copyright        : (c) Galois, Inc 2014-2016+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This module provides a simple generator of new indexes in the ST monad.+It is predictable and not intended for cryptographic purposes.++This module also provides a global nonce generator that will generate+2^64 nonces before looping.++NOTE: The 'TestEquality' and 'OrdF' instances for the 'Nonce' type simply+compare the generated nonce values and then assert to the compiler+(via 'unsafeCoerce') that the types ascribed to the nonces are equal+if their values are equal.+-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE Trustworthy #-}+#if MIN_VERSION_base(4,9,0)+{-# LANGUAGE TypeInType #-}+#endif+module Data.Parameterized.Nonce+  ( -- * NonceGenerator+    NonceGenerator+  , freshNonce+  , Nonce+  , indexValue+    -- * Accessing a nonce generator+  , newSTNonceGenerator+  , newIONonceGenerator+  , withIONonceGenerator+  , withSTNonceGenerator+  , withGlobalSTNonceGenerator+  , GlobalNonceGenerator+  , globalNonceGenerator+  ) where++import Control.Monad.ST+import Data.Hashable+import Data.IORef+import Data.STRef+import Data.Typeable+import Data.Word+import Unsafe.Coerce+import System.IO.Unsafe (unsafePerformIO)++import Data.Parameterized.Classes+import Data.Parameterized.Some++#if MIN_VERSION_base(4,9,0)+import Data.Kind+#endif++-- | Provides a monadic action for getting fresh typed names.+--+-- The first type parameter @m@ is the monad used for generating names, and+-- the second parameter @s@ is used for the counter.+data NonceGenerator (m :: * -> *) (s :: *) = NonceGenerator {+#if MIN_VERSION_base(4,9,0)+-- We have to make the k explicit in GHC 8.0 to avoid a warning.+    freshNonce :: forall k (tp :: k) . m (Nonce s tp)+#else+    freshNonce :: forall (tp :: k) . m (Nonce s tp)+#endif+  }++-- | Create a new counter.+withGlobalSTNonceGenerator :: (forall t . NonceGenerator (ST t) t -> ST t r) -> r+withGlobalSTNonceGenerator f = runST $ do+  r <- newSTRef (toEnum 0)+  f $! NonceGenerator {+      freshNonce = do+          i <- readSTRef r+          writeSTRef r $! succ i+          return $! Nonce i+    }++-- | Create a new nonce generator in the ST monad.+newSTNonceGenerator :: ST t (Some (NonceGenerator (ST t)))+newSTNonceGenerator = g <$> newSTRef (toEnum 0)+  where g r = Some $!+          NonceGenerator {+              freshNonce = do+                i <- readSTRef r+                writeSTRef r $! succ i+                return $! Nonce i+            }++-- | Create a new nonce generator in the ST monad.+newIONonceGenerator :: IO (Some (NonceGenerator IO))+newIONonceGenerator = g <$> newIORef (toEnum 0)+  where g r = Some $!+          NonceGenerator {+              freshNonce = do+                  i <- readIORef r+                  writeIORef r $! succ i+                  return $! Nonce i+            }++-- | Run a ST computation with a new nonce generator in the ST monad.+withSTNonceGenerator :: (forall s . NonceGenerator (ST t) s -> (ST t) r) -> ST t r+withSTNonceGenerator f = do+  Some r <- newSTNonceGenerator+  f r++-- | Create a new nonce generator in the IO monad.+withIONonceGenerator :: (forall s . NonceGenerator IO s -> IO r) -> IO r+withIONonceGenerator f = do+  Some r <- newIONonceGenerator+  f r++-- | An index generated by the counter.+newtype Nonce (s :: *) (tp :: k) = Nonce { indexValue :: Word64 }+  deriving (Eq, Ord, Hashable, Show)++--  Force the type role of Nonce to be nominal: this prevents Data.Coerce.coerce+--  from casting the types of nonces, which it would otherwise be able to do+--  because tp is a phantom type parameter.  This partially helps to protect+--  the nonce abstraction.+type role Nonce nominal nominal++instance TestEquality (Nonce s) where+  testEquality x y | indexValue x == indexValue y = unsafeCoerce (Just Refl)+                   | otherwise = Nothing++instance OrdF (Nonce s) where+  compareF x y =+    case compare (indexValue x) (indexValue y) of+      LT -> LTF+      EQ -> unsafeCoerce EQF+      GT -> GTF++instance HashableF (Nonce s) where+  hashWithSaltF s (Nonce x) = hashWithSalt s x++instance ShowF (Nonce s)++------------------------------------------------------------------------+-- GlobalNonceGenerator++data GlobalNonceGenerator++globalNonceIORef :: IORef Word64+globalNonceIORef = unsafePerformIO (newIORef 0)+{-# NOINLINE globalNonceIORef #-}++-- | A nonce generator that uses a globally-defined counter.+globalNonceGenerator :: NonceGenerator IO GlobalNonceGenerator+globalNonceGenerator =+  NonceGenerator+  { freshNonce = Nonce <$> atomicModifyIORef' globalNonceIORef (\n -> (n+1, n))+  }
+ src/Data/Parameterized/Nonce/Transformers.hs view
@@ -0,0 +1,70 @@+{-|+Copyright        : (c) Galois, Inc 2014-2016+Maintainer       : Eddy Westbrook <westbrook@galois.com>++This module provides a typeclass and monad transformers for generating+nonces.+-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+module Data.Parameterized.Nonce.Transformers+  ( MonadNonce(..)+  , NonceT(..)+  , NonceST+  , NonceIO+  , getNonceSTGen+  , runNonceST+  , runNonceIO+  , module Data.Parameterized.Nonce+  ) where++import Control.Monad.Reader+import Control.Monad.ST+import Control.Monad.State++import Data.Parameterized.Nonce+++-- | A 'MonadNonce' is a monad that can generate fresh 'Nonce's in a given set+-- (where we view the phantom type parameter of 'Nonce' as a designator of the+-- set that the 'Nonce' came from).+class Monad m => MonadNonce m where+  type NonceSet m :: *+  freshNonceM :: forall (tp :: k) . m (Nonce (NonceSet m) tp)++-- | This transformer adds a nonce generator to a given monad.+newtype NonceT s m a =+  NonceT { runNonceT :: ReaderT (NonceGenerator m s) m a }+  deriving (Functor, Applicative, Monad)++instance MonadTrans (NonceT s) where+  lift m = NonceT $ lift m++instance Monad m => MonadNonce (NonceT s m) where+  type NonceSet (NonceT s m) = s+  freshNonceM = NonceT $ lift . freshNonce =<< ask++instance MonadNonce m => MonadNonce (StateT s m) where+  type NonceSet (StateT s m) = NonceSet m+  freshNonceM = lift $ freshNonceM++-- | Helper type to build a 'MonadNonce' from the 'ST' monad.+type NonceST t s = NonceT s (ST t)++-- | Helper type to build a 'MonadNonce' from the 'IO' monad.+type NonceIO s = NonceT s IO++-- | Return the actual 'NonceGenerator' used in an 'ST' computation.+getNonceSTGen :: NonceST t s (NonceGenerator (ST t) s)+getNonceSTGen = NonceT ask++-- | Run a 'NonceST' computation with a fresh 'NonceGenerator'.+runNonceST :: (forall t s. NonceST t s a) -> a+runNonceST m = runST $ withSTNonceGenerator $ runReaderT $ runNonceT m++-- | Run a 'NonceIO' computation with a fresh 'NonceGenerator' inside 'IO'.+runNonceIO :: (forall s. NonceIO s a) -> IO a+runNonceIO m = withIONonceGenerator $ runReaderT $ runNonceT m
+ src/Data/Parameterized/Nonce/Unsafe.hs view
@@ -0,0 +1,107 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.NonceGenerator+-- Description      : A counter in the ST monad.+-- Copyright        : (c) Galois, Inc 2014+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+-- Stability        : provisional+--+-- This module provides a simple generator of new indexes in the ST monad.+-- It is predictable and not intended for cryptographic purposes.+--+-- NOTE: the 'TestEquality' and 'OrdF' instances for the 'Nonce' type simply+-- compare the generated nonce values and then assert to the compiler+-- (via 'unsafeCoerce') that the types ascribed to the nonces are equal+-- if their values are equal.  This is only OK because of the discipline+-- by which nonces should be used: they should only be generated from+-- a 'NonceGenerator' (i.e., should not be built directly), and nonces from+-- different generators must never be compared!  Arranging to compare+-- Nonces from different origins would allow users to build 'unsafeCoerce'+-- via the 'testEquality' function.+--+-- A somewhat safer API would be to brand the generated Nonces with the+-- state type variable of the NonceGenerator whence they came, and to only+-- provide NonceGenerators via a Rank-2 continuation-passing API, similar to+-- 'runST'. This would (via a meta-argument involving parametricity)+-- help to prevent nonces of different origin from being compared.+-- However, this would force us to push the 'ST' type brand into a significant+-- number of other structures and APIs.+--+-- Another alternative would be to use 'unsafePerformIO' magic to make+-- a global nonce generator, and make that the only way to generate nonces.+-- It is not clear that this is actually an improvement from a type safety+-- point of view, but an argument could be made.+--+-- For now, be careful using Nonces, and ensure that you do not mix+-- Nonces from different NonceGenerators.+------------------------------------------------------------------------+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE Unsafe #-}+module Data.Parameterized.Nonce.Unsafe+  ( NonceGenerator+  , newNonceGenerator+  , freshNonce+  , atLimit+  , Nonce+  , indexValue+  ) where++import Control.Monad.ST+import Data.Hashable+import Data.STRef+import Data.Word+import Unsafe.Coerce++import Data.Parameterized.Classes++-- | A simple type that for getting fresh indices in the 'ST' monad.+-- The type parameter @s@ is used for the 'ST' monad parameter.+newtype NonceGenerator s = NonceGenerator (STRef s Word64)++-- | Create a new counter.+newNonceGenerator :: ST s (NonceGenerator s)+newNonceGenerator = NonceGenerator `fmap` newSTRef (toEnum 0)++-- | An index generated by the counter.+newtype Nonce (tp :: k) = Nonce { indexValue :: Word64 }+  deriving (Eq, Ord, Hashable, Show)++--  Force the type role of Nonce to be nominal: this prevents Data.Coerce.coerce+--  from casting the types of nonces, which it would otherwise be able to do+--  because tp is a phantom type parameter.  This partially helps to protect+--  the nonce abstraction.+type role Nonce nominal++instance TestEquality Nonce where+  testEquality x y | indexValue x == indexValue y = unsafeCoerce (Just Refl)+                   | otherwise = Nothing++instance OrdF Nonce where+  compareF x y =+    case compare (indexValue x) (indexValue y) of+      LT -> LTF+      EQ -> unsafeCoerce EQF+      GT -> GTF++instance HashableF Nonce where+  hashWithSaltF s (Nonce x) = hashWithSalt s x++instance ShowF Nonce++{-# INLINE freshNonce #-}+-- | Get a fresh index and increment the counter.+freshNonce :: NonceGenerator s -> ST s (Nonce tp)+freshNonce (NonceGenerator r) = do+  i <- readSTRef r+  writeSTRef r $! succ i+  return (Nonce i)++-- | Return true if counter has reached the limit, and can't be+-- incremented without risk of error.+atLimit :: NonceGenerator s -> ST s Bool+atLimit (NonceGenerator r) = do+  i <- readSTRef r+  return (i == maxBound)
+ src/Data/Parameterized/Pair.hs view
@@ -0,0 +1,51 @@+{-|+Copyright        : (c) Galois, Inc 2017++This module defines a 2-tuple where both elements are parameterized over the+same existentially quantified parameter.++-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+module Data.Parameterized.Pair+  ( Pair(..)+  , fstPair+  , sndPair+  , viewPair+  ) where++import Data.Parameterized.Classes+import Data.Parameterized.Some+import Data.Parameterized.TraversableF++-- | Like a 2-tuple, but with an existentially quantified parameter that both of+-- the elements share.+data Pair (a :: k -> *) (b :: k -> *) where+  Pair :: !(a tp) -> !(b tp) -> Pair a b++instance (TestEquality a, EqF b) => Eq (Pair a b) where+  Pair xa xb == Pair ya yb =+    case testEquality xa ya of+      Just Refl -> eqF xb yb+      Nothing -> False++instance FunctorF (Pair a) where+  fmapF f (Pair x y) = Pair x (f y)++instance FoldableF (Pair a) where+  foldMapF f (Pair _ y) = f y+  foldrF f z (Pair _ y) = f y z++-- | Extract the first element of a pair.+fstPair :: Pair a b -> Some a+fstPair (Pair x _) = Some x++-- | Extract the second element of a pair.+sndPair :: Pair a b -> Some b+sndPair (Pair _ y) = Some y++-- | Project out of Pair.+viewPair :: (forall tp. a tp -> b tp -> c) -> Pair a b -> c+viewPair f (Pair x y) = f x y
+ src/Data/Parameterized/Some.hs view
@@ -0,0 +1,59 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.Some+-- Copyright        : (c) Galois, Inc 2014+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module provides 'Some', a GADT that hides a type parameter.+------------------------------------------------------------------------+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+module Data.Parameterized.Some+  ( Some(..)+  , viewSome+  , mapSome+  , traverseSome+  , traverseSome_+  ) where++import Data.Hashable+import Data.Parameterized.Classes+++data Some (f:: k -> *) = forall x . Some (f x)++instance TestEquality f => Eq (Some f) where+  Some x == Some y = isJust (testEquality x y)++instance OrdF f => Ord (Some f) where+  compare (Some x) (Some y) = toOrdering (compareF x y)++instance HashableF f => Hashable (Some f) where+  hashWithSalt s (Some x) = hashWithSaltF s x+  hash (Some x) = hashF x++instance ShowF f => Show (Some f) where+  show (Some x) = showF x++-- | Project out of Some.+viewSome :: (forall tp . f tp -> r) -> Some f -> r+viewSome f (Some x) = f x++-- | Apply function to inner value.+mapSome :: (forall tp . f tp -> g tp) -> Some f -> Some g+mapSome f (Some x) = Some $! f x++{-# INLINE traverseSome #-}+-- | Modify the inner value.+traverseSome :: Functor m+             => (forall tp . f tp -> m (g tp))+             -> Some f+             -> m (Some g)+traverseSome f (Some x) = Some `fmap` f x++{-# INLINE traverseSome_ #-}+-- | Modify the inner value.+traverseSome_ :: Functor m => (forall tp . f tp -> m ()) -> Some f -> m ()+traverseSome_ f (Some x) = (\_ -> ()) `fmap` f x
+ src/Data/Parameterized/SymbolRepr.hs view
@@ -0,0 +1,106 @@+{-|+Copyright        : (c) Galois, Inc 2014-2015+Maintainer       : Joe Hendrix <jhendrix@galois.com>++This defines a type family 'SymbolRepr' for representing a type-level string+(AKA symbol) at runtime.  This can be used to branch on a type-level value.++The 'TestEquality' and 'OrdF' instances for 'SymbolRepr' are implemented using+'unsafeCoerce'.  This should be typesafe because we maintain the invariant+that the string value contained in a SymbolRepr value matches its static type.++At the type level, symbols have very few operations, so SymbolRepr+correspondingly has very few functions that manipulate them.+-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Trustworthy #-}+module Data.Parameterized.SymbolRepr+  ( -- * SymbolRepr+    SymbolRepr+  , symbolRepr+  , knownSymbol+  , someSymbol+    -- * Re-exports+  , type GHC.Symbol+  , GHC.KnownSymbol+  ) where++import GHC.TypeLits as GHC+import Unsafe.Coerce (unsafeCoerce)++import Data.Hashable+import Data.Proxy+import qualified Data.Text as Text++import Data.Parameterized.Classes+import Data.Parameterized.Some++-- | A runtime representation of a GHC type-level symbol.+newtype SymbolRepr (nm::GHC.Symbol)+  = SymbolRepr { symbolRepr :: Text.Text+                 -- ^ The underlying text representation of the symbol+               }+-- INVARIANT: The contained runtime text value matches the value+-- of the type level symbol.  The SymbolRepr constructor+-- is not exported so we can maintain this invariant in this+-- module.++-- | Generate a symbol representative at runtime.  The type-level+--   symbol will be abstract, as it is hidden by the 'Some' constructor.+someSymbol :: Text.Text -> Some SymbolRepr+someSymbol nm = Some (SymbolRepr nm)++-- | Generate a value representative for the type level symbol.+knownSymbol :: GHC.KnownSymbol s => SymbolRepr s+knownSymbol = go Proxy+  where go :: GHC.KnownSymbol s => Proxy s -> SymbolRepr s+        go p = SymbolRepr $! packSymbol (GHC.symbolVal p)++        -- NOTE here we explicitly test that unpacking the packed text value+        -- gives the desired string.  This is to avoid pathological corner cases+        -- involving string values that have no text representation.+        packSymbol str+           | Text.unpack txt == str = txt+           | otherwise = error $ "Unrepresentable symbol! "++ str+         where txt = Text.pack str++instance (GHC.KnownSymbol s) => KnownRepr SymbolRepr s where+  knownRepr = knownSymbol++instance TestEquality SymbolRepr where+   testEquality (SymbolRepr x :: SymbolRepr x) (SymbolRepr y)+      | x == y    = Just (unsafeCoerce (Refl :: x :~: x))+      | otherwise = Nothing+instance OrdF SymbolRepr where+   compareF (SymbolRepr x :: SymbolRepr x) (SymbolRepr y)+      | x <  y    = LTF+      | x == y    = unsafeCoerce (EQF :: OrderingF x x)+      | otherwise = GTF++-- These instances are trivial by the invariant+-- that the contained string matches the type-level+-- symbol+instance Eq (SymbolRepr x) where+   _ == _ = True+instance Ord (SymbolRepr x) where+   compare _ _ = EQ++instance HashableF SymbolRepr where+  hashWithSaltF = hashWithSalt+instance Hashable (SymbolRepr nm) where+  hashWithSalt s (SymbolRepr nm) = hashWithSalt s nm++instance Show (SymbolRepr nm) where+  show (SymbolRepr nm) = Text.unpack nm++instance ShowF SymbolRepr
+ src/Data/Parameterized/TH/GADT.hs view
@@ -0,0 +1,443 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.TH.GADT+-- Copyright        : (c) Galois, Inc 2013-2014+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module declares template Haskell primitives so that it is easier+-- to work with GADTs that have many constructors.+------------------------------------------------------------------------+{-# LANGUAGE DoAndIfThenElse #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE EmptyCase #-}+module Data.Parameterized.TH.GADT+  ( structuralEquality+  , structuralTypeEquality+  , structuralTypeOrd+  , structuralTraversal+  , structuralShowsPrec+  , structuralHash+  , PolyEq(..)+    -- * Template haskell utilities that may be useful in other contexts.+  , DataD+  , lookupDataType'+  , asTypeCon+  , conPat+  , TypePat(..)+  , dataParamTypes+  , assocTypePats+  ) where++import Control.Monad+import Data.Hashable (hashWithSalt)+import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as Set+import Language.Haskell.TH+import Language.Haskell.TH.Datatype+++import Data.Parameterized.Classes++------------------------------------------------------------------------+-- Template Haskell utilities++type DataD = DatatypeInfo++lookupDataType' :: Name -> Q DatatypeInfo+lookupDataType' = reifyDatatype++-- | Given a constructor and string, this generates a pattern for matching+-- the expression, and the names of variables bound by pattern in order+-- they appear in constructor.+conPat ::+  ConstructorInfo {- ^ constructor information -} ->+  String          {- ^ generated name prefix   -} ->+  Q (Pat, [Name]) {- ^ pattern and bound names -}+conPat con pre = do+  nms <- newNames pre (length (constructorFields con))+  return (ConP (constructorName con) (VarP <$> nms), nms)+++-- | Return an expression corresponding to the constructor.+-- Note that this will have the type of a function expecting+-- the argumetns given.+conExpr :: ConstructorInfo -> Exp+conExpr = ConE . constructorName++------------------------------------------------------------------------+-- TypePat++data TypePat+   = TypeApp TypePat TypePat -- ^ The application of a type.+   | AnyType       -- ^ Match any type.+   | DataArg Int   -- ^ Match the ith argument of the data type we are traversing.+   | ConType TypeQ -- ^ Match a ground type.++matchTypePat :: [Type] -> TypePat -> Type -> Q Bool+matchTypePat d (TypeApp p q) (AppT x y) = do+  r <- matchTypePat d p x+  case r of+    True -> matchTypePat d q y+    False -> return False+matchTypePat _ AnyType _ = return True+matchTypePat tps (DataArg i) tp+  | i < 0 || i > length tps = error $ "Illegal type pattern index " ++ show i+  | otherwise = do+    return $ stripSigT (tps !! i) == tp+  where+    -- th-abstraction can annotate type parameters with their kinds,+    -- we ignore these for matching+    stripSigT (SigT t _) = t+    stripSigT t          = t+matchTypePat _ (ConType tpq) tp = do+  tp' <- tpq+  return (tp' == tp)+matchTypePat _ _ _ = return False++dataParamTypes :: DatatypeInfo -> [Type]+dataParamTypes = datatypeVars++-- | Find value associated with first pattern that matches given pat if any.+assocTypePats :: [Type] -> [(TypePat,v)] -> Type -> Q (Maybe v)+assocTypePats _ [] _ = return Nothing+assocTypePats dTypes ((p,v):pats) tp = do+  r <- matchTypePat dTypes p tp+  case r of+    True -> return (Just v)+    False -> assocTypePats dTypes pats tp++------------------------------------------------------------------------+-- Contructor cases++typeVars :: TypeSubstitution a => a -> Set Name+typeVars = Set.fromList . freeVariables+++-- | @declareStructuralEquality@ declares a structural equality predicate.+structuralEquality :: TypeQ -> [(TypePat,ExpQ)] -> ExpQ+structuralEquality tpq pats =+  [| \x y -> isJust ($(structuralTypeEquality tpq pats) x y) |]++joinEqMaybe :: Name -> Name -> ExpQ -> ExpQ+joinEqMaybe x y r = do+  [| if $(varE x) == $(varE y) then $(r) else Nothing |]++joinTestEquality :: ExpQ -> Name -> Name -> ExpQ -> ExpQ+joinTestEquality f x y r =+  [| case $(f) $(varE x) $(varE y) of+      Nothing -> Nothing+      Just Refl -> $(r)+   |]++matchEqArguments :: [Type]+                    -- ^ Types bound by data arguments.+                  -> [(TypePat,ExpQ)] -- ^ Patterns for matching arguments+                 -> Name+                     -- ^ Name of constructor.+                 -> Set Name+                 -> [Type]+                 -> [Name]+                 -> [Name]+                 -> ExpQ+matchEqArguments dTypes pats cnm bnd (tp:tpl) (x:xl) (y:yl) = do+  doesMatch <- assocTypePats dTypes pats tp+  case doesMatch of+    Just q -> do+      let bnd' =+            case tp of+              AppT _ (VarT nm) -> Set.insert nm bnd+              _ -> bnd+      joinTestEquality q x y (matchEqArguments dTypes pats cnm bnd' tpl xl yl)+    Nothing | typeVars tp `Set.isSubsetOf` bnd -> do+      joinEqMaybe x y        (matchEqArguments dTypes pats cnm bnd  tpl xl yl)+    Nothing -> do+      fail $ "Unsupported argument type " ++ show tp+          ++ " in " ++ show (ppr cnm) ++ "."+matchEqArguments _ _ _ _ [] [] [] = [| Just Refl |]+matchEqArguments _ _ _ _ [] _  _  = error "Unexpected end of types."+matchEqArguments _ _ _ _ _  [] _  = error "Unexpected end of names."+matchEqArguments _ _ _ _ _  _  [] = error "Unexpected end of names."++mkSimpleEqF :: [Type] -- ^ Data declaration types+            -> Set Name+             -> [(TypePat,ExpQ)] -- ^ Patterns for matching arguments+             -> ConstructorInfo+             -> [Name]+             -> ExpQ+             -> Bool -- ^ wildcard case required+             -> ExpQ+mkSimpleEqF dTypes bnd pats con xv yQ multipleCases = do+  -- Get argument types for constructor.+  let nm = constructorName con+  (yp,yv) <- conPat con "y"+  let rv = matchEqArguments dTypes pats nm bnd (constructorFields con) xv yv+  caseE yQ $ match (pure yp) (normalB rv) []+           : [ match wildP (normalB [| Nothing |]) [] | multipleCases ]++-- | Match equational form.+mkEqF :: DatatypeInfo -- ^ Data declaration.+      -> [(TypePat,ExpQ)]+      -> ConstructorInfo+      -> [Name]+      -> ExpQ+      -> Bool -- ^ wildcard case required+      -> ExpQ+mkEqF d pats con =+  let dVars = datatypeVars d+      bnd | null dVars = Set.empty+          | otherwise  = typeVars (init dVars)+  in mkSimpleEqF dVars bnd pats con++-- | @structuralTypeEquality f@ returns a function with the type:+--   forall x y . f x -> f y -> Maybe (x :~: y)+structuralTypeEquality :: TypeQ -> [(TypePat,ExpQ)] -> ExpQ+structuralTypeEquality tpq pats = do+  d <- reifyDatatype =<< asTypeCon "structuralTypeEquality" =<< tpq++  let multipleCons = not (null (drop 1 (datatypeCons d)))+      trueEqs yQ = [ do (xp,xv) <- conPat con "x"+                        match (pure xp) (normalB (mkEqF d pats con xv yQ multipleCons)) []+                   | con <- datatypeCons d+                   ]++  if null (datatypeCons d)+    then [| \x -> case x of {} |]+    else [| \x y -> $(caseE [| x |] (trueEqs [| y |])) |]++-- | @structuralTypeEquality f@ returns a function with the type:+--   forall x y . f x -> f y -> OrderingF x y+--+-- This implementation avoids matching on both the first and second+-- parameters in a simple case expression in order to avoid stressing+-- GHC's coverage checker. In the case that the first and second parameters+-- have unique constructors, a simple numeric comparison is done to+-- compute the result.+structuralTypeOrd ::+  TypeQ ->+  [(TypePat,ExpQ)] {- ^ List of type patterns to match. -} ->+  ExpQ+structuralTypeOrd tpq l = do+  d <- reifyDatatype =<< asTypeCon "structuralTypeEquality" =<< tpq++  let withNumber :: ExpQ -> (Maybe ExpQ -> ExpQ) -> ExpQ+      withNumber yQ k+        | null (drop 1 (datatypeCons d)) = k Nothing+        | otherwise =  [| let yn :: Int+                              yn = $(caseE yQ (constructorNumberMatches (datatypeCons d)))+                          in $(k (Just [| yn |])) |]++  if null (datatypeCons d)+    then [| \x -> case x of {} |]+    else [| \x y -> $(withNumber [|y|] $ \mbYn -> caseE [| x |] (outerOrdMatches d [|y|] mbYn)) |]+  where+    constructorNumberMatches :: [ConstructorInfo] -> [MatchQ]+    constructorNumberMatches cons =+      [ match (recP (constructorName con) [])+              (normalB (litE (integerL i)))+              []+      | (i,con) <- zip [0..] cons ]++    outerOrdMatches :: DatatypeInfo -> ExpQ -> Maybe ExpQ -> [MatchQ]+    outerOrdMatches d yExp mbYn =+      [ do (pat,xv) <- conPat con "x"+           match (pure pat)+                 (normalB (do xs <- mkOrdF d l con i mbYn xv+                              caseE yExp xs))+                 []+      | (i,con) <- zip [0..] (datatypeCons d) ]++-- | Generate a list of fresh names using the base name+-- numbered 1 to n to make them useful in conjunction with+-- @-dsuppress-unqiues@.+newNames ::+  String   {- ^ base name                     -} ->+  Int      {- ^ quantity                      -} ->+  Q [Name] {- ^ list of names: base1, base2.. -}+newNames base n = traverse (\i -> newName (base ++ show i)) [1..n]+++joinCompareF :: ExpQ -> Name -> Name -> ExpQ -> ExpQ+joinCompareF f x y r = do+  [| case $(f) $(varE x) $(varE y) of+      LTF -> LTF+      GTF -> GTF+      EQF -> $(r)+   |]++-- | Compare two variables and use following comparison if they are different.+--+-- This returns an 'OrdF' instance.+joinCompareToOrdF :: Name -> Name -> ExpQ -> ExpQ+joinCompareToOrdF x y r =+  [| case compare $(varE x) $(varE y) of+      LT -> LTF+      GT -> GTF+      EQ -> $(r)+   |]++  -- Match expression with given type to variables+matchOrdArguments :: [Type]+                     -- ^ Types bound by data arguments+                  -> [(TypePat,ExpQ)] -- ^ Patterns for matching arguments+                  -> Name+                     -- ^ Name of constructor.+                  -> Set Name+                    -- ^ Names bound in data declaration+                  -> [Type]+                     -- ^ Types for constructors+                  -> [Name]+                     -- ^ Variables bound in first pattern+                  -> [Name]+                     -- ^ Variables bound in second pattern+                  -> ExpQ+matchOrdArguments dTypes pats cnm bnd (tp : tpl) (x:xl) (y:yl) = do+  doesMatch <- assocTypePats dTypes pats tp+  case doesMatch of+    Just f -> do+      let bnd' = case tp of+                   AppT _ (VarT nm) -> Set.insert nm bnd+                   _ -> bnd+      joinCompareF f x y (matchOrdArguments dTypes pats cnm bnd' tpl xl yl)+    Nothing | typeVars tp `Set.isSubsetOf` bnd -> do+      joinCompareToOrdF x y (matchOrdArguments dTypes pats cnm bnd tpl xl yl)+    Nothing ->+      fail $ "Unsupported argument type " ++ show (ppr tp)+             ++ " in " ++ show (ppr cnm) ++ "."+matchOrdArguments _ _ _ _ [] [] [] = [| EQF |]+matchOrdArguments _ _ _ _ [] _  _  = error "Unexpected end of types."+matchOrdArguments _ _ _ _ _  [] _  = error "Unexpected end of names."+matchOrdArguments _ _ _ _ _  _  [] = error "Unexpected end of names."++mkSimpleOrdF :: [Type] -- ^ Data declaration types+             -> [(TypePat,ExpQ)] -- ^ Patterns for matching arguments+             -> ConstructorInfo -- ^ Information about the second constructor+             -> Integer -- ^ First constructor's index+             -> Maybe ExpQ -- ^ Optional second constructor's index+             -> [Name]  -- ^ Name from first pattern+             -> Q [MatchQ]+mkSimpleOrdF dTypes pats con xnum mbYn xv = do+  (yp,yv) <- conPat con "y"+  let rv = matchOrdArguments dTypes pats (constructorName con) Set.empty (constructorFields con) xv yv+  -- Return match expression+  return $ match (pure yp) (normalB rv) []+         : case mbYn of+             Nothing -> []+             Just yn -> [match wildP (normalB [| if xnum < $yn then LTF else GTF |]) []]++-- | Match equational form.+mkOrdF :: DatatypeInfo -- ^ Data declaration.+       -> [(TypePat,ExpQ)] -- ^ Patterns for matching arguments+       -> ConstructorInfo+       -> Integer+       -> Maybe ExpQ -- ^ optional right constructr index+       -> [Name]+       -> Q [MatchQ]+mkOrdF d pats = mkSimpleOrdF (datatypeVars d) pats++-- | Find the first recurseArg f var tp@ applies @f@ to @var@ where @var@ has type @tp@.+recurseArg :: (Type -> Q (Maybe ExpQ))+           -> ExpQ -- ^ Function to apply+           -> ExpQ+           -> Type+           -> Q (Maybe Exp)+recurseArg m f v tp = do+  mr <- m tp+  case mr of+    Just g ->  Just <$> [| $(g) $(f) $(v) |]+    Nothing ->+      case tp of+        AppT (ConT _) (AppT (VarT _) _) -> Just <$> [| traverse $(f) $(v) |]+        AppT (VarT _) _ -> Just <$> [| $(f) $(v) |]+        _ -> return Nothing++-- | @traverseAppMatch f c@ builds a case statement that matches a term with+-- the constructor @c@ and applies @f@ to each argument.+traverseAppMatch :: (Type -> Q (Maybe ExpQ)) -- Pattern match function+                 -> ExpQ -- ^ Function to apply to each argument recursively.+                 -> ConstructorInfo -- ^ Constructor to match.+                 -> MatchQ -- ^ Match expression that+traverseAppMatch pats fv c0 = do+  (pat,patArgs) <- conPat c0 "p"+  exprs <- zipWithM (recurseArg pats fv) (varE <$> patArgs) (constructorFields c0)++  let mkRes :: ExpQ -> [(Name, Maybe Exp)] -> ExpQ+      mkRes e [] = e+      mkRes e ((v,Nothing):r) =+        mkRes (appE e (varE v)) r+      mkRes e ((_,Just{}):r) = do+        v <- newName "r"+        lamE [varP v] (mkRes (appE e (varE v)) r)++  -- Apply the remaining argument to the expression in list.+  let applyRest :: ExpQ -> [Exp] -> ExpQ+      applyRest e [] = e+      applyRest e (a:r) = applyRest [| $(e) <*> $(pure a) |] r++  -- Apply the first argument to the list+  let applyFirst :: ExpQ -> [Exp] -> ExpQ+      applyFirst e [] = [| pure $(e) |]+      applyFirst e (a:r) = applyRest [| $(e) <$> $(pure a) |] r++  let pargs = patArgs `zip` exprs+  let rhs = applyFirst (mkRes (pure (conExpr c0)) pargs) (catMaybes exprs)+  match (pure pat) (normalB rhs) []++-- | @structuralTraversal tp@ generates a function that applies+-- a traversal @f@ to the subterms with free variables in @tp@.+structuralTraversal :: TypeQ -> [(TypePat, ExpQ)] -> ExpQ+structuralTraversal tpq pats0 = do+  d <- reifyDatatype =<< asTypeCon "structuralTraversal" =<< tpq+  f <- newName "f"+  a <- newName "a"+  lamE [varP f, varP a] $+      caseE (varE a)+      (traverseAppMatch (assocTypePats (datatypeVars d) pats0) (varE f) <$> datatypeCons d)++asTypeCon :: Monad m => String -> Type -> m Name+asTypeCon _ (ConT nm) = return nm+asTypeCon fn _ = fail $ fn ++ " expected type constructor."++-- | @structuralHash tp@ generates a function with the type+-- @Int -> tp -> Int@ that hashes type.+structuralHash :: TypeQ -> ExpQ+structuralHash tpq = do+  d <- reifyDatatype =<< asTypeCon "structuralHash" =<< tpq+  s <- newName "s"+  a <- newName "a"+  lamE [varP s, varP a] $+    caseE (varE a) (zipWith (matchHashCtor (varE s)) [0..] (datatypeCons d))++matchHashCtor :: ExpQ -> Integer  -> ConstructorInfo -> MatchQ+matchHashCtor s0 i c = do+  (pat,vars) <- conPat c "x"+  let args = [| $(litE (IntegerL i)) :: Int |] : (varE <$> vars)+  let go s e = [| hashWithSalt $(s) $(e) |]+  let rhs = foldl go s0 args+  match (pure pat) (normalB rhs) []++-- | @structuralShow tp@ generates a function with the type+-- @tp -> ShowS@ that shows the constructor.+structuralShowsPrec :: TypeQ -> ExpQ+structuralShowsPrec tpq = do+  d <- reifyDatatype =<< asTypeCon "structuralShowPrec" =<< tpq+  p <- newName "_p"+  a <- newName "a"+  lamE [varP p, varP a] $+    caseE (varE a) (matchShowCtor (varE p) <$> datatypeCons d)++showCon :: ExpQ -> Name -> Int -> MatchQ+showCon p nm n = do+  vars <- newNames "x" n+  let pat = ConP nm (VarP <$> vars)+  let go s e = [| $(s) . showChar ' ' . showsPrec 10 $(varE e) |]+  let ctor = [| showString $(return (LitE (StringL (nameBase nm)))) |]+  let rhs | null vars = ctor+          | otherwise = [| showParen ($(p) >= 10) $(foldl go ctor vars) |]+  match (pure pat) (normalB rhs) []++matchShowCtor :: ExpQ -> ConstructorInfo -> MatchQ+matchShowCtor p con = showCon p (constructorName con) (length (constructorFields con))
+ src/Data/Parameterized/TraversableF.hs view
@@ -0,0 +1,117 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.TraversableF+-- Copyright        : (c) Galois, Inc 2014-2015+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module declares classes for working with structures that accept+-- a single parametric type parameter.+------------------------------------------------------------------------+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Trustworthy #-}+module Data.Parameterized.TraversableF+  ( FunctorF(..)+  , FoldableF(..)+  , TraversableF(..)+  , traverseF_+  , fmapFDefault+  , foldMapFDefault+  , allF+  , anyF+  ) where++import Control.Applicative+import Control.Monad.Identity+import Data.Coerce+import Data.Functor.Const+import Data.Monoid+import GHC.Exts (build)++-- | A parameterized type that is a function on all instances.+class FunctorF m where+  fmapF :: (forall x . f x -> g x) -> m f -> m g++instance FunctorF (Const x) where+  fmapF _ = coerce++------------------------------------------------------------------------+-- FoldableF++-- | This is a coercision used to avoid overhead associated+-- with function composition.+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)+(#.) _f = coerce++-- | This is a generalization of the @Foldable@ class to+-- structures over parameterized terms.+class FoldableF (t :: (k -> *) -> *) where+  {-# MINIMAL foldMapF | foldrF #-}++  -- | Map each element of the structure to a monoid,+  -- and combine the results.+  foldMapF :: Monoid m => (forall s . e s -> m) -> t e -> m+  foldMapF f = foldrF (mappend . f) mempty++  -- | Right-associative fold of a structure.+  foldrF :: (forall s . e s -> b -> b) -> b -> t e -> b+  foldrF f z t = appEndo (foldMapF (Endo #. f) t) z++  -- | Left-associative fold of a structure.+  foldlF :: (forall s . b -> e s -> b) -> b -> t e -> b+  foldlF f z t = appEndo (getDual (foldMapF (\e -> Dual (Endo (\r -> f r e))) t)) z++  -- | Right-associative fold of a structure,+  -- but with strict application of the operator.+  foldrF' :: (forall s . e s -> b -> b) -> b -> t e -> b+  foldrF' f0 z0 xs = foldlF (f' f0) id xs z0+    where f' f k x z = k $! f x z++  -- | Left-associative fold of a parameterized structure+  -- with a strict accumulator.+  foldlF' :: (forall s . b -> e s -> b) -> b -> t e -> b+  foldlF' f0 z0 xs = foldrF (f' f0) id xs z0+    where f' f x k z = k $! f z x++  -- | Convert structure to list.+  toListF :: (forall tp . f tp -> a) -> t f -> [a]+  toListF f t = build (\c n -> foldrF (\e v -> c (f e) v) n t)++-- | Return 'True' if all values satisfy predicate.+allF :: FoldableF t => (forall tp . f tp -> Bool) -> t f -> Bool+allF p = getAll #. foldMapF (All #. p)++-- | Return 'True' if any values satisfy predicate.+anyF :: FoldableF t => (forall tp . f tp -> Bool) -> t f -> Bool+anyF p = getAny #. foldMapF (Any #. p)++instance FoldableF (Const x) where+  foldMapF _ _ = mempty++------------------------------------------------------------------------+-- TraversableF++class (FunctorF t, FoldableF t) => TraversableF t where+  traverseF :: Applicative m+            => (forall s . e s -> m (f s))+            -> t e+            -> m (t f)++instance TraversableF (Const x) where+  traverseF _ (Const x) = pure (Const x)++-- | This function may be used as a value for `fmapF` in a `FunctorF`+-- instance.+fmapFDefault :: TraversableF t => (forall s . e s -> f s) -> t e -> t f+fmapFDefault f = runIdentity #. traverseF (Identity #. f)+{-# INLINE fmapFDefault #-}++-- | This function may be used as a value for `Data.Foldable.foldMap`+-- in a `Foldable` instance.+foldMapFDefault :: (TraversableF t, Monoid m) => (forall s . e s -> m) -> t e -> m+foldMapFDefault f = getConst #. traverseF (Const #. f)++-- | Map each element of a structure to an action, evaluate+-- these actions from left to right, and ignore the results.+traverseF_ :: (FoldableF t, Applicative f) => (forall s . e s  -> f ()) -> t e -> f ()+traverseF_ f = foldrF (\e r -> f e *> r) (pure ())
+ src/Data/Parameterized/TraversableFC.hs view
@@ -0,0 +1,162 @@+------------------------------------------------------------------------+-- |+-- Module           : Data.Parameterized.TraversableFC+-- Copyright        : (c) Galois, Inc 2014-2015+-- Maintainer       : Joe Hendrix <jhendrix@galois.com>+--+-- This module declares classes for working with structures that accept+-- a parametric type parameter followed by some fixed kind.+------------------------------------------------------------------------+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeOperators #-}+module Data.Parameterized.TraversableFC+  ( TestEqualityFC(..)+  , OrdFC(..)+  , ShowFC(..)+  , HashableFC(..)+  , FunctorFC(..)+  , FoldableFC(..)+  , TraversableFC(..)+  , traverseFC_+  , forMFC_+  , fmapFCDefault+  , foldMapFCDefault+  , allFC+  , anyFC+  , lengthFC+  ) where++import Control.Applicative (Const(..) )+import Control.Monad.Identity ( Identity (..) )+import Data.Coerce+import Data.Monoid+import GHC.Exts (build)+import Data.Type.Equality++import Data.Parameterized.Classes++-- | A parameterized type that is a function on all instances.+class FunctorFC m where+  fmapFC :: forall f g. (forall x . f x -> g x) ->+                        (forall x . m f x -> m g x)++-- | A parameterized class for types which can be shown, when given+--   functions to show parameterized subterms.+class ShowFC (t :: (k -> *) -> l -> *) where+  {-# MINIMAL showFC | showsPrecFC #-}++  showFC :: forall f. (forall x. f x -> String)+         -> (forall x. t f x -> String)+  showFC sh x = showsPrecFC (\_prec z rest -> sh z ++ rest) 0 x []++  showsPrecFC :: forall f. (forall x. Int -> f x -> ShowS) ->+                           (forall x. Int -> t f x -> ShowS)+  showsPrecFC sh _prec x rest = showFC (\z -> sh 0 z []) x ++ rest+++-- | A parameterized class for types which can be hashed, when given+--   functions to hash parameterized subterms.+class HashableFC (t :: (k -> *) -> l -> *) where+  hashWithSaltFC :: forall f. (forall x. Int -> f x -> Int) ->+                              (forall x. Int -> t f x -> Int)++-- | A parameterized class for types which can be tested for parameterized equality,+--   when given an equality test for subterms.+class TestEqualityFC (t :: (k -> *) -> l -> *) where+  testEqualityFC :: forall f. (forall x y. f x -> f y -> (Maybe (x :~: y))) ->+                              (forall x y. t f x -> t f y -> (Maybe (x :~: y)))++-- | A parameterized class for types which can be tested for parameterized ordering,+--   when given an comparison test for subterms.+class TestEqualityFC t => OrdFC (t :: (k -> *) -> l -> *) where+  compareFC :: forall f. (forall x y. f x -> f y -> OrderingF x y) ->+                         (forall x y. t f x -> t f y -> OrderingF x y)++------------------------------------------------------------------------+-- FoldableF++-- | This is a coercision used to avoid overhead associated+-- with function composition.+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)+(#.) _f = coerce++-- | This is a generalization of the @Foldable@ class to+-- structures over parameterized terms.+class FoldableFC (t :: (k -> *) -> l -> *) where+  {-# MINIMAL foldMapFC | foldrFC #-}++  -- | Map each element of the structure to a monoid,+  -- and combine the results.+  foldMapFC :: Monoid m => (forall s . e s -> m) -> t e c -> m+  foldMapFC f = foldrFC (mappend . f) mempty++  -- | Right-associative fold of a structure.+  foldrFC :: (forall s . e s -> b -> b) -> b -> t e c -> b+  foldrFC f z t = appEndo (foldMapFC (Endo #. f) t) z++  -- | Left-associative fold of a structure.+  foldlFC :: (forall s . b -> e s -> b) -> b -> t e c -> b+  foldlFC f z t = appEndo (getDual (foldMapFC (\e -> Dual (Endo (\r -> f r e))) t)) z++  -- | Right-associative fold of a structure,+  -- but with strict application of the operator.+  foldrFC' :: (forall s . e s -> b -> b) -> b -> t e c -> b+  foldrFC' f0 z0 xs = foldlFC (f' f0) id xs z0+    where f' f k x z = k $! f x z++  -- | Left-associative fold of a parameterized structure+  -- with a strict accumulator.+  foldlFC' :: (forall s . b -> e s -> b) -> b -> t e c -> b+  foldlFC' f0 z0 xs = foldrFC (f' f0) id xs z0+    where f' f x k z = k $! f z x++  -- | Convert structure to list.+  toListFC :: (forall tp . f tp -> a) -> t f c -> [a]+  toListFC f t = build (\c n -> foldrFC (\e v -> c (f e) v) n t)++-- | Return 'True' if all values satisfy predicate.+allFC :: FoldableFC t => (forall tp . f tp -> Bool) -> t f c -> Bool+allFC p = getAll #. foldMapFC (All #. p)++-- | Return 'True' if any values satisfy predicate.+anyFC :: FoldableFC t => (forall tp . f tp -> Bool) -> t f c -> Bool+anyFC p = getAny #. foldMapFC (Any #. p)++-- | Return number of elements in list.+lengthFC :: FoldableFC t => t e c -> Int+lengthFC = foldrFC (const (+1)) 0++------------------------------------------------------------------------+-- TraversableF++class (FunctorFC t, FoldableFC t) => TraversableFC t where+  traverseFC :: Applicative m+             => (forall s . e s -> m (f s))+             -> t e c+             -> m (t f c)++-- | This function may be used as a value for `fmapF` in a `FunctorF`+-- instance.+fmapFCDefault :: TraversableFC t => (forall s . e s -> f s) -> t e c -> t f c+fmapFCDefault = \f -> runIdentity . traverseFC (Identity . f)+{-# INLINE fmapFCDefault #-}++-- | This function may be used as a value for `Data.Foldable.foldMap`+-- in a `Foldable` instance.+foldMapFCDefault :: (TraversableFC t, Monoid m) => (forall s . e s -> m) -> t e c -> m+foldMapFCDefault = \f -> getConst . traverseFC (Const . f)+{-# INLINE foldMapFCDefault #-}++-- | Map each element of a structure to an action, evaluate+-- these actions from left to right, and ignore the results.+traverseFC_ :: (FoldableFC t, Applicative f) => (forall s . e s  -> f ()) -> t e c -> f ()+traverseFC_ f = foldrFC (\e r -> f e *> r) (pure ())+{-# INLINE traverseFC_ #-}++-- | Map each element of a structure to an action, evaluate+-- these actions from left to right, and ignore the results.+forMFC_ :: (FoldableFC t, Applicative f) => t e c -> (forall s . e s  -> f ()) -> f ()+forMFC_ v f = traverseFC_ f v+{-# INLINE forMFC_ #-}
+ src/Data/Parameterized/Utils/BinTree.hs view
@@ -0,0 +1,368 @@+{-|+Description      : Utilities for balanced binary trees.+Copyright        : (c) Galois, Inc 2014+Maintainer       : Joe Hendrix <jhendrix@galois.com>+-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE Safe #-}+module Data.Parameterized.Utils.BinTree+  ( MaybeS(..)+  , fromMaybeS+  , Updated(..)+  , updatedValue+  , TreeApp(..)+  , IsBinTree(..)+  , balanceL+  , balanceR+  , glue+  , merge+  , filterGt+  , filterLt+  , insert+  , delete+  , union+  , link+  , PairS(..)+  ) where++import Control.Applicative++------------------------------------------------------------------------+-- MaybeS++-- | A strict version of 'Maybe'+data MaybeS v+   = JustS !v+   | NothingS++instance Functor MaybeS where+  fmap _ NothingS = NothingS+  fmap f (JustS v) = JustS (f v)++instance Alternative MaybeS where+  empty = NothingS+  mv@JustS{} <|> _ = mv+  NothingS <|> v = v++instance Applicative MaybeS where+  pure = JustS++  NothingS <*> _ = NothingS+  JustS{} <*> NothingS = NothingS+  JustS f <*> JustS x = JustS (f x)++fromMaybeS :: a -> MaybeS a -> a+fromMaybeS r NothingS = r+fromMaybeS _ (JustS v) = v++------------------------------------------------------------------------+-- Updated++-- | Updated a contains a value that has been flagged on whether it was+-- modified by an operation.+data Updated a+   = Updated   !a+   | Unchanged !a++updatedValue :: Updated a -> a+updatedValue (Updated a) = a+updatedValue (Unchanged a) = a++------------------------------------------------------------------------+-- IsBinTree++data TreeApp e t+   = BinTree !e !t !t+   | TipTree++class IsBinTree t e | t -> e where+  asBin :: t -> TreeApp e t+  tip :: t++  bin :: e -> t -> t -> t+  size :: t -> Int++delta,ratio :: Int+delta = 3+ratio = 2++-- `balanceL p l r` returns a balanced tree for the sequence @l ++ [p] ++ r@.+--+-- It assumes that @l@ and @r@ are close to being balanced, and that only+-- @l@ may contain too many elements.+balanceL :: (IsBinTree c e) => e -> c -> c -> c+balanceL p l r = do+  case asBin l of+    BinTree l_pair ll lr | size l > max 1 (delta*size r) ->+      case asBin lr of+        BinTree lr_pair lrl lrr | size lr >= max 2 (ratio*size ll) ->+          bin lr_pair (bin l_pair ll lrl) (bin p lrr r)+        _ -> bin l_pair ll (bin p lr r)++    _ -> bin p l r+{-# INLINE balanceL #-}++-- `balanceR p l r` returns a balanced tree for the sequence @l ++ [p] ++ r@.+--+-- It assumes that @l@ and @r@ are close to being balanced, and that only+-- @r@ may contain too many elements.+balanceR :: (IsBinTree c e) => e -> c -> c -> c+balanceR p l r = do+  case asBin r of+    BinTree r_pair rl rr | size r > max 1 (delta*size l) ->+      case asBin rl of+        BinTree rl_pair rll rlr | size rl >= max 2 (ratio*size rr) ->+          (bin rl_pair $! bin p l rll) $! bin r_pair rlr rr+        _ -> bin r_pair (bin p l rl) rr+    _ -> bin p l r+{-# INLINE balanceR #-}++-- | Insert a new maximal element.+insertMax :: IsBinTree c e => e -> c -> c+insertMax p t =+  case asBin t of+    TipTree -> bin p tip tip+    BinTree q l r -> balanceR q l (insertMax p r)++-- | Insert a new minimal element.+insertMin :: IsBinTree c e => e -> c -> c+insertMin p t =+  case asBin t of+    TipTree -> bin p tip tip+    BinTree q l r -> balanceL q (insertMin p l) r++-- | link is called to insert a key and value between two disjoint subtrees.+link :: IsBinTree c e => e -> c -> c -> c+link p l r =+  case (asBin l, asBin r) of+    (TipTree, _) -> insertMin p r+    (_, TipTree) -> insertMax p l+    (BinTree py ly ry, BinTree pz lz rz)+     | delta*size l < size r -> balanceL pz (link p l lz) rz+     | delta*size r < size l -> balanceR py ly (link p ry r)+     | otherwise             -> bin p l r+{-# INLINE link #-}++-- | A Strict pair+data PairS f s = PairS !f !s++deleteFindMin :: IsBinTree c e => e -> c -> c -> PairS e c+deleteFindMin p l r =+  case asBin l of+    TipTree -> PairS p r+    BinTree lp ll lr ->+      case deleteFindMin lp ll lr of+        PairS q l' -> PairS q (balanceR p l' r)+{-# INLINABLE deleteFindMin #-}++deleteFindMax :: IsBinTree c e => e -> c -> c -> PairS e c+deleteFindMax p l r =+  case asBin r of+    TipTree -> PairS p l+    BinTree rp rl rr ->+      case deleteFindMax rp rl rr of+        PairS q r' -> PairS q (balanceL p l r')+{-# INLINABLE deleteFindMax #-}++-- | Concatenate two trees that are ordered with respect to each other.+merge :: IsBinTree c e => c -> c -> c+merge l r =+  case (asBin l, asBin r) of+    (TipTree, _) -> r+    (_, TipTree) -> l+    (BinTree x lx rx, BinTree y ly ry)+      | delta*size l < size r -> balanceL y (merge l ly) ry+      | delta*size r < size l -> balanceR x lx (merge rx r)+      | size l > size r ->+        case deleteFindMax x lx rx of+          PairS q l' -> balanceR q l' r+      | otherwise ->+        case deleteFindMin y ly ry of+          PairS q r' -> balanceL q l r'+{-# INLINABLE merge #-}++------------------------------------------------------------------------+-- Ordered operations++-- | @insert p m@ inserts the binding into @m@.  It returns+-- an Unchanged value if the map stays the same size and an updated+-- value if a new entry was inserted.+insert :: (IsBinTree c e) => (e -> e -> Ordering) -> e -> c -> Updated c+insert comp x t =+  case asBin t of+    TipTree -> Updated (bin x tip tip)+    BinTree y l r ->+      case comp x y of+        LT ->+          case insert comp x l of+            Updated l'   -> Updated   (balanceL y l' r)+            Unchanged l' -> Unchanged (bin       y l' r)+        GT ->+          case insert comp x r of+            Updated r'   -> Updated   (balanceR y l r')+            Unchanged r' -> Unchanged (bin       y l r')+        EQ -> Unchanged (bin x l r)+{-# INLINABLE insert #-}++-- | 'glue l r' concatenates @l@ and @r@.+--+-- It assumes that @l@ and @r@ are already balanced with respect to each other.+glue :: IsBinTree c e => c -> c -> c+glue l r =+  case (asBin l, asBin r) of+    (TipTree, _) -> r+    (_, TipTree) -> l+    (BinTree x lx rx, BinTree y ly ry)+     | size l > size r ->+       case deleteFindMax x lx rx of+         PairS q l' -> balanceR q l' r+     | otherwise ->+       case deleteFindMin y ly ry of+         PairS q r' -> balanceL q l r'+{-# INLINABLE glue #-}++delete :: IsBinTree c e+       => (e -> Ordering)+          -- ^ Predicate that returns whether the entry is less than, greater than, or equal+          -- to the key we are entry that we are looking for.+       -> c+       -> MaybeS c+delete k t =+  case asBin t of+    TipTree -> NothingS+    BinTree p l r ->+      case k p of+        LT -> (\l' -> balanceR p l' r) <$> delete k l+        GT -> (\r' -> balanceL p l r') <$> delete k r+        EQ -> JustS (glue l r)+{-# INLINABLE delete #-}++------------------------------------------------------------------------+-- filter++-- | Returns only entries that are less than predicate with respect to the ordering+-- and Nothing if no elements are discared.+filterGt :: IsBinTree c e => (e -> Ordering) -> c -> MaybeS c+filterGt k t =+  case asBin t of+    TipTree -> NothingS+    BinTree x l r ->+      case k x of+        LT -> (\l' -> link x l' r) <$> filterGt k l+        GT -> filterGt k r <|> JustS r+        EQ -> JustS r+{-# INLINABLE filterGt #-}+++-- | @filterLt' k m@ returns submap of @m@ that only contains entries+-- that are smaller than @k@.  If no entries are deleted then return Nothing.+filterLt :: IsBinTree c e => (e -> Ordering) -> c -> MaybeS c+filterLt k t =+  case asBin t of+    TipTree -> NothingS+    BinTree x l r ->+      case k x of+        LT -> filterLt k l <|> JustS l+        GT -> (\r' -> link x l r') <$> filterLt k r+        EQ -> JustS l+{-# INLINABLE filterLt #-}++------------------------------------------------------------------------+-- Union++-- Insert a new key and value in the map if it is not already present.+-- Used by `union`.+insertR :: forall c e . (IsBinTree c e) => (e -> e -> Ordering) -> e -> c -> c+insertR comp e m = fromMaybeS m (go e m)+  where+    go :: e -> c -> MaybeS c+    go x t =+      case asBin t of+        TipTree -> JustS (bin x tip tip)+        BinTree y l r ->+          case comp x y of+            LT -> (\l' -> balanceL y l' r) <$> go x l+            GT -> (\r' -> balanceR y l r') <$> go x r+            EQ -> NothingS+{-# INLINABLE insertR #-}++-- | Union two sets+union :: (IsBinTree c e) => (e -> e -> Ordering) -> c -> c -> c+union comp t1 t2 =+  case (asBin t1, asBin t2) of+    (TipTree, _) -> t2+    (_, TipTree) -> t1+    (_, BinTree p (asBin -> TipTree) (asBin -> TipTree)) -> insertR comp p t1+    (BinTree x l r, _) ->+      link x+           (hedgeUnion_UB comp x   l t2)+           (hedgeUnion_LB comp x r   t2)+{-# INLINABLE union #-}++-- | Hedge union where we only add elements in second map if key is+-- strictly above a lower bound.+hedgeUnion_LB :: (IsBinTree c e) => (e -> e -> Ordering) -> e -> c -> c -> c+hedgeUnion_LB comp lo t1 t2 =+  case (asBin t1, asBin t2) of+    (_, TipTree) -> t1+    (TipTree, _) -> fromMaybeS t2 (filterGt (comp lo) t2)+    -- Prune left tree.+    (_, BinTree k _ r) | comp k lo <= EQ -> hedgeUnion_LB comp lo t1 r+    -- Special case when t2 is a single element.+    (_, BinTree x (asBin -> TipTree) (asBin -> TipTree)) -> insertR comp x t1+    -- Split on left-and-right subtrees of t1.+    (BinTree x l r, _) ->+      link x+           (hedgeUnion_LB_UB comp lo x  l t2)+           (hedgeUnion_LB    comp x     r t2)+{-# INLINABLE hedgeUnion_LB #-}++-- | Hedge union where we only add elements in second map if key is+-- strictly below a upper bound.+hedgeUnion_UB :: (IsBinTree c e) => (e -> e -> Ordering) -> e -> c -> c -> c+hedgeUnion_UB comp hi t1 t2 =+  case (asBin t1, asBin t2) of+    (_, TipTree) -> t1+    (TipTree, _) -> fromMaybeS t2 (filterLt (comp hi) t2)+    -- Prune right tree.+    (_, BinTree x l _) | comp x hi >= EQ -> hedgeUnion_UB comp hi t1 l+    -- Special case when t2 is a single element.+    (_, BinTree x (asBin -> TipTree) (asBin -> TipTree))  -> insertR comp x t1+    -- Split on left-and-right subtrees of t1.+    (BinTree x l r, _) ->+      link x+           (hedgeUnion_UB    comp x      l t2)+           (hedgeUnion_LB_UB comp x  hi  r t2)+{-# INLINABLE hedgeUnion_UB #-}++-- | Hedge union where we only add elements in second map if key is+-- strictly between a lower and upper bound.+hedgeUnion_LB_UB :: (IsBinTree c e) => (e -> e -> Ordering) -> e -> e -> c -> c -> c+hedgeUnion_LB_UB comp lo hi t1 t2 =+  case (asBin t1, asBin t2) of+    (_, TipTree) -> t1+    -- Prune left tree.+    (_,   BinTree k _ r) | comp k lo <= EQ -> hedgeUnion_LB_UB comp lo hi t1 r+    -- Prune right tree.+    (_,   BinTree k l _) | comp k hi >= EQ -> hedgeUnion_LB_UB comp lo hi t1 l+    -- When t1 becomes empty (assumes lo <= k <= hi)+    (TipTree, BinTree x l r) ->+      case (filterGt (comp lo) l, filterLt (comp hi) r) of+        -- No variables in t2 were eliminated.+        (NothingS, NothingS) -> t2+        -- Relink t2 with filtered elements removed.+        (l',r') -> link x (fromMaybeS l l') (fromMaybeS r r')+    -- Special case when t2 is a single element.+    (_, BinTree x (asBin -> TipTree) (asBin -> TipTree)) -> insertR comp x t1+    -- Split on left-and-right subtrees of t1.+    (BinTree x l r, _) ->+      link x+           (hedgeUnion_LB_UB comp lo x  l t2)+           (hedgeUnion_LB_UB comp x  hi r t2)+{-# INLINABLE hedgeUnion_LB_UB #-}
+ test/Test/Context.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+module Test.Context+( contextTests+) where++import Test.Tasty+import Test.QuickCheck+import Test.Tasty.QuickCheck++import Data.Parameterized.Classes+import Data.Parameterized.TraversableFC+import Data.Parameterized.Some++import qualified Data.Parameterized.Context.Safe as S+import qualified Data.Parameterized.Context.Unsafe as U++data Payload (ty :: *) where+  IntPayload    :: Int -> Payload Int+  StringPayload :: String -> Payload String+  BoolPayload   :: Bool -> Payload Bool++instance TestEquality Payload where+  testEquality (IntPayload x) (IntPayload y) = if x == y then Just Refl else Nothing+  testEquality (StringPayload x) (StringPayload y) = if x == y then Just Refl else Nothing+  testEquality (BoolPayload x) (BoolPayload y) = if x == y then Just Refl else Nothing+  testEquality _ _ = Nothing++instance Show (Payload tp) where+  show (IntPayload x) = show x+  show (StringPayload x) = show x+  show (BoolPayload x) = show x++instance ShowF Payload++instance Arbitrary (Some Payload) where+  arbitrary = oneof+    [ Some . IntPayload <$> arbitrary+    , Some . StringPayload <$> arbitrary+    , Some . BoolPayload <$> arbitrary+    ]++type UAsgn = U.Assignment Payload+type SAsgn = S.Assignment Payload++mkUAsgn :: [Some Payload] -> Some UAsgn+mkUAsgn = go U.empty+ where go :: UAsgn ctx -> [Some Payload] -> Some UAsgn+       go a [] = Some a+       go a (Some x : xs) = go (U.extend a x) xs++mkSAsgn :: [Some Payload] -> Some SAsgn+mkSAsgn = go S.empty+ where go :: SAsgn ctx -> [Some Payload] -> Some SAsgn+       go a [] = Some a+       go a (Some x : xs) = go (S.extend a x) xs++instance Arbitrary (Some UAsgn) where+  arbitrary = mkUAsgn <$> arbitrary+instance Arbitrary (Some SAsgn) where+  arbitrary = mkSAsgn <$> arbitrary++twiddle :: Payload a -> Payload a+twiddle (IntPayload n) = IntPayload (n+1)+twiddle (StringPayload str) = StringPayload (str++"asdf")+twiddle (BoolPayload b) = BoolPayload (not b)++contextTests :: IO TestTree+contextTests = testGroup "Context" <$> return+   [ testProperty "safe_index_eq" $ \v vs i -> ioProperty $ do+         let vals = v:vs+         let i' = min (max 0 i) (length vals - 1)+         Some a <- return $ mkSAsgn vals+         Just (Some idx) <- return $ S.intIndex i' (S.size a)+         return (Some (a S.! idx) == vals !! i')+   , testProperty "unsafe_index_eq" $ \v vs i -> ioProperty $ do+         let vals = v:vs+         let i' = min (max 0 i) (length vals - 1)+         Some a <- return $ mkUAsgn vals+         Just (Some idx) <- return $ U.intIndex i' (U.size a)+         return (Some (a U.! idx) == vals !! i')+   , testProperty "safe_tolist" $ \vals -> ioProperty $ do+         Some a <- return $ mkSAsgn vals+         let vals' = toListFC Some a+         return (vals == vals')+   , testProperty "unsafe_tolist" $ \vals -> ioProperty $ do+         Some a <- return $ mkUAsgn vals+         let vals' = toListFC Some a+         return (vals == vals')+   , testProperty "adjust_test" $ \v vs i -> ioProperty $ do+         let vals = v:vs+         Some x <- return $ mkUAsgn vals+         Some y <- return $ mkSAsgn vals+         let i' = min (max 0 i) (length vals - 1)++         Just (Some idx_x) <- return $ U.intIndex i' (U.size x)+         Just (Some idx_y) <- return $ S.intIndex i' (S.size y)++         let x' = U.adjust twiddle idx_x x+         let y' = S.adjust twiddle idx_y y++         return (toListFC Some x' == toListFC Some y')+   , testProperty "safe_eq" $ \vals1 vals2 -> ioProperty $ do+         Some x <- return $ mkSAsgn vals1+         Some y <- return $ mkSAsgn vals2+         case testEquality x y of+           Just Refl -> return $ vals1 == vals2+           Nothing   -> return $ vals1 /= vals2+   , testProperty "unsafe_eq" $ \vals1 vals2 -> ioProperty $ do+         Some x <- return $ mkUAsgn vals1+         Some y <- return $ mkUAsgn vals2+         case testEquality x y of+           Just Refl -> return $ vals1 == vals2+           Nothing   -> return $ vals1 /= vals2+   ]
+ test/Test/NatRepr.hs view
@@ -0,0 +1,18 @@+module Test.NatRepr+( natTests+) where++import Test.Tasty+import Test.Tasty.QuickCheck++import Data.Parameterized.NatRepr+import Data.Parameterized.Some+import GHC.TypeLits++natTests :: IO TestTree+natTests = testGroup "Nat" <$> return+  [ testProperty "withKnownNat" $ \nInt ->+      case someNat nInt of+        Nothing -> nInt < 0+        Just (Some r) -> nInt == withKnownNat r (natVal r)+  ]
+ test/UnitTest.hs view
@@ -0,0 +1,22 @@+import Test.Tasty+import Test.Tasty.Ingredients+import Test.Tasty.Runners.AntXML++import qualified Test.Context+import qualified Test.NatRepr++main :: IO ()+main = tests >>= defaultMainWithIngredients ingrs++ingrs :: [Ingredient]+ingrs =+   [ antXMLRunner+   ]+   +++   defaultIngredients++tests :: IO TestTree+tests = testGroup "ParameterizedUtils" <$> sequence+  [ Test.Context.contextTests+  , Test.NatRepr.natTests+  ]