Polymorphism and Typeclasses

Table of Contents

  • Index
  • Repository
  • 1 Polymorphism and Typeclasses

    1.1 Polymorphism

    1.1.1 Parametric Polymorphism

    Parametric polymorphism - Type of a value has one or more unconstrained type variables. The type variable can take any value.

    • Single algorithm can have many types.
    • Example: Haskell function id, head and tail

    Examples:

    -- Identity function.
    id :: a -> a 
    id x = x 
    
    > id "hello"
    "hello"
    it :: [Char]
    > id (Just 10)
    Just 10
    it :: Num a => Maybe a
    > 
    
    -- List functions.
    --
    --
    -- The type 'a' can be replaced by Int, String, Maybe Int, (Int -> Int)
    --  or anything.
    --------------------
    
    head (x:xs) = x
    head []     = error "*** Exception: Prelude.head: empty list"              
    
    > :t head
    head :: [a] -> a
    > 
    > :t tail
    tail :: [a] -> [a]
    > 
    
    > :t head ["hello", "world", "haskell"]
    head ["hello", "world", "haskell"] :: [Char]
    > head ["hello", "world", "haskell"]
    "hello"
    it 
    
    > :t head [\x -> x + 1, \a -> a * 3, \b -> b -5]
    head [\x -> x + 1, \a -> a * 3, \b -> b -5] :: Num a => a -> a
    > head [\x -> x + 1, \a -> a * 3, \b -> b -5] $ 9
    10
    it :: Num a => a
    >
    

    1.1.2 Ad-hoc Polymorphism

    It allows functions to have different algorithms for each type. The choice of the algorithme is determined by the context.

    In Haskell ad-hoc polymorphism is achieved through typeclasses which are similar to Java interface. A typeclass defines a set of functions that must be implemented by every instance of a type class.

    The functions defined by a typeclass can have different behaviors depending on the instance of type class and can be used with all type class instances. It allows the functions and operators (binary functions) such as (+) plus, (-) minus, (>>=) monad bind operator to be reused with many different types and have different algorithms for each type.

    Benefits:

    • Reuse operators and functiosn with different types.
    • Operator overloading.
    • Functions with a different algorithm for each type.
    • Code Reuse for free.

    Example: The type class Functor

    The function fmap defined by the type class Functor has different behaviors (algorithms) for each instance such as list, Maybe and Either types.

    -- Functor Type Class 
    -- 
    > :info Functor
    class Functor (f :: * -> *) where
      fmap :: (a -> b) -> f a -> f b
      (<$) :: a -> f b -> f a
      {-# MINIMAL fmap #-}
    
    
    -- Instances of Functor type class 
    --
    > :info Functor
    class Functor (f :: * -> *) where
      fmap :: (a -> b) -> f a -> f b
      (<$) :: a -> f b -> f a
      {-# MINIMAL fmap #-}
        -- Defined in ‘GHC.Base’
    instance Functor (Either a) -- Defined in ‘Data.Either’
    instance Functor [] -- Defined in ‘GHC.Base’
    instance Functor Maybe -- Defined in ‘GHC.Base’
    instance Functor IO -- Defined in ‘GHC.Base’
    instance Functor ((->) r) -- Defined in ‘GHC.Base’
    instance Functor ((,) a) -- Defined in ‘GHC.Base’
    > 
    
    -- Function fmap applies a function (a -> b) to a Functor. 
    --
    > :t fmap
    fmap :: Functor f => (a -> b) -> f a -> f b
    > 
    
    --- fmap for Functor instance List 
    -------------------------------------------
    data Maybe a = Nothing | Just a 
    
    instance Functor [a] where
        fmap = map 
    
    > fmap (\x -> x + 1) [1, 2, 3, 4, 5]
    [2,3,4,5,6]
    it :: Num b => [b]
    > 
    
    --- fmap for Functor instance Maybe
    -------------------------------------------
    
    instance Functor (Maybe a) where
        fmap fn (Just a) = Just (fn a)
        fmap fn Nothing  = Nothing 
    
    > :t fmap (\x -> x + 1) (Just 10)
    fmap (\x -> x + 1) (Just 10) :: Num b => Maybe b
    > 
    
    > fmap (\x -> x + 1) (Just 10)
    Just 11
    it :: Num b => Maybe b
    > 
    > fmap (\x -> x + 1) Nothing
    Nothing
    it :: Num b => Maybe b
    > 
    
    
    --- fmap for Functor instance Either 
    -------------------------------------------
    
    data Either a b = Left a | Right b
    
    instance Functor (Either a b) where
        fmap fn (Right b) = Right (fn b)
        fmap fn (Left a)  = Left a 
    
    > fmap (\x -> x + 1) (Right 10)
    Right 11
    it :: Num b => Either a b                        
    
    > fmap (\x -> x + 1) (Left "Error: I can't parse the number")
    Left "Error: I can't parse the number"
    it :: Num b => Either [Char] b
    >
    

    1.2 Typeclasses

    Standard Typeclasses

    • Num - Numeric types. Defines numertic operators (+), (-), (*) and functiosn abs and signum.
      • Methods: (+), (-), (*), abs, signum
      • Standard Instances:
        • Int - 29 bits Signed Integer
        • Integer - Arbitrary precision integer
        • Float - IEEE 32-bits Float point
        • Double - IEEE 64-bit Float Point
    • Ord - Comparison.
      • Operators: (<) (<=) (>) (>=) min and max
    • Enum - Enumeration. Allows syntax such as [1 … 10], [0.5 .. 0.1 .. 10] ['a' .. 'z']
    • Eq - Equality.
      • Operator (=) and (/)
    • Show - Defines method show, which converts an instance of this type class to string.
    • Read - Defines the function read, which parses a string into a value.

    Type Classes

    Num

    > :info Num
    class Num a where
      (+) :: a -> a -> a
      (-) :: a -> a -> a
      (*) :: a -> a -> a
      negate :: a -> a
      abs :: a -> a
      signum :: a -> a
      fromInteger :: Integer -> a
      {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
    

    Fractional

    class Num a => Fractional a where 
      (/) :: a -> a -> a
      recip :: a -> a
      fromRational :: Rational -> a
    

    Eq

    class Eq a where
      (==) :: a -> a -> Bool
      (/=) :: a -> a -> Bool
    

    Ord

    data Ordering = LT | EQ | GT
    
    class Eq a => Ord a where
      compare :: a -> a -> Ordering
      (<) :: a -> a -> Bool
      (<=) :: a -> a -> Bool
      (>) :: a -> a -> Bool
      (>=) :: a -> a -> Bool
      max :: a -> a -> a
      min :: a -> a -> a
      {-# MINIMAL compare | (<=) #-}
    

    Enum

    > :info Enum
    class Enum a where
      succ :: a -> a
      pred :: a -> a
      toEnum :: Int -> a
      fromEnum :: a -> Int
      enumFrom :: a -> [a]
      enumFromThen :: a -> a -> [a]
      enumFromTo :: a -> a -> [a]
      enumFromThenTo :: a -> a -> a -> [a]
      {-# MINIMAL toEnum, fromEnum #-}
    

    1.3 Examples

    1.3.1 Refactoring functions to work with many types

    Example: Typeclasses allows writing generic functions that can be used with all instances of a type class. In this example the function fun1 can be refactored to work with all instances of Num typeclass.

    :{
    fun1 :: Double -> Double -> Double
    fun1 x y = 2 * x + 4 * y         
    :}
    
     > fun1 3 4
    22.0
    it :: Double
    > fun1 3 4.23
    22.92
    it :: Double
    >
    
    > let a = 10 :: Int
    a :: Int
    > let c = 20 :: Int
    c :: Int
    > 
    > fun1 a b
    
    <interactive>:189:6: error:
         Couldn't match expected typeDouble’ with actual typeInt In the first argument of ‘fun1’, namely ‘a’
          In the expression: fun1 a b
          In an equation for ‘it’: it = fun1 a b
    
    <interactive>:189:8: error: Variable not in scope: b :: Double
    > 
    
    --- This function can be rewritten to work with
    --- all members of Num type class.
    
    :{
    fun :: Num a => a -> a -> a
    fun x y = 2 * x + 4 * y         
    :}
    
    > fun a c
    100
    it :: Int
    > fun 3.0 10.3
    47.2
    it :: Fractional a => a
    > 
     > let x = 10.4 :: Double
    x :: Double
    > let y = 20.5 :: Double
    y :: Double
    > fun x y 
    102.8
    it :: Double
    >
    

    Example: Generic monad combinator. The functions mapM2 applies a function fn to two monad instances.

    > :t (>>=)
    (>>=) :: Monad m => m a -> (a -> m b) -> m b
    > 
    > :t return
    return :: Monad m => a -> m a
    > 
    
    > :info Monad
    class Applicative m => Monad (m :: * -> *) where
      (>>=) :: m a -> (a -> m b) -> m b
      (>>) :: m a -> m b -> m b
      return :: a -> m a
      fail :: String -> m a
      {-# MINIMAL (>>=) #-}
            -- Defined in ‘GHC.Base’
    instance Monad (Either e) -- Defined in ‘Data.Either’
    instance Monad [] -- Defined in ‘GHC.Base’
    instance Monad Maybe -- Defined in ‘GHC.Base’
    instance Monad IO -- Defined in ‘GHC.Base’
    instance Monad ((->) r) -- Defined in ‘GHC.Base’
    instance Monoid a => Monad ((,) a) -- Defined in ‘GHC.Base’
    > 
    
    
    :{
    mapM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
    mapM2 fn ma mb = do
      a <- ma
      b <- mb
      return $ fn a b
    :}
    
    
    :{
    mapM2a :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
    mapM2a fn ma mb = 
      ma >>= \ a ->
      mb >>= \ b -> return $ fn a b
    :}
    
    
    > mapM2 (+) (Just 10) (Just 4)
    Just 14
    it :: Num c => Maybe c
    > mapM2 (+) (Just 10) Nothing
    Nothing
    it :: Num c => Maybe c
    > mapM2 (+) Nothing (Just 4)
    Nothing
    it :: Num c => Maybe c
    > 
    
    > mapM2 (+) (Right 10) (Right 5)
    
    <interactive>:225:12: error:
         Couldn't match expected typeMaybe c’
                      with actual typeEither a0 Integer In the second argument of ‘mapM2’, namely ‘(Right 10)’
          In the expression: mapM2 (+) (Right 10) (Right 5)
          In an equation for ‘it’: it = mapM2 (+) (Right 10) (Right 5)
         Relevant bindings include
    
    > mapM2 (+) [1, 2, 3] [5, 6]
    
    <interactive>:227:11: error:
         Couldn't match expected typeMaybe c’
                      with actual type ‘[Integer]’
         In the second argument of ‘mapM2’, namely ‘[1, 2, 3]’
          In the expression: mapM2 (+) [1, 2, 3] [5, 6]
          In an equation for ‘it’: it = mapM2 (+) [1, 2, 3] [5, 6]
         Relevant bindings include
            it :: Maybe c (bound at <interactive>:227:1)
    
    <interactive>:227:21: error:
         Couldn't match expected typeMaybe c’
                      with actual type ‘[Integer]’
         In the third argument of ‘mapM2’, namely ‘[5, 6]’
          In the expression: mapM2 (+) [1, 2, 3] [5, 6]
          In an equation for ‘it’: it = mapM2 (+) [1, 2, 3] [5, 6]
         Relevant bindings include
            it :: Maybe c (bound at <interactive>:227:1)
    > 
    
    
    > mapM2a (+) (Just 10) (Just 15)
    Just 25
    it :: Num c => Maybe c
    > 
    > mapM2a (+) [1, 2, 3] [4, 5]
    
    <interactive>:258:12: error:
         Couldn't match expected typeMaybe c’
                      with actual type ‘[Integer]’
         In the second argument of ‘mapM2a’, namely ‘[1, 2, 3]’
          In the expression: mapM2a (+) [1, 2, 3] [4, 5]
          In an equation for ‘it’: it = mapM2a (+) [1, 2, 3] [4, 5]
         Relevant bindings include
            it :: Maybe c (bound at <interactive>:258:1)
    
    --- This function can be rewritten to operate on any Monad instance
    
    :{
    mapM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
    mapM2 fn ma mb = do
      a <- ma
      b <- mb
      return $ fn a b
    :}
    
    :{
    mapM2b :: Monad m => (a -> b -> c) -> m a -> m b -> m c
    mapM2b fn ma mb = 
      ma >>= \a -> 
      mb >>= \b -> 
      return $ fn a b
    :}
    
    
    > mapM2 (+) (Just 10) (Just 4)
    Just 14
    it :: Num c => Maybe c
    
    > mapM2 (,) [1, 2, 3] ["a", "b"]
    [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]
    it :: Num a => [(a, [Char])]
    >
    
    > mapM2 (+) (Right 10) (Right 4)
    Right 14
    it :: Num c => Either a c
    
    > mapM2 (+) (Right 10) (Left "Failed")
    Left "Failed"
    it :: Num c => Either [Char] c
    > 
    
    > mapM2b (+) (Just 10) (Just 5)
    Just 15
    it :: Num c => Maybe c
    > mapM2b (+) (Just 10) Nothing
    Nothing
    it :: Num c => Maybe c
    > mapM2b (+) (Right 10) (Right 5)
    Right 15
    it :: Num c => Either a c
    >
    

    1.3.2 Defining typeclasses instances

    1.3.2.1 Defining a new instance of typeclass Functor
    > data Identity a = Identity a deriving (Eq, Read, Show)
    data Identity a = Identity a
    
    > Identity 10
    Identity 10
    it :: Num a => Identity a
    
    > Identity "Hello world"
    Identity "Hello world"
    it :: Identity [Char]
    > 
    
    :{ 
    instance Functor Identity where
        fmap fn (Identity a) = Identity (fn a)
    :}
    
    
    > fmap (\x -> x + 1) (Identity 9)
    Identity 10
    it :: Num b => Identity b
    
    > fmap (\x -> x ++ " world") (Identity "Hello ")
    Identity "Hello  world"
    it :: Identity [Char]
    >
    
    1.3.2.2 Defining a new instance of typeclass Num.
    > :info Num
    class Num a where
      (+) :: a -> a -> a
      (-) :: a -> a -> a
      (*) :: a -> a -> a
      negate :: a -> a
      abs :: a -> a
      signum :: a -> a
      fromInteger :: Integer -> a
      {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
        -- Defined in ‘GHC.Num’
    
    instance Num Word -- Defined in ‘GHC.Num’
    instance Num Integer -- Defined in ‘GHC.Num’
    instance Num Int -- Defined in ‘GHC.Num’
    instance Num Float -- Defined in ‘GHC.Float’
    instance Num Double -- Defined in ‘GHC.Float’
    > 
    
    
    > data Vector3D = Vector3D (Double, Double, Double) deriving (Eq, Read, Show)
    data Vector3D = Vector3D (Double, Double, Double)
    > 
    
    
    -- Extractors
    >  let vX (Vector3D (x, y, z)) = x 
    vX :: Vector3D -> Double
    >
    
    > let vY (Vector3D (x, y, z)) = y 
    vY :: Vector3D -> Double
    > 
    
    > let vZ (Vector3D (x, y, z)) = z
    vZ :: Vector3D -> Double
    > 
    
    -- Smart constructor 
    
    > let vec x y z = Vector3D (x, y, z)
    vec :: Double -> Double -> Double -> Vector3D
    >
    
    
    :{
    instance Num Vector3D where
    
        -- (+) :: Num a => a -> a -> a
        Vector3D (x1, y1, z1) + Vector3D (x2, y2, z2) =
            vec (x1 + x2) (y1 + y2) (z1 + z2)
    
        -- (*) :: Num a => a -> a -> a
        Vector3D (x1, y1, z1) * Vector3D (x2, y2, z2) =
             vec (x1 * x2) (y1 * y2) (z1 * z2)
    
        -- (-) :: Num a => a -> a -> a
        Vector3D (x1, y1, z1) - Vector3D (x2, y2, z2) =
            vec (x1 - x2) (y1 - y2) (z1 - z2)
    
        -- abs :: Num a => a -> a            
        abs (Vector3D (x1, y1, z1)) = vec (abs x1) (abs y1) (abs z1)
    
        -- Dummy operation for Vector3D - Don't care about this
        -- operation.
        --
        -- signum :: Num a => a -> a
        signum (Vector3D (x1, y1, z1)) =  Vector3D (-x1, -y1, -z1)
    
        -- Dummy operation
        --                                  
        -- fromInteger :: Num a => Integer -> a
        fromInteger x  = let a = fromIntegral x
                         in Vector3D (a, a, a)
    
    :}
    
    
    > let norm (Vector3D (x, y, z)) = sqrt (x * x + y * y + z * z)
    norm :: Vector3D -> Double
    >
    
    > let dist va vb = norm $ va - vb
    dist :: Vector3D -> Vector3D -> Double
    > 
    
    > let scale f (Vector3D (x, y, z)) = vec (f * x) (f * y) (f * z)
    scale :: Double -> Vector3D -> Vector3D
    > 
    
    --- Get unitary 3D vector with same direction as v
    :{
    versor v = scale f v
        where 
          f = 1.0 / norm v
    :}      
    versor :: Vector3D -> Vector3D
    >          
    
    
    > vec 1 2 3
    Vector3D (1.0,2.0,3.0)
    it :: Vector3D
    > 
    > vec 1 2 3 + vec 3 4 5
    Vector3D (4.0,6.0,8.0)
    it :: Vector3D
    > 
    > vec 1 2 3 * vec 3 4 5
    Vector3D (3.0,8.0,15.0)
    it :: Vector3D
    > 
    > vec 1 2 3 - vec 3 4 5
    Vector3D (-2.0,-2.0,-2.0)
    it :: Vector3D
    > 
    > vec 3 4 5 - vec 1 2 3
    Vector3D (2.0,2.0,2.0)
    it :: Vector3D
    > 
    > norm $ vec 1 2 3
    3.7416573867739413
    it :: Double
    > 
    
    > dist (vec 10 4 5.0) (vec 2 7 8)
    9.055385138137417
    it :: Double
    > 
    
    > fromInteger 15 :: Vector3D 
    Vector3D (15.0,15.0,15.0)
    it :: Vector3D
    > 
    
    > - (vec 1 2 3)
    Vector3D (-1.0,-2.0,-3.0)
    it :: Vector3D
    > 
    
    > let ux k = scale k (Vector3D (1, 0, 0))
    ux :: Double -> Vector3D
    > 
    > let uy k = scale k (Vector3D (0, 1, 0))
    uy :: Double -> Vector3D
    > 
    > let uz k = scale k (Vector3D (0, 0, 1))
    uz :: Double -> Vector3D
    > 
    > ux 10
    Vector3D (10.0,0.0,0.0)
    it :: Vector3D
    > 
    > ux 5.0
    Vector3D (5.0,0.0,0.0)
    it :: Vector3D
    > 
    > uz 9
    Vector3D (0.0,0.0,9.0)
    it :: Vector3D
    > 
    > ux 2 + uy 4 + uz 7
    Vector3D (2.0,4.0,7.0)
    it :: Vector3D
    > 
    
    -- Declare an instance of typeclass show to change the way a type is displayed.
    :{
    instance Show Vector3D where
        show (Vector3D (x, y, z)) =
            show $ "Vector = " ++ show x ++ "i + " ++ show y ++ "j + " ++ show z ++ "k"
    
    :}   
    
    > let v1 = Vector3D (4.5, 3.0, 2.0)
    v1 :: Vector3D
    > v1
    "Vector = 4.5i + 3.0j + 2.0k"
    it :: Vector3D
    > 
    > let v1 = Vector3D (4.5, -3.0, 2.0)
    v1 :: Vector3D
    > v1
    "Vector = 4.5i + -3.0j + 2.0k"
    it :: Vector3D
    >
    
    1.3.2.3 Complex Number Implementation
    import Text.Printf (printf)
    
    
    
    -- Complex number in rectangular form
    -- 
    data Cpl = Cpl Double Double deriving (Eq, Read)
    
    
    > let real (Cpl x y) = x 
    real :: Cpl -> Double
    > 
    
    > let imag (Cpl x y) = y 
    imag :: Cpl -> Double
    >
    
    > let rad2deg rad = 180 / pi * rad 
    rad2deg :: Floating a => a -> a
    
    > let deg2rad deg = pi / 180 * deg
    deg2rad :: Floating a => a -> a
    > 
    
    -- Magnitude 
    > let mag (Cpl x y) = sqrt ( x * x + y * y)
    mag :: Cpl -> Double
    
    -- Angle 
    let phase (Cpl x y) = rad2deg $ atan2 y x 
    
    
    -- Complex in polar form with angle in degrees.                       
    :{
    let polard r a =
            let ar = deg2rad a -- angle in radians
            in Cpl (r * cos ar) (r * sin ar)
    :}
    
    
    > let (+.) x y = Cpl x y
    (+.) :: Double -> Double -> Cpl
    > 
    
    > let (<@) r a = polard r a
    (<@) :: Double -> Double -> Cpl
    > 
    
    
    
    :{         
    instance Show Cpl where
        show (Cpl x y) = printf "%.3f + %.3fj" x y 
    :}
    
    :{ 
    instance Num Cpl where
        (Cpl x1 y1) + (Cpl x2 y2) =
            Cpl (x1 + x2) (y1 + y2)
    
        (Cpl x1 y1) - (Cpl x2 y2) =
            Cpl (x1 - x2) (y1 - y2)
    
        {- (x1 + y1*j)(x2 + y2*j) = x1 x2 + x1 y2 j + y1 x2 j + y1 y2 j j 
                                  = (x1 x2 - y1y2) + (x1 y2 + y1 x2)j
        -}                            
        (Cpl x1 y1) * (Cpl x2 y2) =
            Cpl (x1 * x2 - y1 * y2) (x1 * y2 + y1 * x2)
    
        negate (Cpl x y) = Cpl (-x) (-y)
    
        abs (Cpl x y) = Cpl (sqrt $ x * x + y * y) 0                   
    
        signum (Cpl x y) = Cpl (signum x) 0
    
        fromInteger x = Cpl (fromIntegral x) 0 
    :}
    
    
    > :info Fractional
    class Num a => Fractional a where
      (/) :: a -> a -> a
      recip :: a -> a
      fromRational :: Rational -> a
      {-# MINIMAL fromRational, (recip | (/)) #-}
            -- Defined in ‘GHC.Real’
    instance Fractional Float -- Defined in ‘GHC.Float’
    instance Fractional Double -- Defined in ‘GHC.Float’
    > 
    
    :{
    instance Fractional Cpl where
        recip (Cpl x y) = let s = x * x + y * y
                          in  Cpl (x / s) (- y / s)
    
        fromRational x = Cpl 1.0 0 -- Dummy operation to satisfy the type class                      
    :}   
    
    
    > 10 +. 4
    10.000 + 4.000j
    it :: Cpl
    > 
    
    > 20.0 <@ 45.0
    14.142 + 14.142j
    it :: Cpl
    > 10.0 <@ 60.0
    5.000 + 8.660j
    it :: Cpl
    > 10.0 <@ 30.0
    8.660 + 5.000j
    it :: Cpl
    > 
    
    
    > polard 2.0 (-135.0)
    -1.414 + -1.414j
    it :: Cpl
    > 
    > phase $ polard 2.0 (-135.0)
    -135.0
    it :: Double
    > mag $ polard 2.0 (-135.0)
    2.0
    it :: Double
    > 
    
    
    > Cpl 3.0 (-4.0)
    3.000 + -4.000j
    it :: Cpl
    > 
    > Cpl 3.0 4.0 
    3.000 + 4.000j
    it :: Cpl
    > 
    > Cpl 3.0 0.0
    3.000 + 0.000j
    it :: Cpl
    > 
    
    > Cpl 10 3 + Cpl 4 5
    14.000 + 8.000j
    it :: Cpl
    > Cpl 10 3 * Cpl 4 5
    25.000 + 62.000j
    it :: Cpl
    > Cpl 0 1 * Cpl 0 1
    -1.000 + 0.000j
    it :: Cpl
    > 
    
    > let c1 = 10 +. 20
    c1 :: Cpl
    > c1
    10.000 + 20.000j
    it :: Cpl
    > let c2 = 5 +. 0 
    c2 :: Cpl
    > 
    > let c3 = 10.0 <@ 30.0 
    c3 :: Cpl
    > c3
    8.660 + 5.000j
    it :: Cpl
    > 
    
    > mag $ recip c3 
    0.1
    it :: Double
    > phase $ recip c3 
    -29.999999999999993
    it :: Double
    > 
    
    > c1 / c2 
    2.000 + 4.000j
    it :: Cpl
    > 
    > c1 / c3 
    1.866 + 1.232j
    it :: Cpl
    >
    
    1.3.2.4 Vector Arithmetic
    data Vector = Vector [Double] deriving (Eq, Read, Show)
    
    > :t uncurry
    uncurry :: (a -> b -> c) -> (a, b) -> c
    >               
    
    :{ 
    instance Num Vector where   
    
        (Vector xs) + (Vector ys) = Vector $ map (uncurry (+)) (zip xs ys)
    
        (Vector xs) - (Vector ys) = Vector $ map (uncurry (-)) (zip xs ys)
    
        (Vector xs) * (Vector ys) = Vector $ map (uncurry (*)) (zip xs ys)                                
    
        negate (Vector xs) = Vector $ map (\x -> -1.0 * x) xs
    
        abs (Vector xs) = Vector $ map abs xs
    
        signum (Vector xs) = Vector $ map signum xs
    
        -- Dummy operation (Don't care)                      
        fromInteger x = Vector [fromIntegral x]
    :}
    
    
    
    :{
    instance Fractional Vector where
        -- recip (Vector xs) = Vector $ map (\x -> 1 / x) xs
        --- Or
        (Vector xs) / (Vector ys) = Vector $ map (uncurry (/)) (zip xs ys)    
    
        -- Dummy operation - Don't care
        fromRational x = Vector [fromRational x]
    :}   
    
    let apply fn (Vector xs) = Vector (map fn xs)
    
    
    > let scale k = apply (\x -> x * k)
    scale :: Double -> Vector -> Vector
    
    
    > let repeatv x size = Vector (take size $ repeat x)
    repeatv :: Double -> Int -> Vector
    > 
    
    
    > 
    > :info Floating
    class Fractional a => Floating a where
      pi :: a
      exp :: a -> a
      log :: a -> a
      sqrt :: a -> a
      (**) :: a -> a -> a
      logBase :: a -> a -> a
      sin :: a -> a
      cos :: a -> a
      tan :: a -> a
      asin :: a -> a
      acos :: a -> a
      atan :: a -> a
      sinh :: a -> a
      cosh :: a -> a
      tanh :: a -> a
      asinh :: a -> a
      acosh :: a -> a
      atanh :: a -> a
      GHC.Float.log1p :: a -> a
      GHC.Float.expm1 :: a -> a
      GHC.Float.log1pexp :: a -> a
      GHC.Float.log1mexp :: a -> a
      {-# MINIMAL pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh,
                  asinh, acosh, atanh #-}
            -- Defined in ‘GHC.Float’
    instance Floating Float -- Defined in ‘GHC.Float’
    instance Floating Double -- Defined in ‘GHC.Float’
    > 
    
    :{
    instance Floating Vector where
        pi                    = Vector $ repeat pi
        exp  (Vector xs)      = Vector (map exp xs)
        log  (Vector xs)      = Vector (map log xs)
        sqrt  (Vector xs)     = Vector (map sqrt xs)
    
        (**) (Vector xs) (Vector ns) =
            let n = head ns
            in Vector $ map (\x -> x ** n) xs
    
        logBase (Vector xs) (Vector ns) =
            let n = head ns
            in Vector $ map (\x -> logBase x n) xs
    
        sin (Vector xs)       = Vector $ map sin xs
        cos (Vector xs)       = Vector $ map cos xs
        tan (Vector xs)       = Vector $ map tan xs
        asin (Vector xs)      = Vector $ map asin xs
        acos (Vector xs)      = Vector $ map acos xs
        atan (Vector xs)      = Vector $ map atan xs
        sinh (Vector xs)      = Vector $ map sinh xs
        cosh (Vector xs)      = Vector $ map cosh xs
        tanh (Vector xs)      = Vector $ map tanh xs
        asinh (Vector xs)     = Vector $ map asinh xs
        acosh (Vector xs)     = Vector $ map acosh xs
        atanh (Vector xs)     = Vector $ map atanh xs
    :}
    
    
    
    
    -- ------------------- Testing ------------------------ 
    
    
    
    > 10.0 :: Vector
    Vector [10.0]
    it :: Vector
    > 
    
    
    > let xs = Vector [1.0, 2.0, 3.0, 4.0]
    xs :: Vector
    > let ys = Vector [10.0, 3.0, 5.0, 6.0] 
    ys :: Vector
    > 
    
    
    > ys - xs
    Vector [9.0,1.0,2.0,2.0]
    it :: Vector
    > 
    > xs * ys 
    Vector [10.0,6.0,15.0,24.0]
    it :: Vector
    > 
    > 
    
    > apply sqrt xs
    Vector [1.0,1.4142135623730951,1.7320508075688772,2.0]
    it :: Vector
    > 
    > apply sqrt ys
    Vector [3.1622776601683795,1.7320508075688772,2.23606797749979,2.449489742783178]
    it :: Vector
    >
    
    > 
    > scale 4.0 ys + scale 3.0 xs
    Vector [43.0,18.0,29.0,36.0]
    it :: Vector
    > scale 4.0 ys / scale 3.0 xs
    Vector [13.333333333333334,2.0,2.2222222222222223,2.0]
    it :: Vector
    > 
    
    > xs + repeatv 3.0 5 + ys
    Vector [14.0,8.0,11.0,13.0]
    it :: Vector
    > 
    >
    
    > exp xs
    Vector [2.718281828459045,7.38905609893065,20.085536923187668,54.598150033144236]
    it :: Vector
    > 
    
    > xs + sqrt ys
    Vector [4.16227766016838,3.732050807568877,5.23606797749979,6.449489742783178]
    it :: Vector
    > 
    
    > xs ** 5.0 - scale 3.0 ys
    Vector [-29.0,23.0,228.0,1006.0]
    it :: Vector
    >
    
    1.3.2.5 Num a => Maybe a as instance of Num typeclass
    :{
    mapM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
    mapM2 fn ma mb = do
      a <- ma
      b <- mb
      return $ fn a b 
    :}
    
    :{
    instance Num a => Num (Maybe a) where
        (+) = mapM2 (+)
        (-) = mapM2 (-)
        (*) = mapM2 (*)
    
        negate = fmap negate
        abs    = fmap abs
        signum = fmap signum
    
        fromInteger x = Just (fromInteger x)
    :}
    

    Running in REPL:

    > Just 10 + Just 15
    Just 25
    it :: Num a => Maybe a
    
    > Just 10 + Nothing
    Nothing
    it :: Num a => Maybe a
    > 
    
    >  Just 10 + 20 * 5 + 3 * 4 * 5 
    Just 170
    it :: Num a => Maybe a
    
    > Just 10 + 20 * 5 + 3 * 4 * 5 + Nothing
    Nothing
    
    
    > signum $ Just (-10)
    Just (-1)
    it :: Num a => Maybe a
    > 
    > signum $ Just (10)
    Just 1
    it :: Num a => Maybe a
    > 
    
    > signum $ Nothing
    Nothing
    it :: Num a => Maybe a
    >
    

    1.3.3 Creating a type class

    :{
    class Shape a where
        shapePerimiter :: a -> Double
        shapeArea      :: a -> Double
    :}                 
    
    
    -- Paste in the REPL 
    > :{
    - class Shape where
    -     shapePerimiter :: Shape -> Double
    -     shapeArea :: Shape -> Double
    - :}                 
    class Shape a where
      shapePerimiter :: a -> Double
      shapeArea :: a -> Double
      {-# MINIMAL shapePerimiter, shapeArea #-}
    >     
    > 
    
    
    data Square a = Square a deriving (Eq, Read, Show)
    
    data Circle r = Circle r deriving (Eq, Read, Show)
    
    data Rectangle x y = Rectangle x y deriving (Eq, Read, Show)
    
    :{                   
    instance Shape (Square Double) where
        shapePerimiter (Square a) = 4.0 * a
        shapeArea (Square a) = a * a 
    :}
    
     :{                   
    instance Shape (Circle Double) where
        shapePerimiter (Circle r) = 2.0 * pi * r 
        shapeArea (Circle r) = pi * r * r 
    :}
    
    :{
    instance Shape (Rectangle Double Double) where
        shapePerimiter (Rectangle x y) = 2.0 * (x + y)
        shapeArea (Rectangle x y) = x * y
    :}    
    
    > let s1 = Square 10.0 :: Square Double
    s1 :: Square Double
    > let s2 = Circle 2.0 :: Circle Double
    s2 :: Circle Double
    > let s3 = Rectangle 5.0 4.0 :: Rectangle Double Double
    s3 :: Rectangle Double Double
    >
    
    > :t shapeArea 
    shapeArea :: Shape a => a -> Double
    > 
    > :t shapePerimiter 
    shapePerimiter :: Shape a => a -> Double
    > 
    
    
    > shapePerimiter s1
    40.0
    it :: Double
    > shapePerimiter s2
    12.566370614359172
    it :: Double
    > shapePerimiter s3
    18.0
    it :: Double
    > 
    > shapeArea s1
    100.0
    it :: Double
    > shapeArea s2
    12.566370614359172
    it :: Double
    > shapeArea s3
    20.0
    it :: Double
    >
    

    1.3.4 References

    See:

    Author: nobody

    Created: 2018-06-17 Sun 02:38

    Emacs 25.3.1 (Org mode 8.2.10)

    Validate