1- {-# LANGUAGE PolyKinds, StandaloneDeriving, UndecidableInstances #-}
1+ {-# LANGUAGE PatternSynonyms #-}
2+ {-# LANGUAGE PolyKinds #-}
3+ {-# LANGUAGE StandaloneDeriving #-}
4+ {-# LANGUAGE UndecidableInstances #-}
5+ {-# LANGUAGE ViewPatterns #-}
6+
27-- | n-ary products (and products of products)
38module Data.SOP.NP
49 ( -- * Datatypes
5- NP (.. )
10+ NP (.. , Nil , (:*) )
611 , POP (.. )
712 , unPOP
813 -- * Constructing products
@@ -90,6 +95,8 @@ module Data.SOP.NP
9095import Data.Coerce
9196import Data.Kind (Type )
9297import Data.Proxy (Proxy (.. ))
98+ import qualified Data.Vector as V
99+ import GHC.Exts (Any )
93100import Unsafe.Coerce
94101#if !MIN_VERSION_base(4,11,0)
95102import Data.Semigroup (Semigroup (.. ))
@@ -110,8 +117,14 @@ import Data.SOP.Sing
110117-- @i@-th element of the list is of type @x@, then the @i@-th
111118-- element of the product is of type @f x@.
112119--
113- -- The constructor names are chosen to resemble the names of the
114- -- list constructors.
120+ -- The pattern synoyms are chosen to resemble the names of the
121+ -- list constructors. @NP@ is morally equivalent to:
122+ --
123+ -- > data NP :: (k -> Type) -> [k] -> Type where
124+ -- > Nil :: NP f '[]
125+ -- > (:*) :: f x -> NP f xs -> NP f (x ': xs)
126+ --
127+ -- The actual representation however is compact, using an array.
115128--
116129-- Two common instantiations of @f@ are the identity functor 'I'
117130-- and the constant functor 'K'. For 'I', the product becomes a
@@ -130,12 +143,40 @@ import Data.SOP.Sing
130143-- > K 0 :* K 1 :* Nil :: NP (K Int) '[ Char, Bool ]
131144-- > Just 'x' :* Nothing :* Nil :: NP Maybe '[ Char, Bool ]
132145--
133- data NP :: (k -> Type ) -> [k ] -> Type where
134- Nil :: NP f '[]
135- (:*) :: f x -> NP f xs -> NP f (x ': xs )
146+ newtype NP (f :: k -> * ) (xs :: [k ]) = NP (V. Vector Any )
147+
148+ -- | View on NP
149+ --
150+ -- This is only used internally, for the definition of the pattern synonyms.
151+ data ViewNP (f :: k -> * ) (xs :: [k ]) where
152+ IsNil :: ViewNP f '[]
153+ IsCons :: f x -> NP f xs -> ViewNP f (x ': xs )
154+
155+ -- | Construct 'ViewNP'
156+ --
157+ -- NOTE: 'V.unsafeTail' is O(1).
158+ viewNP :: NP f xs -> ViewNP f xs
159+ viewNP (NP xs)
160+ | null xs = unsafeCoerce $ IsNil
161+ | otherwise = unsafeCoerce $ IsCons (unsafeCoerce (V. unsafeHead xs))
162+ (NP (V. unsafeTail xs))
136163
164+ pattern Nil :: forall f xs . () => (xs ~ '[] ) => NP f xs
165+ pattern Nil <- (viewNP -> IsNil )
166+ where
167+ Nil = NP V. empty
168+
169+ pattern (:*) :: forall f xs' . ()
170+ => forall x xs . (xs' ~ (x ': xs)) => f x -> NP f xs -> NP f xs'
171+ pattern x :* xs <- (viewNP -> IsCons x xs)
172+ where
173+ x :* NP xs = NP (V. cons (unsafeCoerce x) xs)
137174infixr 5 :*
138175
176+ #if __GLASGOW_HASKELL__ >= 802
177+ {-# COMPLETE Nil, (:*) #-}
178+ #endif
179+
139180-- This is written manually,
140181-- because built-in deriving doesn't use associativity information!
141182instance All (Show `Compose ` f ) xs => Show (NP f xs ) where
@@ -145,8 +186,13 @@ instance All (Show `Compose` f) xs => Show (NP f xs) where
145186 . showString " :* "
146187 . showsPrec 5 fs
147188
148- deriving instance All (Eq `Compose ` f ) xs => Eq (NP f xs )
149- deriving instance (All (Eq `Compose ` f ) xs , All (Ord `Compose ` f ) xs ) => Ord (NP f xs )
189+ instance All (Eq `Compose ` f ) xs => Eq (NP f xs ) where
190+ xs == ys =
191+ and (hcollapse (hczipWith (Proxy :: Proxy (Eq `Compose ` f )) (\ x y -> K (x == y)) xs ys))
192+
193+ instance (All (Eq `Compose ` f ) xs , All (Ord `Compose ` f ) xs ) => Ord (NP f xs ) where
194+ compare xs ys =
195+ mconcat (hcollapse (hczipWith (Proxy :: Proxy (Ord `Compose ` f )) (\ x y -> K (compare x y)) xs ys))
150196
151197-- | @since 0.4.0.0
152198instance All (Semigroup `Compose ` f ) xs => Semigroup (NP f xs ) where
0 commit comments