It was the
rad :: a b -> b
that I was missing. Two type variables-- one for Angle and one for Float. This now compiles just fine:
data Angle a = Radians a
| Degrees a
deriving (Eq, Show)
class Angular a where
rad :: (Floating b) => a b -> b
rad x = pi * (deg x) / 180
deg :: (Floating b) => a b -> b
deg x = 180 * (rad x) / pi
instance Angular Angle where
rad (Radians x) = x
deg (Degrees x) = x
x :: Angle Float
x = Radians (pi / 2)
y :: Float
y = deg x
It just doesn't execute, because of a non-exhaustive pattern for deg. I guess that makes sense-- the default functions are per type, not per value constructor...
Changing my instance definition to this:
instance Angular Angle where
rad (Radians x) = x
rad (Degrees x) = pi * x / 180
deg (Radians x) = 180 * x / pi
deg (Degrees x) = x
resolves the problem.
Thanks again for the help. I'm now that much further down the type system learning curve...
Cheers--
Greg
On Aug 23, 2010, at 8:06 PM, Tobias Brandt wrote:
You don't need a type class, you can just define your functions with pattern matching:
rad :: Angle a -> a
rad (Radians x) = x
rad (Degrees x) = pi * (deg x) / 180
deg :: Angle a -> a
deg (Radians x) = 180 * (rad x) / pi
deg (Degrees x) = x
Alternatively, you can define Radians and Degrees as separate types and use a type class:
data Radians a = Radians a
data Degrees a = Degrees a
class Angular a where
rad :: a b -> b
deg :: a b -> b
instance Angular Radians where
rad (Radians x) = x
deg (Radians x) = 180 * (rad x) / pi
instance Angular Degrees where
rad (Degrees x) = pi * (deg x) / 180
deg (Degrees x) = x
This would be extensible, but it this case not really useful.