I wanted to create a type or class, which can be used to handle angles. I started with defining a class, to make it possible to store degrees in Integral, radians in Floating, etc... But I realized, that I will meet complex problems if I continue it so. For example handling the addition of two angles, like a Radian Double and a Degree Int.To keep it simple, easy to use and develop, I decided to create one type only, and write the functions around it.module Data.Angle whereimport Data.Fixed -- mod'data Angle a = Radians a deriving (Eq, Show)-- Creating Angle from a value-- | Create an Angle with the given degreesangleFromDegrees :: (Integral d, Floating r) => d -> Angle rangleFromDegrees x = Radians $ (realToFrac x) * pi/180-- | Create an Angle with the given turnsangleFromTurns :: (Real t, Floating r) => t -> Angle rangleFromTurns x = Radians $ (realToFrac x) * pi*2-- | Create an Angle with the given turnsangleFromRadians :: (Floating r) => r -> Angle rangleFromRadians x = Radians x-- Get the value from Angle-- | Get degrees from an AngleangleValueDegrees :: (Floating r, RealFrac r, Integral d) => Angle r -> dangleValueDegrees (Radians x) = round $ x / pi * 180.0-- | Get radians from an AngleangleValueRadians :: (Floating r) => Angle r -> rangleValueRadians (Radians x) = x-- | Get turns from AngleangleValueTurns :: (Floating r) => Angle r -> rangleValueTurns (Radians x) = x / (pi*2)-- Basic functions-- | Adding two anglesaddAngle :: (Floating a) => Angle a -> Angle a -> Angle aaddAngle (Radians r1) (Radians r2) = Radians $ r1 + r2-- | Normalize Angle: transforming back to (0-2pi)normAngle :: (Floating a, Real a) => Angle a -> Angle anormAngle (Radians r) = Radians $ mod' r (pi*2)-- | Add two angles and normalize the resultaddAngleNorm :: (Floating a, Real a) => Angle a -> Angle a -> Angle aaddAngleNorm a b = normAngle $ addAngle a b-- | Distance between two anglesdistAngle :: (Floating a, Real a) => Angle a -> Angle a -> Angle adistAngle (Radians r1) (Radians r2) = Radians $ if (a' < b') then a' else b'    where        a' = mod' (r1-r2) (pi*2)        b' = mod' (r2-r1) (pi*2)-- | Flip AngleflipAngle :: (Floating a) => Angle a -> Angle aflipAngle (Radians r) = Radians (-r)-- | Flip Angle and normalize the resultflipAngleNorm :: (Floating a, Real a) => Angle a -> Angle aflipAngleNorm = normAngle . flipAngle-- | Add degrees to AngleaddAngleDegrees :: (Floating r, Integral d) => Angle r -> d -> Angle raddAngleDegrees ang deg = addAngle ang $ angleFromDegrees deg-- | Add radians to AngleaddAngleRadians :: (Floating r) => Angle r -> r -> Angle raddAngleRadians (Radians r1) r2 = Radians $ r1 + r2-- | Add turns to AngleaddAngleTurns :: (Floating r, Real t) => Angle r -> t -> Angle raddAngleTurns ang turn = addAngle ang $ angleFromTurns turn-- Trigonometric functions-- | Sine of the anglesinAngle :: (Floating a) => Angle a -> asinAngle (Radians r) = sin r-- | Cosine of the anglecosAngle :: (Floating a) => Angle a -> acosAngle (Radians r) = cos r-- | Tangent of the angletanAngle :: (Floating a) => Angle a -> atanAngle (Radians r) = tan r-- | Cotangent of the anglecotAngle :: (Floating a) => Angle a -> acotAngle (Radians r) = 1 / (tan r)-- Inverse trigonometric functions-- | Create angle from inverse sineasinAngle :: (Floating a) => a -> Angle aasinAngle x = Radians $ asin x-- | Create angle from inverse cosineacosAngle :: (Floating a) => a -> Angle aacosAngle x = Radians $ acos x-- | Create angle from inverse tangentatanAngle :: (Floating a) => a -> Angle aatanAngle x = Radians $ atan x-- | Create angle from inverse cotangentacotAngle :: (Floating a) => a -> Angle aacotAngle x = Radians $ (pi/2) - (atan x)The finished library is available here:Haskell Angle Library
Angle type in Haskell
haskell
I would use newtype instead of data to define Angle, like this:newtype Angle a = Radians { angleValueRadians :: a }This will automatically create the angleValueRadians function to get the angle value, and will be a bit more efficient.Here is a good answer explaining the differences between data, newtype, and type: https://stackoverflow.com/a/21081227/1525759You could use function composition to define many of your functions instead of pattern matching (pattern matching isn't bad, I just prefer this style):sinAngle :: (Floating a) => Angle a -> asinAngle = sin . angleValueRadiansAlso, if you made Angle an instance of Applicative, you could define addAngle like this:addAngle :: (Floating a) => Angle a -> Angle a -> Angle aaddAngle r1 r2 = (+) <$> r1 <*> r2-- or even this, because `f <$> x <*> y` is the same as `liftA2 f x y`:addAngle = liftA2 (+)Here's how you could do that:instance Functor Angle where    fmap f (Radians x) = Radians (f x)instance Applicative Angle where    pure = Radians           Radians f <*> r = fmap f r