问题
I have a datatype
newtype Zq q = Zq (IntType q)
where 'q' will be an instance of the class
class Foo a where
type IntType a
and 'IntType' is just the underlying representation (i.e. Int, Integral, etc) associated with 'q'.
I want to make Zq an instance of Data.Vector.Unbox. We are currently manually deriving Unbox using about 50 lines of trivial code as suggested in the link above. We will be making several different types 'Unbox' in our code, so writing 50 lines for each type is not appealing.
I found two alternatives here. One alternative is to use this package which uses Template Haskell to derive instances of Unbox. The TH code would look like:
derivingUnbox "Zq"
[d| instance (Foo q, U.Unbox (IntType q)) => Unbox' (ZqBasic q) (IntType q) |]
[| \ (Zq x) -> x |]
[| \ x -> Zq x |]
The problem is, I can't define instances using associated type synonyms (or can I??)
[A related question: Why does TypeSynonymInstances, an extension implied by FlexibleInstances, not allow associated type synonym instances? Is this somehow fundamentally a different beast?]
My current solution to that problem is to redefine Zq as
newtype Zq q i = Zq i
and then add the equality constraint
i~(IntType q)
in every instance involving (Zq q i), which isn't very elegant. My (working) Unbox derivation becomes
derivingUnbox "Zq"
[d| instance (U.Unbox i, i~IntType q, Foo q) => Unbox' (Zq q i) i |]
[| \ (Zq x) -> x |]
[| \ x -> Zq x |]
I feel like I should be able to accomplish this without resorting to explicitly exposing the type 'i'. All I've done is moved it from an associated type synonym to an explicit parameter with equality constraints. Why is this "fundamentally" a different (and apparently safer) approach? Is there some way I can avoid adding the type parameter 'i' and still get automatic Unbox derivation?
Extra type parameter aside, I'm having trouble using the TH package to derive Unbox for (Vector r), that is, I want to make an Unbox Vector of Unbox Vectors. My attempt is something like:
newtype Bar r = Bar (Vector r)
derivingUnbox "Bar"
[d| instance (Unbox r) => Unbox' (Bar r) (Vector r) |]
[| \ (Bar x) -> x |]
[| \ x -> Bar x |]
but I get (lots of) errors like:
`basicUnsafeFreeze` is not a (visible) method of class `Data.Vector.Generic.Base.Vector`
I'm not sure why it can't find this method, when it works just fine for my Zq type.
The second approach listed above is using the extension GeneralizedNewtypeDeriving. The biggest problem I see with this approach is that I have some actual Data (rather than Newtype) that I need to be Unbox. However, just using the extension, I should be able to write
newtype Zq q = Zq (IntType q) deriving (Unbox, M.MVector MVector, G.Vector Vector)
or at least
newtype Zq q i = Zq i deriving (Unbox, M.MVector MVector, G.Vector Vector)
The first leads to the errors:
No instance for (Unbox (IntType q)) arising from the `deriving` clause of a data type declaration
No instance for (M.MVector MVector (IntType q)) ""
No instance for (G.Vector Vector (IntType q)) ""
and the second gives:
No instance for (M.MVector MVector i) ""
No instance for (G.Vector U.Vector i) ""
I'm not sure why it can't derive these instances, as the post above leads me to believe it should be able to. Perhaps I could get away with using the associated type synonym with GeneralizedNewtypeDeriving? (This still (probably) doesn't solve my problem when I need to derive Unbox for 'data's.)
Thanks for your help!
回答1:
You're running into a few separate problems here:
The TH approach
Yes, class instances for associated type synonyms are illegal
It's true that you can't define class instances for associated type synonyms or type functions, and this is with good reason: the compiler can't tell whether they overlap. For instance:
type family F a
instance Eq (F Int)
instance Eq (F Bool)
Do these instances overlap? Given the above source code, we can't tell: it depends on how someone later defines instances for F
. For instance, they could define
type instance F Int = Double
type instance F Bool = Double
and then the two instances of Eq
would in fact be overlapping.
You're running into a problem with the vector-th-unbox
package
If you look at the actual Unbox
instance you want, you don't actually want an instance for IntType q
; what you want is simply this:
instance (Unbox (IntType q), Foo q) => Unbox (Zq q) where
...
The problem is that the vector-th-unbox
package forces you to use the fake type-class Unbox'
to communicate the intermediate representation type (IntType q
in your case), as a convenient way of abusing Template Haskell's syntax to pass in a type. And then GHC sees that you have written Unbox' (Zq q) (IntType q)
and complains. I would suggest filing a bug for the vector-th-unbox
package.
Unbox
for Vector r
I think Louis Wasserman covered this.
The GeneralizedNewtypeDeriving
approach
The specific compile error you're having is because GHC can't infer the appropriate context. For most problems similar to the one you're having, StandaloneDeriving
language extension would solve your problem:
deriving instance Unbox (IntType q) => Unbox (Zq q)
deriving instance Unbox (IntType q) => M.MVector MVector (Zq q)
deriving instance Unbox (IntType q) => G.Vector Vector (Zq q)
But DON'T do this!
While GeneralizedNewtypeDeriving
often does exactly what you want, it is broken in some fundamental ways, and the Unbox
instance it produces is completely broken! So stick with the TH approach, after lobbying Liyang for a fix for your current problem.
回答2:
I've changed the syntax in 4820b73, so you should be able to do what you want now:
derivingUnbox "Complex"
[d| (Unbox a) ⇒ Complex a → (a, a) |]
[| \ (r :+ i) → (r, i) |]
[| \ (r, i) → r :+ i |]
I've also fixed the “xyz is not a (visible) method…” errors in fe37976, although it was possible to work around it with:
import qualified Data.Vector.Generic
import qualified Data.Vector.Generic.Mutable
Out now on Hackage. CC: @reinerp
来源:https://stackoverflow.com/questions/11399143/automatic-derivation-of-data-vector-unbox-with-associated-type-synonyms