
Hi-- I'm back to trying to teach myself Haskell, and I've already got myself into a muddle again. I've begun playing a bit with the type system, and typeclasses and figured I'd build a type to hold angular values since it would be nice to force the type system to check whether I'm using degrees or radians: data Angle a = Radians a | Degrees a deriving (Eq, Show) Then I decided I wanted to create a (gratuitous) type class to abstract out the radian/degree conversions: class Angular a where rad :: a -> a rad x = pi * (deg x) / 180 deg :: a -> a deg x = 180 * (rad x) / pi Then tried to make Angle an instance of Angular (such as Float in an instance of Floating): instance Angular (Angle a) where rad (Radians x) = x deg (Degrees x) = x I've removed all the context clauses, rather than pick one of many equally broken implementations to share, because that seems to be a large part of where my problems are. Is this just an issue of syntax, or am I doing something fundamentally wrong? Thanks-- Greg

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.

Thank you! I agree that what I'm doing isn't useful from an application perspective, but this turned out to be very useful for comprehension. 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.

On Tue, Aug 24, 2010 at 05:06:05AM +0200, 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
I may look like a nitpicker but the above function definitions lose their type information. I'd have written it this way: data Angle a = Radians a | Degrees a deriving (Eq, Show) rad :: Floating a => Angle a -> Angle a rad (Radians x) = Radians x rad (Degrees x) = Radians (pi * x / 180) deg :: Floating a => Angle a -> Angle a deg (Radians x) = Degrees (180 * x / pi) deg (Degrees x) = Degrees x On the other side, I am not particularly confident this approach is the best. Other people may give you better solutions … /‡ John

On 08/23/10 22:33, Greg wrote:
...it would be nice to force the type system to check whether I'm using degrees or radians:
data Angle a = Radians a | Degrees a deriving (Eq, Show)
You did it wrong... the difference between Radians and Degrees here is only visible at runtime, as they are both of the same type, Angle. Don't feel bad, this confused me for a while as a Haskell-beginner too. An example to "force the type system to check" would be data Radians a = Radians a deriving (Eq, Show) data Degrees a = Degrees a deriving (Eq, Show) Then you could make conversion functions, say, radToDeg :: (Floating a) => Radians a -> Degrees a degToRad :: (Floating a) => Degrees a -> Radians a and implement them; you *could* have a 'class Angle' or such, if you wanted... perhaps trig functions or such would be sensible to put in there. And/or you could try to make each data-type be a member of Num and related classes (being able to add angles, etc) For a real program, I think I would try to stick to just one unit (e.g. radians) for internal program use (and convert any outside data to that unit promptly), unless there was a reason that didn't work very well; but the typeclass-stuff is an excellent thing to play with! -Isaac

Thanks. I wasn't trying to do anything terribly practical-- just looking for toy problems to get my head around the type system. The part I figured was impractical was the "Angular" class, but this is the second feedback that suggested making both Degrees and Radians type constructors rather than alternate value constructors. Is that the right approach? I know the trig functions are already available and they all just traffic in floats, but if this weren't the case, I'd imagine a structure along the lines of data Angle a = Radians a | Degrees a deriving (Eq, Show) sin :: Angle a -> a I think the approach you were suggesting is to make Degrees and Radians as types, and put sin as a function of the class. I suppose that is better in that it makes it much easier to implement additional angular measurements (without reimplementing sin). My reservations were with needing to define sin for each angle type (which I now think my method would force me to do anyway), which could be a potentially expensive operation. With Degree and Radian as types, I think I can get away without reimplementing by using default functions such as: sin x = radianSin $ rad x or some such which would only require that I define a radian conversion for each angle type. (where Degree and Radian are stand ins for useful types and classes) Cheers-- Greg On Aug 23, 2010, at 10:01 PM, Isaac Dupree wrote:
On 08/23/10 22:33, Greg wrote:
...it would be nice to force the type system to check whether I'm using degrees or radians:
data Angle a = Radians a | Degrees a deriving (Eq, Show)
You did it wrong... the difference between Radians and Degrees here is only visible at runtime, as they are both of the same type, Angle. Don't feel bad, this confused me for a while as a Haskell-beginner too. An example to "force the type system to check" would be
data Radians a = Radians a deriving (Eq, Show) data Degrees a = Degrees a deriving (Eq, Show)
Then you could make conversion functions, say, radToDeg :: (Floating a) => Radians a -> Degrees a degToRad :: (Floating a) => Degrees a -> Radians a
and implement them;
you *could* have a 'class Angle' or such, if you wanted... perhaps trig functions or such would be sensible to put in there. And/or you could try to make each data-type be a member of Num and related classes (being able to add angles, etc)
For a real program, I think I would try to stick to just one unit (e.g. radians) for internal program use (and convert any outside data to that unit promptly), unless there was a reason that didn't work very well; but the typeclass-stuff is an excellent thing to play with!
-Isaac _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

