Skip to content

Commit 82fda7d

Browse files
committed
Revive compact representation of NP
1 parent dcac014 commit 82fda7d

File tree

2 files changed

+57
-10
lines changed

2 files changed

+57
-10
lines changed

sop-core/sop-core.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ library
4242
Data.SOP.NS
4343
Data.SOP.Sing
4444
build-depends: base >= 4.9 && < 4.16,
45-
deepseq >= 1.3 && < 1.5
45+
deepseq >= 1.3 && < 1.5,
46+
vector >= 0.12 && < 0.13
4647
hs-source-dirs: src
4748
default-language: Haskell2010
4849
ghc-options: -Wall

sop-core/src/Data/SOP/NP.hs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
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)
38
module 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
9095
import Data.Coerce
9196
import Data.Kind (Type)
9297
import Data.Proxy (Proxy(..))
98+
import qualified Data.Vector as V
99+
import GHC.Exts (Any)
93100
import Unsafe.Coerce
94101
#if !MIN_VERSION_base(4,11,0)
95102
import 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)
137174
infixr 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!
141182
instance 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
152198
instance All (Semigroup `Compose` f) xs => Semigroup (NP f xs) where

0 commit comments

Comments
 (0)