user3248346
user3248346

Reputation:

Correct Use of Typeclasses in Haskell

I wrote a typeclass to avoid having to write a duplicate function for a different type as below:

import Statistics.Distribution.Normal
import Data.Random

d1 :: Double -> Double -> Double -> Double -> Double -> Double
d1 s k r v t = ( log ( s / k ) + ( ( v * v ) / 2 ) * t ) / ( v * sqrt t )

d2 :: Double -> Double -> Double -> Double -> Double -> Double
d2 s k r v t = d1 s k r v t - v * sqrt t

call :: Double -> Double -> Double -> Double -> Double -> Double
call s k r t v = exp ( -r * t ) * ( s * cdf normal ( d1 s k r v t )
                                  - k * cdf normal ( d2 s k r v t ) )
  where normal = Normal (0 :: Double) 1

put :: Double -> Double -> Double -> Double -> Double -> Double
put s k r t v = exp ( -r * t ) * ( k * cdf normal ( - d2 s k r v t )
                                 - s * cdf normal ( - d1 s k r v t ) )
  where normal = Normal (0 :: Double) 1

class Black a where
  price :: a -> Double -> Double -> Double

instance Black ( Option Future ) where
  price ( Option ( Future s ) Call European k t ) r v = call s k r t v
  price ( Option ( Future s ) Put European k t ) r v = put s k r t v

instance Black ( Option Forward ) where
  price ( Option ( Forward s ) Call European k t ) r v = call s k r t v
  price ( Option ( Forward s ) Put European k t ) r v = put s k r t v

Is this a valid use of typeclasses? The reason I am asking is that I am not overloading the definition of the price function for any given type. All I am doing is avoiding having to write:

priceFuture :: (Option Future) -> Double -> Double -> Double
// impl
priceFoward :: (Option Forward) -> Double -> Double -> Double
impl



data Option a = Option a Type Style Strike Expiration deriving (Show)
data Future = Future Price deriving (Show)
data Forward = Forward Price deriving (Show)

type Barrier = Double
type Expiration = Double
type Price = Double
type Strike = Double
type Value = Double
type Dividend = Double
type Rate = Double

data Type = Call | Put deriving (Eq, Show)
data Style = European | American deriving (Eq, Show)

Upvotes: 1

Views: 119

Answers (2)

Daniel Wagner
Daniel Wagner

Reputation: 153342

I think I would just make the the argument to Option be a phantom type:

data Option a = Option Price Type Style Strike
data Future
data Forward

price :: Option a -> Double -> Double -> Double
price (Option s Call European k t) r v = call s k r t v
price (Option s Put  European k t) r v = put  s k r t v

Much less repetition, and no need for a type-class, but you still get a type-level distinction between forward options (Option Forward) and future options (Option Future) should you need that elsewhere. If you're really excited, you could turn on DataKinds to make sure that Future and Forward are the only two possible type-level arguments to Option.

Upvotes: 5

Paul S
Paul S

Reputation: 7755

How about this?

data Option = Option ForFut Type Style Strike Expiration deriving (Show)
data ForFut = Forward Price | Future Price deriving (Show)

type Barrier = Double
type Expiration = Double
type Price = Double
type Strike = Double
type Value = Double
type Dividend = Double

type Rate = Double

data Type = Call | Put deriving (Eq, Show)
data Style = European | American deriving (Eq, Show)

call :: Double -> Double -> Double -> Double -> Double -> Double
call = undefined

put :: Double -> Double -> Double -> Double -> Double -> Double
put = undefined

price :: Option -> Double -> Double -> Double
price ( Option ( Future s ) Call European k t ) r v = call s k r t v
price ( Option ( Future s ) Put European k t ) r v = put s k r t v
price ( Option ( Forward s ) Call European k t ) r v = call s k r t v
price ( Option ( Forward s ) Put European k t ) r v = put s k r t v

So I've combined the Forward and Future types into a single type. This then avoids the need to make Option a higher kinded type. The type class can then be removed, and price can be defined with simple pattern matching.

Upvotes: 1

Related Questions