Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Lift instances #343

Merged
merged 3 commits into from
Dec 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

* Define `dataCast1` for `HashMap`.

* [Add `Lift` instances for Template Haskell](https://github.com/haskell-unordered-containers/unordered-containers/pull/343)

## [0.2.16.0]

* [Increase maximum branching factor from 16 to 32](https://github.com/haskell-unordered-containers/unordered-containers/pull/317)
Expand Down
23 changes: 20 additions & 3 deletions Data/HashMap/Internal.hs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
{-# LANGUAGE BangPatterns, CPP, MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE LambdaCase #-}
#if __GLASGOW_HASKELL__ >= 802
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE UnboxedSums #-}
Expand Down Expand Up @@ -179,6 +184,7 @@ import GHC.Exts (TYPE, Int (..), Int#)
import Data.Functor.Identity (Identity (..))
import Control.Applicative (Const (..))
import Data.Coerce (coerce)
import qualified Language.Haskell.TH.Syntax as TH

-- | A set of values. A set cannot contain duplicate values.
------------------------------------------------------------------------
Expand All @@ -193,6 +199,14 @@ data Leaf k v = L !k v
instance (NFData k, NFData v) => NFData (Leaf k v) where
rnf (L k v) = rnf k `seq` rnf v

-- | @since 0.2.17.0
instance (TH.Lift k, TH.Lift v) => TH.Lift (Leaf k v) where
#if MIN_VERSION_template_haskell(2,16,0)
liftTyped (L k v) = [|| L k $! v ||]
#else
lift (L k v) = [| L k $! v |]
#endif

#if MIN_VERSION_deepseq(1,4,3)
-- | @since 0.2.14.0
instance NFData k => NF.NFData1 (Leaf k) where
Expand All @@ -217,6 +231,9 @@ data HashMap k v

type role HashMap nominal representational

-- | @since 0.2.17.0
deriving instance (TH.Lift k, TH.Lift v) => TH.Lift (HashMap k v)

instance (NFData k, NFData v) => NFData (HashMap k v) where
rnf Empty = ()
rnf (BitmapIndexed _ ary) = rnf ary
Expand Down
25 changes: 25 additions & 0 deletions Data/HashMap/Internal/Array.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples, ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-}
{-# OPTIONS_HADDOCK not-home #-}

Expand Down Expand Up @@ -69,6 +70,7 @@ module Data.HashMap.Internal.Array
, traverse'
, toList
, fromList
, fromList'
) where

import Control.Applicative (liftA2)
Expand All @@ -84,6 +86,8 @@ import GHC.Exts (SmallArray#, newSmallArray#, readSmallArray#, writeSmallArray#,
SmallMutableArray#, sizeofSmallArray#, copySmallArray#, thawSmallArray#,
sizeofSmallMutableArray#, copySmallMutableArray#, cloneSmallMutableArray#)

import qualified Language.Haskell.TH.Syntax as TH

#if defined(ASSERTS)
import qualified Prelude
#endif
Expand Down Expand Up @@ -474,6 +478,27 @@ fromList n xs0 =
go (x:xs) mary i = do write mary i x
go xs mary (i+1)

fromList' :: Int -> [a] -> Array a
fromList' n xs0 =
CHECK_EQ("fromList'", n, Prelude.length xs0)
run $ do
mary <- new_ n
go xs0 mary 0
where
go [] !mary !_ = return mary
go (!x:xs) mary i = do write mary i x
go xs mary (i+1)
Comment on lines +481 to +490
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation for adding this in addition to the existing fromList which looks identical at first glance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strict in the elements, so the resulting HashMap won't be full of thunks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Can you add haddocks that point this out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but the leaves may still be thunks .... Hmm.... Maybe it's better to be lazy after all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's better to be lazy after all?

@madgen, what do you think / prefer?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjakobi in my particular application laziness doesn't play much role, so I prefer it there weren't full of thunks, but I don't think that is a superior choice in general. Do what you think would be most generally applicable please.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjakobi, The problem is that it's not a "full of thunks" vs. "not full of thunks". If we're lazy in the leaves, as we really should be, then we should be lazy in the spines as well for best results. But users may want to be strict in the leaves, so we should have a strict lift in the Strict module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. What you mean by leaves. Elements in the HashMap? Isn't the assumption that they will be floated out and made static data if they can?

(That's why I'd like SmallArray# literals, you'll just know that lifted expressions won't have any thunks as it perfectly would be just constructors or literals all the way).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phadej, I guess we want to force construction of the values? Will the derived instance do that? I got a bit confused again.


instance TH.Lift a => TH.Lift (Array a) where
#if MIN_VERSION_template_haskell(2,16,0)
liftTyped ar = [|| fromList' arlen arlist ||]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's disappointing that SmallArray# (and other *Array#) don't have literals, but I don't think we can do better atm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fortunately, I don't that any overhead converting the arrays from lists will really be noticeable, especially since I expect all this spliced stuff gets floated out as CAFs. Literals would be nice.

Copy link
Contributor

@phadej phadej Dec 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they will be CAFs, but they could been off-heap structures, which would been even better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean just in the program code? Do we have things with pointers that work like that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. Map and IntSet, yes.

#else
lift ar = [| fromList' arlen arlist |]
#endif
where
arlen = length ar
arlist = toList ar

toList :: Array a -> [a]
toList = foldr (:) []

Expand Down
6 changes: 6 additions & 0 deletions Data/HashSet/Internal.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Trustworthy #-}
{-# OPTIONS_HADDOCK not-home #-}
Expand Down Expand Up @@ -113,6 +115,7 @@ import qualified Data.Hashable.Lifted as H
#if MIN_VERSION_deepseq(1,4,3)
import qualified Control.DeepSeq as NF
#endif
import qualified Language.Haskell.TH.Syntax as TH

-- | A set of values. A set cannot contain duplicate values.
newtype HashSet a = HashSet {
Expand All @@ -121,6 +124,9 @@ newtype HashSet a = HashSet {

type role HashSet nominal

-- | @since 0.2.17.0
deriving instance TH.Lift a => TH.Lift (HashSet a)

instance (NFData a) => NFData (HashSet a) where
rnf = rnf . asMap
{-# INLINE rnf #-}
Expand Down
3 changes: 2 additions & 1 deletion unordered-containers.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ library
build-depends:
base >= 4.9 && < 5,
deepseq >= 1.1,
hashable >= 1.0.1.1 && < 1.5
hashable >= 1.0.1.1 && < 1.5,
template-haskell < 2.19

default-language: Haskell2010

Expand Down