1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
import shapeless.Generic
class Playground {
class ScalaStdlibTypes { // Motivation
class ScalaConcreteTypesWithTypes {
type Rect = (Int, Int) // this is a Product (AND relation)
type Circle = Int
type Shape = Either[Rect, Circle] // either implies a Coproduct (OR relation)
def area(s: Shape) = s match {
case Left((a, b)) => a*b
case Right(a) => math.Pi*a*a
}
val rect: Shape = Left(4, 2)
val circle: Shape = Right(1)
println(area(rect), area(circle))
}
val concrete_types = new ScalaConcreteTypesWithTypes
class ScalaGenericTypes {
trait Shape
case class Rect(a: Int, b: Int) extends Shape
case class Circle(a: Int) extends Shape
def area(s: Shape) = s match {
case Rect(a, b) => a*b
case Circle(a) => math.Pi*a*a
}
val rect = new Rect(3, 4)
val circle = new Circle(3)
println(area(rect))
}
val generic_types = new ScalaGenericTypes
}
class ShapelessGenericTypes {
class FromTuples {
import shapeless.{HList, ::, HNil}
type testType = Int :: Int :: HNil
val k: testType = (1 :: 1 :: HNil) // manual construction of a "repr" (generic representation of a type) from a tuple
val newType = Generic[(Int, Int)]
println(newType.to((1, 1)))
}
class FromCaseClasses {
trait Shape
case class Rect(a: Int, b: Int) extends Shape
val rectGen = Generic[Rect]
println(rectGen)
}
val case_classes = new FromCaseClasses
}
// val stdlib_types = new ScalaStdlibTypes
val shapless_generic_types = new ShapelessGenericTypes
class GenericCSVEncoder {
trait CSVEncoder[A] { // generic typeclass that defines the encode operation
def encode(s: A): List[String]
}
def writeCSV[A](vals: List[A])(implicit enc: CSVEncoder[A]): String =
vals.map(v => enc.encode(v).mkString(",")).mkString("\n")
/** Simple example with a case class
*/
case class Employee(a: String, b: Int)
implicit val employeeEncoder: CSVEncoder[Employee] = {
new CSVEncoder[Employee] {
def encode(e: Employee): List[String] = List(e.a, e.b.toString)
}
}
val employees = List(Employee("a", 1), Employee("b", 2))
println(writeCSV(employees))
/** compiler searches for (i.e. resolves )an implicit encoder function matching
* the given type
*
* needs an implicit encoder function in the context for both the types
*/
implicit def pairEncoder[A, B] (
implicit
aEncoder: CSVEncoder[A],
bEncoder: CSVEncoder[B]
): CSVEncoder[(A, B)] =
new CSVEncoder[(A, B)] {
def encode(pair: (A, B)): List[String] = {
val (a, b) = pair
aEncoder.encode(a) ++ bEncoder.encode(b)
}
}
implicit val icecreamEncoder: CSVEncoder[IceCream] = {
new CSVEncoder[IceCream] {
def encode(e: IceCream): List[String] = List(e.a, e.b.toString)
}
}
case class IceCream(a: String, b: Int)
val icecream = List(IceCream("a", 1), IceCream("b", 2))
println(writeCSV(employees zip employees))
// Standard patterns with companion objects
trait CsvEncoder2[T] {
def encode(k: T): List[String]
}
/** Demonsrates a standard pattern for generic typeclass companion objects
*
* apply performs implicit compiler resolution, while
* instance wraps a HOF into the encode function defined by the trait
*/
object CsvEncoder2 {
// "Summoner" method
def apply[A](implicit enc: CSVEncoder[A]): CSVEncoder[A] = enc
// "Constructor" method
def instance[A](func: A => List[String]): CSVEncoder[A] =
new CSVEncoder[A] {
def encode(value: A): List[String] =
func(value)
}
}
// with the new companion object, encode for ice cream can be:
// implicit val icecreamEncoder2: CsvEncoder2[IceCream] =
// instance(x => List(x.a, x.b.toString))
}
val generic_csv_encoder = new GenericCSVEncoder
}
object main {
def main(args: Array[String]): Unit = {
new Playground()
}
}
|