## Introduction

An important principle of good code design is to avoid repetition and it is known as do not repeat yourself (DRY).

#### Ad Hoc Polymorphism:

Ad Hoc Polymorphism is utilizing a possibly different implementations based on Types.

### Agenda

• Implement Type Class Pattern using Scala.
• Implement Type Class Pattern using Simulacrum.

### Code: By Scala

```trait Number[T] {

def plus(t1: T, t2: T): T
def minus(t1: T, t2: T): T
def divide(t1: T, t2: Int): T
def multiply(t1: T, t2: T): T
def sqrt(t1: T): T
}

object Number {
implicit object DoubleNumber extends Number[Double] {
override def plus(t1: Double, t2: Double): Double = t1 + t2
override def minus(t1: Double, t2: Double): Double = t1 - t2
override def divide(t1: Double, t2: Int): Double = t1 / t2
override def multiply(t1: Double, t2: Double): Double = t1 * t2
override def sqrt(t1: Double): Double = Math.sqrt(t1)
}

implicit object IntNumber extends Number[Int] {
override def plus(t1: Int, t2: Int): Int = t1 + t2 + 10
override def minus(t1: Int, t2: Int): Int = t1 - t2
override def divide(t1: Int, t2: Int): Int = t1 / t2
override def multiply(t1: Int, t2: Int): Int = t1 * t2
override def sqrt(t1: Int): Int = Math.sqrt(t1).toInt
}
}
```

```class StatsExample {

def mean[T: Number] (xs: Vector[T]): T = implicitly[Number[T]]
.divide (xs.reduce(implicitly[Number[T]].plus(_, _)), xs.size)

//assume vector is in sorted order
def median[T: Number] (xs: Vector[T]): T = xs(xs.size / 2)
}

object StatsExample extends App {

val intVector = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, 25)
val doubleVector = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
20.0, 21.0, 22.0, 23.0, 24.0, 25.0)

val example = new StatsExample
println(s"Mean (int) \${example.mean(intVector)}")
println(s"Median (int) \${example.median(intVector)}")

println(s"Mean (double) \${example.mean(doubleVector)}")
println(s"Median (double) \${example.median(doubleVector)}")
}
```

### Code: By Simulacrum

```@typeclass trait Number[T] {

@op("+") def plus(t1: T, t2: T): T
@op("-") def minus(t1: T, t2: T): T
@op("/") def divide(t1: T, t2: Int): T
@op("*") def multiply(t1: T, t2: T): T
@op("^") def sqrt(t1: T): T
}

object Number {
implicit object DoubleNumber extends Number[Double] {
override def plus(t1: Double, t2: Double): Double = t1 + t2
override def minus(t1: Double, t2: Double): Double = t1 - t2
override def divide(t1: Double, t2: Int): Double = t1 / t2
override def multiply(t1: Double, t2: Double): Double = t1 * t2
override def sqrt(t1: Double): Double = Math.sqrt(t1)
}

implicit object IntNumber extends Number[Int] {
override def plus(t1: Int, t2: Int): Int = t1 + t2
override def minus(t1: Int, t2: Int): Int = t1 - t2
override def divide(t1: Int, t2: Int): Int = t1 / t2
override def multiply(t1: Int, t2: Int): Int = t1 * t2
override def sqrt(t1: Int): Int = Math.sqrt(t1).toInt
}
}
```

```class StatsExample {

import Number.ops._

def mean[T: Number] (xs: Vector[T]): T = xs.reduce(_ + _) / xs.size

//assume vector is in sorted order
def median[T: Number] (xs: Vector[T]): T = xs(xs.size / 2)
}

object StatsExample extends App {

val intVector = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, 25)
val doubleVector = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
20.0, 21.0, 22.0, 23.0, 24.0, 25.0)

val example = new StatsExample
println(s"Mean (int) \${example.mean(intVector)}")
println(s"Median (int) \${example.median(intVector)}")

println(s"Mean (double) \${example.mean(doubleVector)}")
println(s"Median (double) \${example.median(doubleVector)}")
}
```