-
Notifications
You must be signed in to change notification settings - Fork 58
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
Support levity polymorphic interface #356
Comments
Great idea! |
I though about this a bit more and there are some problems that arise:
Can you think of any other problems or solutions to the above problems? And what solutions would you prefer? |
Thanks for writing this up. I think this is a good area for improvement. On question 1, I’m pretty strongly in favor of matching what the Array# does and making the levity argument be inferred. The backwards compatibility is nice, and in practice, I’ve never seen a situation where this would cause a problem. On question 2, I think it would be sufficient to make write/index/copy operations levity polymorphic and leave read operations in their current deficient state. The strategy is backwards compatible, and it leaves open the possibility of improving the read operations if Monad ever becomes levity polymorphic. The other options would be a breaking change both now and possibly later if Monad were ever improved (since we would probably undo them at that point). |
I believe that in many, perhaps all, existing levity-poly operations, the levity is Inferred. I think that's probably best. But rather than guessing, this conversation would be much more concrete if you could write out
Without the specifics, it's hard to be sure that we are talking sense. | however it seems to be designed only for types like Array#, MutVar#, etc., that already have a lifted wrapper type. Also, about converting unlifted to lifted, as part of !8750 I have defined one "boxing type" for each RuntimeRep. These defns are in ghc-prim:GHC.Types. For example
Notice that these are polymorphic, although (necessarily) monomorphic in RuntimeRep. I have not automated the boxing or unboxing steps... I just needed them internally in the compiler. Just mentioning them here in case it helps the conversation about lifting. |
Here is the design that I envision (I would love to see other designs if people have other opinions about the best way to go about this):
With this design,
The wrapper types ( |
I think you wrote a few times You might get in trouble with the type-checker though, because you will inevidably have to bind a levity-polymorphic variable Anyway, I think the types are as general as possible, perhaps even more general than possible (at the moment in GHC). |
Thanks. For the benefit of future readers, I've updated my previous comment to correct the mistaken uses I didn't realize that levity polymorphic binding was not yet supported by GHC. That greatly limits the utility of this change to primitive. Even so, I think I would still be fine with moving forward with my suggested interface (minus Again, I'm open to other ideas on this. The whole thing may still be a bit premature, and we could always wait for GHC to progress a little more before changing anything. |
So I tried to make the functions levity polymorphic, using the approach @andrewthad suggested. The only functions where that is possible are the following:
In particular, there is no way to even create values of these types (with the exception of However, I don't think there's anything preventing levity polymorphic variables in theory (they're just a pointer either way). This has already been suggested: https://gitlab.haskell.org/ghc/ghc/-/issues/15532. @sgraf812 would it be possible to use data-elevator to somehow circumvent this problem in the meantime? |
I don't think What you could do to define the levity-polymorphic functions would be something like writeArray :: forall {v :: Levity} (m :: Type -> Type) (a :: TYPE ('BoxedRep v)). PrimMonad m => MutableArray s a -> Int -> a -> m ()
writeArray = unsafeCoerce writeArrayImpl
writeArrayImpl :: forall (m :: Type -> Type) (a :: UnliftedType). PrimMonad m => MutableArray s a -> Int -> a -> m ()
writeArrayImpl = ... (Actually, you could do We'll have to see wether Perhaps the levity-polymorphic API should go in a separate module and simply be implemented in terms of the Lifted one+unsafeCoerce. |
If we had a levity-polymorphic newtype ST s (a :: TYPE ('BoxedRep l)) = ST (State# s -> (# State# s, a #)) then we could support levity-polymorphic |
Since GHC 9.4, various primitives (
Array#
,SmallArray#
,MutVar#
, ...) and their corresponding primops are now levity polymorphic. I propose making theprimitive
wrappers (Array
,SmallArray
,MutVar
, ...) also levity polymorphic, so that users can take advantage of the levity polymorphism without having to use the primops fromGHC.Exts
directly.This would also supersede primitive-unlifted afaict.
I'm not sure if this would be a breaking change, but it should at least be minor, since type variables default to kind
Type
(soArray a
would still beArray (a :: Type)
).The text was updated successfully, but these errors were encountered: