Angle type in Haskell 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 where

import Data.Fixed -- mod'

data Angle a = Radians a deriving (Eq, Show)


-- Creating Angle from a value


-- | Create an Angle with the given degrees
angleFromDegrees :: (Integral d, Floating r) =&gt; d -&gt; Angle r
angleFromDegrees x = Radians $ (realToFrac x) * pi/180

-- | Create an Angle with the given turns
angleFromTurns :: (Real t, Floating r) =&gt; t -&gt; Angle r
angleFromTurns x = Radians $ (realToFrac x) * pi*2

-- | Create an Angle with the given turns
angleFromRadians :: (Floating r) =&gt; r -&gt; Angle r
angleFromRadians x = Radians x


-- Get the value from Angle


-- | Get degrees from an Angle
angleValueDegrees :: (Floating r, RealFrac r, Integral d) =&gt; Angle r -&gt; d
angleValueDegrees (Radians x) = round $ x / pi * 180.0

-- | Get radians from an Angle
angleValueRadians :: (Floating r) =&gt; Angle r -&gt; r
angleValueRadians (Radians x) = x

-- | Get turns from Angle
angleValueTurns :: (Floating r) =&gt; Angle r -&gt; r
angleValueTurns (Radians x) = x / (pi*2)


-- Basic functions


-- | Adding two angles
addAngle :: (Floating a) =&gt; Angle a -&gt; Angle a -&gt; Angle a
addAngle (Radians r1) (Radians r2) = Radians $ r1 + r2

-- | Normalize Angle: transforming back to (0-2pi)
normAngle :: (Floating a, Real a) =&gt; Angle a -&gt; Angle a
normAngle (Radians r) = Radians $ mod' r (pi*2)

-- | Add two angles and normalize the result
addAngleNorm :: (Floating a, Real a) =&gt; Angle a -&gt; Angle a -&gt; Angle a
addAngleNorm a b = normAngle $ addAngle a b

-- | Distance between two angles
distAngle :: (Floating a, Real a) =&gt; Angle a -&gt; Angle a -&gt; Angle a
distAngle (Radians r1) (Radians r2) = Radians $ if (a' &lt; b') then a' else b'
    where
        a' = mod' (r1-r2) (pi*2)
        b' = mod' (r2-r1) (pi*2)

-- | Flip Angle
flipAngle :: (Floating a) =&gt; Angle a -&gt; Angle a
flipAngle (Radians r) = Radians (-r)

-- | Flip Angle and normalize the result
flipAngleNorm :: (Floating a, Real a) =&gt; Angle a -&gt; Angle a
flipAngleNorm = normAngle . flipAngle

-- | Add degrees to Angle
addAngleDegrees :: (Floating r, Integral d) =&gt; Angle r -&gt; d -&gt; Angle r
addAngleDegrees ang deg = addAngle ang $ angleFromDegrees deg

-- | Add radians to Angle
addAngleRadians :: (Floating r) =&gt; Angle r -&gt; r -&gt; Angle r
addAngleRadians (Radians r1) r2 = Radians $ r1 + r2

-- | Add turns to Angle
addAngleTurns :: (Floating r, Real t) =&gt; Angle r -&gt; t -&gt; Angle r
addAngleTurns ang turn = addAngle ang $ angleFromTurns turn

-- Trigonometric functions


-- | Sine of the angle
sinAngle :: (Floating a) =&gt; Angle a -&gt; a
sinAngle (Radians r) = sin r

-- | Cosine of the angle
cosAngle :: (Floating a) =&gt; Angle a -&gt; a
cosAngle (Radians r) = cos r

-- | Tangent of the angle
tanAngle :: (Floating a) =&gt; Angle a -&gt; a
tanAngle (Radians r) = tan r

-- | Cotangent of the angle
cotAngle :: (Floating a) =&gt; Angle a -&gt; a
cotAngle (Radians r) = 1 / (tan r)


-- Inverse trigonometric functions


-- | Create angle from inverse sine
asinAngle :: (Floating a) =&gt; a -&gt; Angle a
asinAngle x = Radians $ asin x

-- | Create angle from inverse cosine
acosAngle :: (Floating a) =&gt; a -&gt; Angle a
acosAngle x = Radians $ acos x

-- | Create angle from inverse tangent
atanAngle :: (Floating a) =&gt; a -&gt; Angle a
atanAngle x = Radians $ atan x

-- | Create angle from inverse cotangent
acotAngle :: (Floating a) =&gt; a -&gt; Angle a
acotAngle x = Radians $ (pi/2) - (atan x)




The finished library is available here:

Haskell Angle Library
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/1525759

You 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) =&gt; Angle a -&gt; a
sinAngle = sin . angleValueRadians


Also, if you made Angle an instance of Applicative, you could define addAngle like this:

addAngle :: (Floating a) =&gt; Angle a -&gt; Angle a -&gt; Angle a
addAngle r1 r2 = (+) &lt;$&gt; r1 &lt;*&gt; r2
-- or even this, because `f &lt;$&gt; x &lt;*&gt; 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 &lt;*&gt; r = fmap f r