-
Notifications
You must be signed in to change notification settings - Fork 178
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
Can we de-duplicate Set/Map by leveraging common structure? #851
Comments
I believe it would be possible with Backpack, though I don't know how to use that. Unfortunately, |
Currently we have data Set a
= Bin {-# UNPACK #-} !Size !a !(Set a) !(Set a)
| Tip
data Map k a
= Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)
| Tip which seems to be the same structure we'd get with {-# LANGUAGE PolyKinds #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedNewtypes #-}
import GHC.Exts
data BinTree (a :: TYPE r)
= Bin {-# UNPACK #-} !Size !a !(BinTree a) !(BinTree a)
| Tip
newtype Set a = Set (BinTree a)
newtype Map k v = Map (BinTree (Arg' k v))
newtype Arg' a b = Arg' (# !a, b #) unfortunately |
I see no newtypes there. As far as I know, we still don't have a story for functions that actually work with that kind of |
Last 3 lines?
Actually looks like I can't even define it. Something in my setup was making ghc extra lenient somehow - running ghc directly fails on two places
Deadend it seems |
If/when levity polymorphism supports the above, newtype DMap k v = DMap (BinTree (exists x. Arg (k x) (v x))) |
Nothing much has changed. See commercialhaskell/stack#2540 and commercialhaskell/stack#4745 |
newtypes can't change the semantics of the types they wrap in this way. Here you try to say "make a newtype of a unboxed tuple, but also make the tuple strict in it's first field. Sadly there is currently also no type that can express the semantics you want here. You would either need strict unboxed tuples: https://gitlab.haskell.org/ghc/ghc/-/issues/17001 (seems unlikely to happen) or one of the things discussed in https://gitlab.haskell.org/ghc/ghc/-/issues/21617 (which I'm working on in my spare time) In theory GHC could one day support a type like:
But supporting functions which take levity polymorphic arguments is unlikely to happen as GHC often needs/wants to generate different code depending on the liftedness of the argument. |
What about doing it the other way? -- Just like today:
data Map k a
= Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)
| Tip
-- New: Re-use Map, but with unit as value type
-- a has the role `nominal` as required :-)
newtype Set a = Set (Map a ()) This approach is used in many other programming languages, exactly to improve code-reusage between the two datastructures. Essentially the whole interface of I believe this alternate definition of Set does not differ in strictness of its contents from the current one (though I might have missed something of course), which makes it nicely backwards-compatible. |
This would increase the size of the |
That's how it's done for It has the issue pointed above but I also wonder if it's morally "backwards". It's defining the more general thing first then specializing it, whereas It's easier to obtain multiple shapes (e.g. |
Looking at #817 's
it seems the two structures are the same "up to parenthesization"
I wonder if one could exploit this to de-duplicate a good deal of code.
It's not as simple as
since
causes
but maybe we could have the binary tree structure be its own type and then
allowing a good deal of functions to simply delegate
There are obvious examples like
null
,size
,member
butSet
andMap.Lazy
may have more similarities -insert
seems pretty much the same "up to parenthesization".It's not obvious to me whether
Map
can be decomposed like this, since the extra indirection (in this casedata Arg a b
) interferes with lazyness and performance.The text was updated successfully, but these errors were encountered: