-
Notifications
You must be signed in to change notification settings - Fork 139
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
Deriving of unboxed vector for newtypes #315
Comments
What would happen to mutable vectors, manipulated in |
Do you mean ones that use IO in implementation (Storable)? I think nothing. They need to use unsafePrimToPrim anyway. It will work just fine for converting to ST. User facing API shouldn't change at all. D.V.Generic.Mutable should continue to use PrimMonad |
Are you proposing this? class MVector v a where ...
basicClear :: v s a -> ST s ()
clear :: (PrimMonad m, MVector v a) => v (PrimState m) a -> m () |
Yes. Exactly this. |
This looks legit to me. |
And what would be the definition of |
clear :: (PrimMonad m, MVector v a) => v (PrimState m) a -> m ()
clear = basicClear
-- vvv
clear = primToPrim . basicClear |
Got it. Agreed, great idea. I quickly grepped GitHub, seems that folks mostly follow the recommendation do not use |
Here is an alternative approach for easy creation of Create a class (which could I think be simply added to class Unbox (Components e) => MyUnbox e where
type Components e :: Type
toComponents :: e -> Components e
fromComponents :: Components e -> e Here is an example in Default implementation with Then we just create all the boiler plate only once and further instances become so much simpler. An example of an instance say for instance Unbox a => MyComplex (Complex a) where
type Components (Complex a) = (a, a)
toComponents (r :+ i) = (r, i)
fromComponents (r, i) = r :+ i And that all it takes to unbox a type with this approach and it looks like ghc is smart enough to optimize it all away. The full example from @Shimuuar and @Bodigrim let me know what are you thoughts on this. |
But you still need to write down all this 50 lines of MVector/Vector instances, right? I think any practically useful deriving mechanism must not require write those by hand. Programmers will do that only is they absolutely have to.Effectively it limits unboxed vector to types derived in libraries. But I think you basically described iso-deriving's approach. Say one wants to define Unbox instance for Since DerivingVia uses same mechanism as GND it only strengthens case for proposed change. |
That's the point, if |
I see. But changing definition of Unbox is much more invasive change all existing Unbox instances. There're very few experiments on how SoA could be encoded in haskell. We have Unbox that works but probably could be improved O(1) zips/unzips for product for example. But this is to a large degree independent from this proposal. It aim to make it possible to use GDN DerivingVia for MVector/Vector type classes. Mostly for benefit of unboxed vectors but there could be other uses. |
I'm not fully on board with changing the existing |
No worries, I just threw it there as an alternative idea that worked well for me. I am by no means suggesting we should apply it. I do prefer not to break users code either ;) |
I accidentally stumbled on GHC issue about deriving for vectors: https://gitlab.haskell.org/ghc/ghc/-/issues/9112 |
If I understand that ghc ticket correctly it is suggesting a wrong thing. We should ask ourselves a question: is it correct to have vector/Data/Vector/Primitive/Mutable.hs Line 82 in 6b8fcdf
and vector/Data/Vector/Storable/Mutable.hs Line 100 in c7858a7
I think the same reasoning applies to unboxed vectors as well. Nothing prevents me from creating two instances for two equivalent newtypes which implement unboxing differently. If representational type families were a thing it would allow me to cast between incompatible unboxed vector representations. |
I just wrote down related issue on GHC bug tracker. I however was under implression that GHC can perform coercions when corresponding constructors are in scope. |
@Shimuuar You are right, this will compile just fine because all newtype constructors are in scope newtype Foo = Foo Int
newtype Bar = Bar Int
newtype instance VU.MVector s Foo = MV_Foo Foo
newtype instance VU.MVector s Bar = MV_Bar Bar λ> foo = MV_Foo (Foo 5) :: VU.MVector () Foo
λ> coerce foo :: VU.MVector () Bar
coerce foo :: VU.MVector () Bar :: VU.MVector () Bar But this one will not, because they aren't declared as newtypes: data instance VU.MVector s Foo = MV_Foo Foo
data instance VU.MVector s Bar = MV_Bar Bar Note that in the ghc ticket it is declared as
The whole point about roles is that I can implement the way values of Coercion is good for deriving, but for custom Not quite sure what's the correct direction to go here. If we are to promote the same safety for unboxed as we are for storable and primitive we might want to hide the constructor for vector/Data/Vector/Unboxed/Mutable.hs Line 17 in 6b8fcdf
just as it is done for Line 40 in 6b8fcdf
This way if anyone needs access to these constructors for deriving as it is suggested in this ticket they will have to use the internal |
Fixed by #335 |
Problem is very simple. Newtype deriving doesn't work since roles were introduced to GHC.
So this simple program fails with:
Reason for that are very obvious. Methods of type class has type
∀m. PrimMonad m => ... -> m a
and GHC can't coercem Int
tom X
because it can't assume that Int has representational/phantom role.This problem could be fixed by changing
∀m. PrimMonad ⇒ m
type parameter to∀s. ST s
. Sincem
only appears in positive positions both approaches are equivalent and every instance that could be written in one style could be written in another.This is of course breaking change but breakage should be relatively limited. Type class methods are not meant to be used directly and custom instances should continue to compile
The text was updated successfully, but these errors were encountered: