Problems with type family in a class

Hi, Consider the following type classes: class (Signal sx) => Complexable sx where type ComplexSignalType .... class (Complexable sx) => FourierTransformable sx where fft :: (cx ~ ComplexSignalType sx) => cx -> cx .... and an instance: instance (RealFloat e) => Complexable [e] where type ComplexSignalType = [Complex e] instance (RealFloat e) => FourierTransformable [e] where fft = ... Why this example will give errors: *DSP.Signals.Core Prelude Data.Complex> :t ax ax :: [Complex Double] *DSP.Signals.Core Prelude Data.Complex> fft ax <interactive>:90:1: No instance for (FourierTransformable s0 [Complex Double]) arising from a use of ‘fft’ The type variable ‘s0’ is ambiguous Note: there is a potential instance available: instance [overlap ok] (RealFloat e, Math.FFT.Base.FFTWReal e) => FourierTransformable [e] [Complex e] -- Defined at src/DSP/Signals/Instances/List.hs:40:10 In the expression: fft ax In an equation for ‘it’: it = fft ax Thanks! -- Lemol-C 50 Aniversario de la Cujae. Inaugurada por Fidel el 2 de diciembre de 1964 http://cujae.edu.cu

On Wed, 16 Jul 2014 22:32:27 -0400, Leza Morais Lutonda
Hi,
Consider the following type classes:
class (Signal sx) => Complexable sx where type ComplexSignalType ....
class (Complexable sx) => FourierTransformable sx where fft :: (cx ~ ComplexSignalType sx) => cx -> cx ....
and an instance:
instance (RealFloat e) => Complexable [e] where type ComplexSignalType = [Complex e]
instance (RealFloat e) => FourierTransformable [e] where fft = ...
Why this example will give errors:
*DSP.Signals.Core Prelude Data.Complex> :t ax ax :: [Complex Double] *DSP.Signals.Core Prelude Data.Complex> fft ax
<interactive>:90:1: No instance for (FourierTransformable s0 [Complex Double]) arising from a use of ‘fft’ The type variable ‘s0’ is ambiguous Note: there is a potential instance available: instance [overlap ok] (RealFloat e, Math.FFT.Base.FFTWReal e) => FourierTransformable [e] [Complex e] -- Defined at src/DSP/Signals/Instances/List.hs:40:10 In the expression: fft ax In an equation for ‘it’: it = fft ax
Thanks!
The reason you're getting this error is because all you know about your instance is that the type of fft is :: [Complex Double] -> [Complex Double]. Since by definition this [Complex Double] is ComplexSignalType sx, all we know about the associated instance (parametrized by sx) is that ComplexSignalType sx ~ [Complex Double]. Since ComplexSignalType is a type family, it's not injective, so this does not suffice to uniquely identify our actual instance (sx, here called s0 by GHC). For example, we could have a second instance: instance Complexable () where type ComplexSignalType () = [Complex Double] instance FourierTransformable () where fft = error "oops" And with this instance, fft would also be [Complex Double] -> [Complex Double] but the behavior of would clearly be different to your actually intended function. If you think your signal function is bijective, you could use a two-way functional dependency like this: class Complexable sx cx | sx -> cx, cx -> sx class Complexable sx cx => FourierTransformable sx cx where fft :: cx -> cx Or if you want to avoid the overhead of redesigning Complexable you could even do something like this: class (cx ~ ComplexSignalType sx, Complexable sx) => FourierTransformable sx cx | cx -> sx where fft :: cx -> cx Either way, if instance selection should be possible based solely on the type of ‘cx’, then ‘cx’ must imply everything contained in the instance head.

On 17/07/14 02:32, Niklas Haas wrote:
The reason you're getting this error is because all you know about your instance is that the type of fft is :: [Complex Double] -> [Complex Double].
Since by definition this [Complex Double] is ComplexSignalType sx, all we know about the associated instance (parametrized by sx) is that ComplexSignalType sx ~ [Complex Double]. Since ComplexSignalType is a type family, it's not injective, so this does not suffice to uniquely identify our actual instance (sx, here called s0 by GHC).
For example, we could have a second instance:
instance Complexable () where type ComplexSignalType () = [Complex Double]
instance FourierTransformable () where fft = error "oops"
And with this instance, fft would also be [Complex Double] -> [Complex Double] but the behavior of would clearly be different to your actually intended function.
If you think your signal function is bijective, you could use a two-way functional dependency like this:
class Complexable sx cx | sx -> cx, cx -> sx
class Complexable sx cx => FourierTransformable sx cx where fft :: cx -> cx
Or if you want to avoid the overhead of redesigning Complexable you could even do something like this:
class (cx ~ ComplexSignalType sx, Complexable sx) => FourierTransformable sx cx | cx -> sx where fft :: cx -> cx
Either way, if instance selection should be possible based solely on the type of ‘cx’, then ‘cx’ must imply everything contained in the instance head. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Thanks for the explanation, Niklas, now I understand (I'm understanding) type families and I left with: class (Signal s, ComplexSignal (ComplexSignalType s), RealSignalType (ComplexSignalType s) ~ s) ⇒ Complexable s where type ComplexSignalType s class (Complexable s) ⇒ FourierTransformable s where fft ⦂ ∀ csx. (csx ~ ComplexSignalType s, ComplexSignal csx) ⇒ csx → csx Maybe more complex than normal? Thanks again. -- Leza Morais Lutonda, Lemol-C http://lemol.github.io 50 Aniversario de la Cujae. Inaugurada por Fidel el 2 de diciembre de 1964 http://cujae.edu.cu

On Sun, 20 Jul 2014 00:33:00 -0400, Leza Morais Lutonda
Thanks for the explanation, Niklas, now I understand (I'm understanding) type families and I left with:
class (Signal s, ComplexSignal (ComplexSignalType s), RealSignalType (ComplexSignalType s) ~ s) ⇒ Complexable s where
type ComplexSignalType s
class (Complexable s) ⇒ FourierTransformable s where fft ⦂ ∀ csx. (csx ~ ComplexSignalType s, ComplexSignal csx) ⇒ csx → csx
Maybe more complex than normal?
Thanks again.
Hmm, to be honest I'm not sure if this is effectively any different from what you had in your original example. Does this work as expected w.r.t type inferencing and instance selection?

On 20/07/14 09:57, Niklas Haas wrote:
Hmm, to be honest I'm not sure if this is effectively any different from what you had in your original example.
Does this work as expected w.r.t type inferencing and instance selection? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Yes, it works as expected, e.g, now I have instances of FourierTrasformable for List [Double] and for HMatrix.Vector Double, and the following works well:
fft (toComplex [1,2,3,4 :: Double]) [10.0 :+ 0.0,(-2.0) :+ 2.0,(-2.0) :+ 0.0,(-2.0) :+ (-2.0)] fft (toComplex $ vector [1,2,3,4]) fromList [10.0 :+ 0.0,(-2.0) :+ 2.0,(-2.0) :+ 0.0,(-2.0) :+ (-2.0)]
I think (if I understand well) this is because the constraints (RealSignalType (ComplexSignalType s) ~ s) in the Complexable class definition and (csx ~ ComplexSignalType s, ComplexSignal csx) in the fft definition makes a one-by-one dependency between s and csx. -- Leza Morais Lutonda, Lemol-C http://lemol.github.io 50 Aniversario de la Cujae. Inaugurada por Fidel el 2 de diciembre de 1964 http://cujae.edu.cu

On Mon, 21 Jul 2014 02:16:46 -0400, Leza Morais Lutonda
Yes, it works as expected, e.g, now I have instances of FourierTrasformable for List [Double] and for HMatrix.Vector Double, and the following works well:
fft (toComplex [1,2,3,4 :: Double]) [10.0 :+ 0.0,(-2.0) :+ 2.0,(-2.0) :+ 0.0,(-2.0) :+ (-2.0)] fft (toComplex $ vector [1,2,3,4]) fromList [10.0 :+ 0.0,(-2.0) :+ 2.0,(-2.0) :+ 0.0,(-2.0) :+ (-2.0)]
I think (if I understand well) this is because the constraints (RealSignalType (ComplexSignalType s) ~ s) in the Complexable class definition and (csx ~ ComplexSignalType s, ComplexSignal csx) in the fft definition makes a one-by-one dependency between s and csx.
I think you may be right. I missed the changes to the ComplexSignal class in between this and your original edit, I was just fixated on the fact that your new ‘fft’ definition looked nearly identical to your first. You can drop the forall csx. bit, for what it's worth, since csx is an unbound variable either way (and hence implicitly quantified).

On Mon, Jul 21, 2014 at 2:52 PM, Niklas Haas
You can drop the forall csx. bit, for what it's worth, since csx is an unbound variable either way (and hence implicitly quantified).
Also, the "ComplexSignal csx" constraint is redundant, since the Complexable superclass constraint includes it. In other words, you can write: class (Complexable s) ⇒ FourierTransformable s where fft :: (csx ~ ComplexSignalType s) ⇒ csx → csx

On Mon, 21 Jul 2014 15:39:08 -0400, adam vogt
Also, the "ComplexSignal csx" constraint is redundant, since the Complexable superclass constraint includes it. In other words, you can write:
class (Complexable s) ⇒ FourierTransformable s where fft :: (csx ~ ComplexSignalType s) ⇒ csx → csx
In fact, this type should now even be equivalent to: class Complexable s => FourierTransformable s where fft :: ComplexSignalType s -> ComplexSignalType s

On 21/07/14 16:09, Niklas Haas wrote:
On Mon, 21 Jul 2014 15:39:08 -0400, adam vogt
wrote: Also, the "ComplexSignal csx" constraint is redundant, since the Complexable superclass constraint includes it. In other words, you can write:
class (Complexable s) ⇒ FourierTransformable s where fft :: (csx ~ ComplexSignalType s) ⇒ csx → csx In fact, this type should now even be equivalent to:
class Complexable s => FourierTransformable s where fft :: ComplexSignalType s -> ComplexSignalType s _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe Yes, it was redundant, and now it looks better. Thanks!
-- Leza Morais Lutonda, Lemol-C http://lemol.github.io 50 Aniversario de la Cujae. Inaugurada por Fidel el 2 de diciembre de 1964 http://cujae.edu.cu
participants (3)
-
adam vogt
-
Leza Morais Lutonda
-
Niklas Haas