packages feed

membership (empty) → 0

raw patch · 7 files changed

+441/−0 lines, 7 filesdep +basedep +constraintsdep +deepseqsetup-changed

Dependencies added: base, constraints, deepseq, hashable, prettyprinter, template-haskell, th-lift

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for membership++## 0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2019, Fumiaki Kinoshita++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 Fumiaki Kinoshita nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ membership.cabal view
@@ -0,0 +1,36 @@+cabal-version:       2.2+-- Initial package description 'membership.cabal' generated by 'cabal+-- init'.  For further documentation, see+-- http://haskell.org/cabal/users-guide/++name:                membership+version:             0+synopsis:            Indices for type level lists+description:         See README+homepage:            https://github.com/fumieval/membership+-- bug-reports:+license:             BSD-3-Clause+license-file:        LICENSE+author:              Fumiaki Kinoshita+maintainer:          fumiexcel@gmail.com+-- copyright:+category:            Type+extra-source-files:  CHANGELOG.md++library+  exposed-modules:+    Type.Membership+    Type.Membership.Internal+    Type.Membership.HList+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.9.0.0 && <5+    , deepseq+    , constraints+    , hashable+    , template-haskell+    , th-lift+    , prettyprinter+  hs-source-dirs:      src+  ghc-options: -Wall -Wcompat+  default-language:    Haskell2010
+ src/Type/Membership.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+module Type.Membership+  ( -- * Membership+  Membership+  , getMemberId+  , mkMembership+  , leadership+  , nextMembership+  , testMembership+  , compareMembership+  , impossibleMembership+  , reifyMembership+  , Member+  -- * Association+  , Assoc(..)+  , type (>:)+  , Lookup(..)+  , KeyOf+  , KeyIs+  , proxyKeyOf+  , stringKeyOf+  , TargetOf+  , proxyTargetOf+  , TargetIs+  , KeyTargetAre+  -- * Enumeration+  , Generate(..)+  , Forall(..)+  , ForallF+  -- * Reexports+  , Proxy(..)+  ) where++import Data.Constraint+import Data.Proxy+import Data.String+import GHC.TypeLits+import Type.Membership.Internal++-- | Every type-level list is an instance of 'Generate'.+class Generate (xs :: [k]) where+  -- | Enumerate all possible 'Membership's of @xs@.+  henumerate :: (forall x. Membership xs x -> r -> r) -> r -> r++  -- | Count the number of memberships.+  hcount :: proxy xs -> Int++  -- | Enumerate 'Membership's and construct an 'HList'.+  hgenerateList :: Applicative f+    => (forall x. Membership xs x -> f (h x)) -> f (HList h xs)++instance Generate '[] where+  henumerate _ r = r++  hcount _ = 0++  hgenerateList _ = pure HNil++instance Generate xs => Generate (x ': xs) where+  henumerate f r = f leadership $ henumerate (f . nextMembership) r++  hcount _ = 1 + hcount (Proxy :: Proxy xs)++  -- | Enumerate 'Membership's and construct an 'HList'.+  hgenerateList f = HCons <$> f leadership <*> hgenerateList (f . nextMembership)++-- | Every element in @xs@ satisfies @c@+class (ForallF c xs, Generate xs) => Forall (c :: k -> Constraint) (xs :: [k]) where+  -- | Enumerate all possible 'Membership's of @xs@ with an additional context.+  henumerateFor :: proxy c -> proxy' xs -> (forall x. c x => Membership xs x -> r -> r) -> r -> r++  hgenerateListFor :: Applicative f+    => proxy c -> (forall x. c x => Membership xs x -> f (h x)) -> f (HList h xs)++instance Forall c '[] where+  henumerateFor _ _ _ r = r++  hgenerateListFor _ _ = pure HNil++instance (c x, Forall c xs) => Forall c (x ': xs) where+  henumerateFor p _ f r = f leadership $ henumerateFor p (Proxy :: Proxy xs) (f . nextMembership) r++  hgenerateListFor p f = HCons <$> f leadership <*> hgenerateListFor p (f . nextMembership)++-- | HACK: Without this, the constraints are not propagated well.+type family ForallF (c :: k -> Constraint) (xs :: [k]) :: Constraint where+  ForallF c '[] = ()+  ForallF c (x ': xs) = (c x, Forall c xs)++-- | Take the type of the key+type family KeyOf (kv :: Assoc k v) :: k where+  KeyOf (k ':> v) = k++-- | Proxy-level 'KeyOf'. This is useful when using 'symbolVal'.+proxyKeyOf :: proxy kv -> Proxy (KeyOf kv)+proxyKeyOf _ = Proxy++-- | Get a string from a proxy of @'Assoc' 'Symbol' v@.+stringKeyOf :: (IsString a, KnownSymbol (KeyOf kv)) => proxy kv -> a+stringKeyOf = fromString . symbolVal . proxyKeyOf+{-# INLINE stringKeyOf #-}++-- | Constraint applied to 'KeyOf'+class (pk (KeyOf kv)) => KeyIs pk kv where++instance (pk k) => KeyIs pk (k ':> v)++-- | Take the type of the value+type family TargetOf (kv :: Assoc k v) :: v where+  TargetOf (k ':> v) = v++-- | Proxy-level 'TargetOf'.+proxyTargetOf :: proxy kv -> Proxy (TargetOf kv)+proxyTargetOf _ = Proxy++-- | Constraint applied to 'TargetOf'+class (pv (TargetOf kv)) => TargetIs pv kv where++instance (pv v) => TargetIs pv (k ':> v)++-- | Combined constraint for 'Assoc'+class (pk (KeyOf kv), pv (TargetOf kv)) => KeyTargetAre pk pv kv where++instance (pk k, pv v) => KeyTargetAre pk pv (k ':> v)
+ src/Type/Membership/HList.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Type.Membership.HList (HList(..)+  , hindex+  , hfoldrWithIndex+  , htraverse+  , htraverseWithIndex+  , hlength) where++import Type.Membership.Internal++hindex :: HList h xs -> Membership xs x -> h x+hindex HNil _ = error "impossible"+hindex (HCons x xs) m = testMembership m (\Refl -> x) (hindex xs)++htraverse :: forall f g h xs. Applicative f => (forall x. g x -> f (h x)) -> HList g xs -> f (HList h xs)+htraverse f = go where+  go :: HList g ts -> f (HList h ts)+  go HNil = pure HNil+  go (HCons h xs) = HCons <$> f h <*> go xs+{-# INLINE htraverse #-}++hlength :: HList h xs -> Int+hlength = go 0 where+  go :: Int -> HList h xs -> Int+  go n HNil = n+  go n (HCons _ xs) = let !n' = n + 1 in go n' xs+{-# INLINE hlength #-}
+ src/Type/Membership/Internal.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Type.Membership.Internal (+  -- * Membership+  Membership+  , getMemberId+  , mkMembership+  , leadership+  , nextMembership+  , testMembership+  , compareMembership+  , impossibleMembership+  , reifyMembership+  -- * Member class+  , Member(..)+  , type (∈)+  , FindType+  -- * Association+  , Assoc(..)+  , type (>:)+  , Lookup(..)+  , FindAssoc+  -- * Sugar+  , Elaborate+  , Elaborated(..)+  -- * HList+  , HList(..)+  , hfoldrWithIndex+  , htraverseWithIndex+  -- * Miscellaneous+  , module Data.Type.Equality+  , module Data.Proxy+  ) where+import Control.DeepSeq (NFData)+import Data.Type.Equality+import Data.Proxy+import Control.Monad+import Unsafe.Coerce+import Data.Hashable+import Data.Text.Prettyprint.Doc+import Data.Typeable+import Language.Haskell.TH hiding (Pred)+import Language.Haskell.TH.Lift+import Data.Semigroup (Semigroup(..))+import GHC.TypeLits++-- | A witness that of @x@ is a member of a type level set @xs@.+newtype Membership (xs :: [k]) (x :: k) = Membership+  { getMemberId :: Int -- ^ get the position as an 'Int'.+  } deriving (Typeable, NFData)++instance Show (Membership xs x) where+  show (Membership n) = "$(mkMembership " ++ show n ++ ")"++instance Pretty (Membership xs x) where+  pretty (Membership n) = "$(mkMembership " <> pretty n <> ")"++instance Eq (Membership xs x) where+  _ == _ = True++instance Ord (Membership xs x) where+  compare _ _ = EQ++instance Semigroup (Membership xs x) where+  x <> _ = x++-- | Generates a 'Membership' that corresponds to the given ordinal (0-origin).+mkMembership :: Int -> Q Exp+mkMembership n = do+  let names = map mkName $ take (n + 1) $ concatMap (flip replicateM ['a'..'z']) [1..]+  let rest = mkName "any"+  let cons x xs = PromotedConsT `AppT` x `AppT` xs+  let t = foldr cons (VarT rest) (map VarT names)+  sigE (conE 'Membership `appE` litE (IntegerL $ toInteger n))+    $ forallT (PlainTV rest : map PlainTV names) (pure [])+    $ conT ''Membership `appT` pure t `appT` varT (names !! n)++instance Lift (Membership xs x) where+  lift (Membership i) = mkMembership i++-- | @x@ is a member of @xs@+class Member xs x where+  membership :: Membership xs x++instance (Elaborate x (FindType x xs) ~ 'Expecting pos, KnownNat pos) => Member xs x where+  membership = Membership (fromInteger $ natVal (Proxy :: Proxy pos))+  {-# INLINE membership #-}++instance Hashable (Membership xs x) where+  hashWithSalt s = hashWithSalt s . getMemberId++-- | A readable type search result+data Elaborated k v = Expecting v | Missing k | Duplicate k++-- | Make the result more readable+type family Elaborate (key :: k) (xs :: [v]) :: Elaborated k v where+  Elaborate k '[] = 'Missing k+  Elaborate k '[x] = 'Expecting x+  Elaborate k xs = 'Duplicate k++-- | Find a type associated to the specified key.+type family FindAssoc (n :: Nat) (key :: k) (xs :: [Assoc k v]) where+  FindAssoc n k ((k ':> v) ': xs) = (n ':> v) ': FindAssoc (1 + n) k xs+  FindAssoc n k ((k' ':> v) ': xs) = FindAssoc (1 + n) k xs+  FindAssoc n k '[] = '[]++-- | Embodies a type equivalence to ensure that the 'Membership' points the first element.+testMembership :: Membership (y ': xs) x -> (x :~: y -> r) -> (Membership xs x -> r) -> r+testMembership (Membership 0) l _ = l (unsafeCoerce Refl)+testMembership (Membership n) _ r = r (Membership (n - 1))+{-# INLINE testMembership #-}++-- | Compare two 'Membership's.+compareMembership :: Membership xs x -> Membership xs y -> Either Ordering (x :~: y)+compareMembership (Membership m) (Membership n) = case compare m n of+  EQ -> Right (unsafeCoerce Refl)+  x -> Left x+{-# INLINE compareMembership #-}++-- | There is no 'Membership' of an empty list.+impossibleMembership :: Membership '[] x -> r+impossibleMembership _ = error "Impossible"++-- | This 'Membership' points to the first element+leadership :: Membership (x ': xs) x+leadership = Membership 0+{-# INLINE leadership #-}++-- | The next membership+nextMembership :: Membership xs y -> Membership (x ': xs) y+nextMembership (Membership n) = Membership (n + 1)+{-# INLINE nextMembership #-}++-- | Make up a 'Membership' from an integer.+reifyMembership :: Int -> (forall x. Membership xs x -> r) -> r+reifyMembership n k = k (Membership n)++-- | Unicode flipped alias for 'Member'+type x ∈ xs = Member xs x++-- | FindType types+type family FindType (x :: k) (xs :: [k]) :: [Nat] where+  FindType x (x ': xs) = 0 ': MapSucc (FindType x xs)+  FindType x (y ': ys) = MapSucc (FindType x ys)+  FindType x '[] = '[]++-- | Ideally, it will be 'Map Succ'+type family MapSucc (xs :: [Nat]) :: [Nat] where+  MapSucc '[] = '[]+  MapSucc (x ': xs) = (1 + x) ': MapSucc xs++-- | The kind of key-value pairs+data Assoc k v = k :> v+infix 0 :>++-- | A synonym for (':>')+type (>:) = '(:>)++-- | @'Lookup' xs k v@ is essentially identical to @(k :> v) ∈ xs@+-- , but the type @v@ is inferred from @k@ and @xs@.+class Lookup xs k v | k xs -> v where+  association :: Membership xs (k ':> v)++instance (Elaborate k (FindAssoc 0 k xs) ~ 'Expecting (n ':> v), KnownNat n) => Lookup xs k v where+  association = Membership (fromInteger $ natVal (Proxy :: Proxy n))++data HList (h :: k -> *) (xs :: [k]) where+  HNil :: HList h '[]+  HCons :: h x -> HList h xs -> HList h (x ': xs)++infixr 5 `HCons`++hfoldrWithIndex :: forall h r xs. (forall x. Membership xs x -> h x -> r -> r) -> r -> HList h xs -> r+hfoldrWithIndex f r = go 0 where+  go :: Int -> HList h t -> r+  go !k (HCons x xs) = f (Membership k) x $ go (k + 1) xs+  go _ HNil = r+{-# INLINE hfoldrWithIndex #-}++htraverseWithIndex :: forall f g h xs. Applicative f+    => (forall x. Membership xs x -> g x -> f (h x)) -> HList g xs -> f (HList h xs)+htraverseWithIndex f = go 0 where+  go :: Int -> HList g t -> f (HList h t)+  go !k (HCons x xs) = HCons <$> f (Membership k) x <*> go (k + 1) xs+  go _ HNil = pure HNil+{-# INLINE htraverseWithIndex #-}