|
| 1 | +package com.baeldung.scala.shapeless |
| 2 | + |
| 3 | +import org.scalatest.matchers.should.Matchers |
| 4 | +import org.scalatest.wordspec.AnyWordSpec |
| 5 | +import shapeless.ops.record.Keys |
| 6 | +import shapeless.{Generic, HNil, Inl, Inr, _} |
| 7 | + |
| 8 | +class ShapelessSpecs extends AnyWordSpec with Matchers { |
| 9 | + val hlist: Int :: Double :: String :: Boolean :: HNil = |
| 10 | + 1 :: 1.0 :: "One" :: false :: HNil |
| 11 | + |
| 12 | + case class User(name: String, age: Int) |
| 13 | + |
| 14 | + "HList" should { |
| 15 | + "return the first element of hlist" in { |
| 16 | + assert(hlist.head == 1) |
| 17 | + } |
| 18 | + |
| 19 | + "return the two frist elements of hlist" in { |
| 20 | + assert(hlist.take(2) == 1 :: 1.0 :: HNil) |
| 21 | + } |
| 22 | + |
| 23 | + "return the tail of hlist" in { |
| 24 | + assert(hlist.tail == 1.0 :: "One" :: false :: HNil) |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + "Coproduct" should { |
| 29 | + sealed trait TrafficLight |
| 30 | + case class Green() extends TrafficLight |
| 31 | + case class Red() extends TrafficLight |
| 32 | + case class Yellow() extends TrafficLight |
| 33 | + |
| 34 | + "convert sealed trait coproduct to shapeless coproduct" in { |
| 35 | + val gen = Generic[TrafficLight] |
| 36 | + val green = gen.to(Green()) |
| 37 | + val red = gen.to(Red()) |
| 38 | + val yellow = gen.to(Yellow()) |
| 39 | + assert(green == Inl(Green())) |
| 40 | + assert(red == Inr(Inl(Red()))) |
| 41 | + assert(yellow == Inr(Inr(Inl(Yellow())))) |
| 42 | + } |
| 43 | + |
| 44 | + "convert back shapeless coproduct to sealed trait product type" in { |
| 45 | + val gen = Generic[TrafficLight] |
| 46 | + val green = gen.from(Inl(Green())) |
| 47 | + val red = gen.from(Inr(Inl(Red()))) |
| 48 | + val yellow = gen.from(Inr(Inr(Inl(Yellow())))) |
| 49 | + assert(green == Green()) |
| 50 | + assert(red == Red()) |
| 51 | + assert(yellow == Yellow()) |
| 52 | + } |
| 53 | + |
| 54 | + "create coproduct" in { |
| 55 | + import shapeless._ |
| 56 | + object Green |
| 57 | + object Red |
| 58 | + object Yellow |
| 59 | + type Light = Green.type :+: Red.type :+: Yellow.type :+: CNil |
| 60 | + val light = Coproduct[Light](Green) |
| 61 | + light.filter |
| 62 | + assert(light.select[Green.type] == Some(Green)) |
| 63 | + assert(light.select[Yellow.type] == None) |
| 64 | + } |
| 65 | + |
| 66 | + } |
| 67 | + |
| 68 | + "Generic" should { |
| 69 | + "Convert case class to corresponding HList product type" in { |
| 70 | + val user = User("John", 25) |
| 71 | + val userHList = Generic[User].to(user) |
| 72 | + assert(userHList == "John" :: 25 :: HNil) |
| 73 | + } |
| 74 | + |
| 75 | + "Convert back hlist to corresponding case class product type" in { |
| 76 | + val user = User("John", 25) |
| 77 | + val userHList = Generic[User].to(user) |
| 78 | + val userRecord = Generic[User].from(userHList) |
| 79 | + assert(user == userRecord) |
| 80 | + } |
| 81 | + |
| 82 | + "Convert tuple to HList product type" in { |
| 83 | + val user = ("John", 25) |
| 84 | + val userHList = Generic[(String, Int)].to(user) |
| 85 | + assert(userHList == "John" :: 25 :: HNil) |
| 86 | + } |
| 87 | + |
| 88 | + "Operations on tuple" should { |
| 89 | + import shapeless.syntax.std.tuple._ |
| 90 | + val tuple = ("John", 25, true) |
| 91 | + |
| 92 | + "take head from tuple" in { |
| 93 | + val head = tuple.head |
| 94 | + assert(head == "John") |
| 95 | + } |
| 96 | + |
| 97 | + "drop two element from tuple" in { |
| 98 | + val res = tuple.drop(2) |
| 99 | + assert(res == Tuple1(true)) |
| 100 | + } |
| 101 | + |
| 102 | + "concat two tuple to one" in { |
| 103 | + val list = tuple ++ (1.3, "foo") |
| 104 | + assert(list == ("John", 25, true, 1.3, "foo")) |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + "Convert back hlist to corresponding tuple product type" in { |
| 109 | + val user = ("John", 25) |
| 110 | + val userHList = Generic[(String, Int)].to(user) |
| 111 | + val userRecord = Generic[(String, Int)].from(userHList) |
| 112 | + assert(user == userRecord) |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + "LabelledGeneric" should { |
| 117 | + import shapeless._ |
| 118 | + import record._ |
| 119 | + val user = User("John", 25) |
| 120 | + val userGen = LabelledGeneric[User] |
| 121 | + val userLaballedRecord = userGen.to(user) |
| 122 | + |
| 123 | + "read name field from labelled Hlist" in { |
| 124 | + assert(userLaballedRecord('name) == "John") |
| 125 | + } |
| 126 | + |
| 127 | + "return list of keys from labelled Hlist" in { |
| 128 | + val keys = Keys[userGen.Repr] |
| 129 | + assert(keys() == 'name :: 'age :: HNil) |
| 130 | + } |
| 131 | + |
| 132 | + "return list of values from labelled Hlist" in { |
| 133 | + assert(userLaballedRecord.values == "John" :: 25 :: HNil) |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + "Polymorphic function" should { |
| 138 | + "calculate length of variant input types" in { |
| 139 | + import shapeless._ |
| 140 | + object polyLength extends Poly1 { |
| 141 | + implicit val listCase = at[List[Int]](i => i.length) |
| 142 | + implicit val stringCase = at[String](d => d.length) |
| 143 | + implicit val arrayCase = at[Array[Int]](d => d.length) |
| 144 | + } |
| 145 | + |
| 146 | + val list = List(1, 2) :: "123" :: Array(1, 2, 3, 4) :: HNil |
| 147 | + |
| 148 | + assert(polyLength(List(1, 2)) == 2) |
| 149 | + assert(polyLength("123") == 3) |
| 150 | + assert(polyLength(Array(1, 2, 3, 4)) == 4) |
| 151 | + assert(list.map(polyLength) == 2 :: 3 :: 4 :: HNil) |
| 152 | + } |
| 153 | + } |
| 154 | +} |
0 commit comments