[hopefully this won't double post, I accidentally sent from a non-member address] Thanks. I wasn't trying to do anything terribly practical-- just looking for toy problems to get my head around the type system. The part I figured was impractical was the "Angular" class, but this is the second feedback that suggested making both Degrees and Radians type constructors rather than alternate value constructors. Is that the right approach? I know the trig functions are already available and they all just traffic in floats, but if this weren't the case, I'd imagine a structure along the lines of data Angle a = Radians a | Degrees a deriving (Eq, Show) sin :: Angle a -> a I think the approach you were suggesting is to make Degrees and Radians as types, and put sin as a function of the class. I suppose that is better in that it makes it much easier to implement additional angular measurements (without reimplementing sin). My reservations were with needing to define sin for each angle type (which I now think my method would force me to do anyway), which could be a potentially expensive operation. With Degree and Radian as types, I think I can get away without reimplementing by using default functions such as: sin x = radianSin $ rad x or some such which would only require that I define a radian conversion for each angle type. (where Degree and Radian are stand ins for useful types and classes) Cheers-- Greg On Aug 23, 2010, at 10:01 PM, Isaac Dupree wrote:
On 08/23/10 22:33, Greg wrote:
...it would be nice to force the type system to check whether I'm using degrees or radians:
data Angle a = Radians a | Degrees a deriving (Eq, Show)
You did it wrong... the difference between Radians and Degrees here is only visible at runtime, as they are both of the same type, Angle. Don't feel bad, this confused me for a while as a Haskell-beginner too. An example to "force the type system to check" would be
data Radians a = Radians a deriving (Eq, Show) data Degrees a = Degrees a deriving (Eq, Show)
Then you could make conversion functions, say, radToDeg :: (Floating a) => Radians a -> Degrees a degToRad :: (Floating a) => Degrees a -> Radians a
and implement them;
you *could* have a 'class Angle' or such, if you wanted... perhaps trig functions or such would be sensible to put in there. And/or you could try to make each data-type be a member of Num and related classes (being able to add angles, etc)
For a real program, I think I would try to stick to just one unit (e.g. radians) for internal program use (and convert any outside data to that unit promptly), unless there was a reason that didn't work very well; but the typeclass-stuff is an excellent thing to play with!
-Isaac _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On 08/24/10 01:38, Greg Best wrote:
My reservations were with needing to define sin for each angle type (which I now think my method would force me to do anyway)
Yeah, the two methods are about equal in how much substantive code you have to write. -- each allows a certain amount of code-sharing (you can figure out how, with practice!), yet there's no "free lunch" (the code doesn't *all* write itself, and it shouldn't).

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 8/24/10 01:38 , Greg Best wrote:
The part I figured was impractical was the "Angular" class, but this is the second feedback that suggested making both Degrees and Radians type constructors rather than alternate value constructors. Is that the right approach? I know the trig functions are already available and they all just traffic in floats, but if this weren't the case, I'd imagine a structure along the lines of
The point of typeclasses is to allow you to abstract operations over multiple types. If you have only one type...
data Angle a = Radians a | Degrees a deriving (Eq, Show)
then a typeclass is just unnecessary complexity. It's when you have multiple types which share a group of operations that typeclasses become useful. So, for example, with the above type (sin) can be implemented as a single function which pattern matches on the constructor; but if (Radians) and (Degrees) need to be separate types instead of value constructors for a single type, then you *must* use a typeclass to implement a single (sin) instead of (sinRadians) or (sinDegrees). - -- brandon s. allbery [linux,solaris,freebsd,perl] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkxzXyMACgkQIn7hlCsL25UfdgCgnx492P1aLyttxTsTQKBycSlf KvQAnA/FkXYgPpJP37aF2hoJCMWUct+I =aldW -----END PGP SIGNATURE-----
participants (7)
-
Brandon S Allbery KF8NH
-
Greg
-
Greg Best
-
Greg Best
-
Isaac Dupree
-
John Obbele
-
Tobias Brandt