on master help sign in
Implement union
commit0e3d5a897d2a9fedf0ad4c1eb3526001877c03df authorSridhar Ratnakumar <srid@srid.ca> dateSat 22 Aug 2020, 14:25 (6 years ago) parentf59a722
3 files changed+50−11
src/JRec.hs+4−6 src/JRec/Internal.hs+42 test/JRecSpec.hs+4−5
src/JRec.hs+4−6
@@ -42,7 +42,7 @@ unField _ (_ R.:= value) = value
4242
4343-- Appends records, without removing duplicates.
4444--
45−-- Left-biased. Does not sort.
45+-- FIXME: see spec
4646append ::
4747 forall lhs rhs res.
4848 ( KnownNat (R.RecSize lhs),
@@ -57,24 +57,22 @@ append ::
5757 Rec res
5858append = R.combine
5959
60−-- Merges records, removing duplicates (TODO)
60+-- Merges records, removing duplicates
6161--
6262-- Left-biased. Does not sort.
63−--
64−-- FIXME: This doesn't remove duplicates?
6563union ::
6664 forall lhs rhs res.
6765 ( KnownNat (R.RecSize lhs),
6866 KnownNat (R.RecSize rhs),
6967 KnownNat (R.RecSize lhs + R.RecSize rhs),
70− res ~ R.RecAppend lhs rhs,
68+ res ~ R.Union lhs rhs,
7169 R.RecCopy lhs lhs res,
7270 R.RecCopy rhs rhs res
7371 ) =>
7472 Rec lhs ->
7573 Rec rhs ->
7674 Rec res
77−union = R.combine
75+union = R.union
7876
7977----------------------------------------------------------------------------
8078-- Generic
src/JRec/Internal.hs+42
@@ -221,6 +221,15 @@ type family KeyDoesNotExist (l :: Symbol) (lts :: [*]) :: Constraint where
221221 )
222222 KeyDoesNotExist q (l := t ': lts) = KeyDoesNotExist q lts
223223
224+type family Insert (a :: *) (xs :: [*]) where
225+ Insert (a := t) '[] = a := t ': '[]
226+ Insert (a := t1) (a := t2 ': xs) = a := t1 ': xs
227+ Insert (a := t1) (x := t2 ': xs) = x := t2 ': Insert (a := t1) xs
228+
229+type family Union xs ys where
230+ Union xs '[] = xs
231+ Union xs (y := t ': ys) = Union (Insert (y := t) xs) ys
232+
224233type family RecAppend lhs rhs where
225234 RecAppend '[] rhs = rhs
226235 RecAppend (x := t ': xs) rhs = x := t ': RecAppend xs rhs
@@ -384,6 +393,39 @@ combine lts rts =
384393 (# s''''#, a# #) -> (# s''''#, MkRec a# #)
385394{-# INLINE combine #-}
386395
396+-- | Union two records
397+--
398+-- NOTE: changed from original superrecord to not require a 'Sort'
399+union ::
400+ forall lhs rhs res.
401+ ( KnownNat (RecSize lhs),
402+ KnownNat (RecSize rhs),
403+ KnownNat (RecSize lhs + RecSize rhs),
404+ res ~ Union lhs rhs,
405+ RecCopy lhs lhs res,
406+ RecCopy rhs rhs res
407+ ) =>
408+ Rec lhs ->
409+ Rec rhs ->
410+ Rec res
411+union lts rts =
412+ let !(I# size#) =
413+ fromIntegral $ natVal' (proxy# :: Proxy# (RecSize lhs + RecSize rhs))
414+ in runST' $
415+ ST $ \s# ->
416+ case newSmallArray# size# (error "No value") s# of
417+ (# s'#, arr# #) ->
418+ -- Copy rhs first, so that lhs can override later so as to retain
419+ -- the left-biased semantics of union.
420+ case recCopyInto (Proxy :: Proxy rhs) rts (Proxy :: Proxy res) arr# s'# of
421+ s''# ->
422+ case recCopyInto (Proxy :: Proxy lhs) lts (Proxy :: Proxy res) arr# s''# of
423+ s'''# ->
424+ case unsafeFreezeSmallArray# arr# s'''# of
425+ (# s''''#, a# #) -> (# s''''#, MkRec a# #)
426+{-# INLINE union #-}
427+
428+
387429-- | Alias for 'combine'
388430(++:) ::
389431 forall lhs rhs res.
test/JRecSpec.hs+4−5
@@ -7,8 +7,8 @@ import Test.Hspec
77spec :: Spec
88spec = do
99 it "polymorphic" $ do
10− (Rec (#u := True, #a := 5, #b := 6) & #u .~ 5)
11− `shouldBe` Rec (#u := 5, #a := 5, #b := 6)
10+ (Rec (#u := True, #a := 5, #a := 2, #b := 6) & #u .~ 5)
11+ `shouldBe` Rec (#u := 5, #a := 5, #a := 2, #b := 6)
1212 it "show" $ do
1313 show (Rec ()) `shouldBe` "{}"
1414 show (Rec (#foo := True)) `shouldBe` "{foo = True}"
@@ -53,9 +53,8 @@ spec = do
5353 it "simple union" $ do
5454 Rec (#a := 1) `union` Rec (#b := 2)
5555 `shouldBe` Rec (#a := 1, #b := 2)
56− it "union with duplicates (not implemented)" $ do
57− -- TODO: Actually remove duplicates
56+ it "union with duplicates" $ do
5857 let r1 = Rec (#b := 5, #a := 6)
5958 r2 = Rec (#c := 7, #a := 8)
6059 r1 `union` r2
61− `shouldBe` Rec (#b := 5, #a := 8, #c := 7, #a := 8)
60+ `shouldBe` Rec (#b := 5, #a := 6, #c := 7)