Skip to content

Commit 6d4f2a9

Browse files
author
Alejandro Gómez
committed
👶
0 parents  commit 6d4f2a9

35 files changed

+2052
-0
lines changed

.gitignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
project/project
2+
project/target
3+
target
4+
.idea
5+
.tmp
6+
7+
*.iml
8+
/out
9+
.idea_modules
10+
.classpath
11+
.project
12+
/RUNNING_PID
13+
.settings
14+
.sass-cache
15+
scalajvm/upload/*
16+
17+
# temp files
18+
.~*
19+
*~
20+
*.orig
21+
22+
# eclipse
23+
.scala_dependencies
24+
.buildpath
25+
.cache
26+
.target
27+
bin/
28+
.ensime
29+
.ensime_cache
30+
31+
# OSX
32+
.DS_Store

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: scala
2+
scala:
3+
- 2.11.7
4+
jdk:
5+
- oraclejdk8

build.sbt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
lazy val shapeless = (project in file("."))
2+
.enablePlugins(ExerciseCompilerPlugin)
3+
.settings(
4+
organization := "org.scalaexercises",
5+
name := "content-shapeless",
6+
scalaVersion := "2.11.7",
7+
version := "0.0.0-SNAPSHOT",
8+
resolvers ++= Seq(
9+
Resolver.sonatypeRepo("snapshots")
10+
),
11+
libraryDependencies ++= Seq(
12+
"org.typelevel" %% "cats-core" % "0.4.1",
13+
"com.chuusai" %% "shapeless" % "2.2.5",
14+
"org.scalatest" %% "scalatest" % "2.2.4",
15+
"org.scalaexercises" %% "runtime" % "0.0.0-SNAPSHOT" changing(),
16+
"org.scalaexercises" %% "definitions" % "0.0.0-SNAPSHOT" changing(),
17+
"org.scalacheck" %% "scalacheck" % "1.12.5",
18+
"com.github.alexarchambault" %% "scalacheck-shapeless_1.12" % "0.3.1",
19+
compilerPlugin("org.spire-math" %% "kind-projector" % "0.7.1")
20+
)
21+
)

project/build.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.13.9

project/plugins.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("org.scalaexercises" % "sbt-exercise" % "0.0.0-SNAPSHOT", "0.13", "2.10")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defaultLib.Library_cats$1$
2+
defaultLib.Library_shapeless$1$
3+
defaultLib.Library_stdlib$1$
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package shapelessex
2+
3+
import org.scalatest._
4+
import shapeless._
5+
import ops.hlist._
6+
7+
/** == Facilities for abstracting over arity ==
8+
*
9+
* Conversions between tuples and `HList`'s, and between ordinary Scala functions of arbitrary arity and functions which
10+
* take a single corresponding `HList` argument allow higher order functions to abstract over the arity of the functions
11+
* and values they are passed
12+
* {{{
13+
* import syntax.std.function._
14+
* import ops.function._
15+
*
16+
* def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F)
17+
* (implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) =
18+
* f.toProduct(gen.to(p))
19+
* }}}
20+
*
21+
* @param name arity
22+
*/
23+
object ArityExercises extends FlatSpec with Matchers with exercise.Section {
24+
25+
import syntax.std.function._
26+
import ops.function._
27+
28+
object Helper {
29+
30+
def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F)(implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L R]) =
31+
f.toProduct(gen.to(p))
32+
33+
}
34+
35+
import Helper._
36+
37+
/** Abstracting over arity
38+
*/
39+
def arityTest(res0: Int, res1: Int) = {
40+
applyProduct(1, 2)((_: Int) + (_: Int)) should be(res0)
41+
applyProduct(1, 2, 3)((_: Int) * (_: Int) * (_: Int)) should be(res1)
42+
}
43+
44+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package shapelessex
2+
3+
import org.scalatest._
4+
import shapeless._
5+
6+
trait Monoid[T] {
7+
def zero: T
8+
def append(a: T, b: T): T
9+
}
10+
11+
object Monoid extends ProductTypeClassCompanion[Monoid] {
12+
def mzero[T](implicit mt: Monoid[T]) = mt.zero
13+
14+
implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
15+
def zero = false
16+
def append(a: Boolean, b: Boolean) = a || b
17+
}
18+
19+
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
20+
def zero = 0
21+
def append(a: Int, b: Int) = a + b
22+
}
23+
24+
implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] {
25+
def zero = 0.0
26+
def append(a: Double, b: Double) = a + b
27+
}
28+
29+
implicit def stringMonoid: Monoid[String] = new Monoid[String] {
30+
def zero = ""
31+
def append(a: String, b: String) = a + b
32+
}
33+
34+
object typeClass extends ProductTypeClass[Monoid] {
35+
def emptyProduct = new Monoid[HNil] {
36+
def zero = HNil
37+
def append(a: HNil, b: HNil) = HNil
38+
}
39+
40+
def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] {
41+
def zero = mh.zero :: mt.zero
42+
def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail)
43+
}
44+
45+
def project[F, G](instance: Monoid[G], to: F G, from: G F) = new Monoid[F] {
46+
def zero = from(instance.zero)
47+
def append(a: F, b: F) = from(instance.append(to(a), to(b)))
48+
}
49+
}
50+
}
51+
52+
trait MonoidSyntax[T] {
53+
def |+|(b: T): T
54+
}
55+
56+
object MonoidSyntax {
57+
implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] {
58+
def |+|(b: T) = mt.append(a, b)
59+
}
60+
}
61+
62+
/** == Automatic type class instance derivation ==
63+
*
64+
* Based on and extending `Generic` and `LabelledGeneric`, Lars Hupel ([[https://twitter.com/larsr_h @larsr_h]]) has contributed the `TypeClass`
65+
* family of type classes, which provide automatic type class derivation facilities roughly equivalent to those available
66+
* with GHC as described in [[http://dreixel.net/research/pdf/gdmh.pdf "A Generic Deriving Mechanism for Haskell"]]. There is a description of an
67+
* earlier iteration of the Scala mechanism [[http://typelevel.org/blog/2013/06/24/deriving-instances-1.html here]], and examples of its use deriving `Show` and `Monoid`
68+
* instances [[https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/shows.scala here]]
69+
* and [[https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/monoids.scala here]] for labelled coproducts and unlabelled products respectively.
70+
*
71+
* For example, in the `Monoid` case, once the general deriving infrastructure for monoids is in place, instances are
72+
* automatically available for arbitrary case classes without any additional boilerplate
73+
*
74+
* {{{
75+
* trait Monoid[T] {
76+
* def zero: T
77+
* def append(a: T, b: T): T
78+
* }
79+
*
80+
* object Monoid extends ProductTypeClassCompanion[Monoid] {
81+
* def mzero[T](implicit mt: Monoid[T]) = mt.zero
82+
*
83+
* implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
84+
* def zero = false
85+
* def append(a: Boolean, b: Boolean) = a || b
86+
* }
87+
*
88+
* implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
89+
* def zero = 0
90+
* def append(a: Int, b: Int) = a+b
91+
* }
92+
*
93+
* implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] {
94+
* def zero = 0.0
95+
* def append(a: Double, b: Double) = a+b
96+
* }
97+
*
98+
* implicit def stringMonoid: Monoid[String] = new Monoid[String] {
99+
* def zero = ""
100+
* def append(a: String, b: String) = a+b
101+
* }
102+
*
103+
* object typeClass extends ProductTypeClass[Monoid] {
104+
* def emptyProduct = new Monoid[HNil] {
105+
* def zero = HNil
106+
* def append(a: HNil, b: HNil) = HNil
107+
* }
108+
*
109+
* def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] {
110+
* def zero = mh.zero :: mt.zero
111+
* def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail)
112+
* }
113+
*
114+
* def project[F, G](instance: => Monoid[G], to: F => G, from: G => F) = new Monoid[F] {
115+
* def zero = from(instance.zero)
116+
* def append(a: F, b: F) = from(instance.append(to(a), to(b)))
117+
* }
118+
* }
119+
* }
120+
*
121+
* trait MonoidSyntax[T] {
122+
* def |+|(b: T): T
123+
* }
124+
*
125+
* object MonoidSyntax {
126+
* implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] {
127+
* def |+|(b: T) = mt.append(a, b)
128+
* }
129+
* }
130+
* }}}
131+
* The [[https://github.com/typelevel/shapeless-contrib shapeless-contrib]] project also contains automatically derived type class instances for
132+
* [[https://github.com/typelevel/shapeless-contrib/blob/master/scalaz/main/scala/typeclass.scala Scalaz]],
133+
* [[https://github.com/typelevel/shapeless-contrib/blob/master/spire/main/scala/typeclass.scala Spire]] and
134+
* [[https://github.com/typelevel/shapeless-contrib/blob/master/scalacheck/main/scala/package.scala Scalacheck]].
135+
*
136+
* @param name auto_typeclass_derivation
137+
*
138+
*/
139+
object AutoTypeClassExercises extends FlatSpec with Matchers with exercise.Section {
140+
141+
object Helper {
142+
143+
// A pair of arbitrary case classes
144+
case class Foo(i: Int, s: String)
145+
case class Bar(b: Boolean, s: String, d: Double)
146+
}
147+
148+
import Helper._
149+
150+
/** {{{
151+
*
152+
* // A pair of arbitrary case classes
153+
* case class Foo(i : Int, s : String)
154+
* case class Bar(b : Boolean, s : String, d : Double)
155+
*
156+
* }}}
157+
*/
158+
def monoidDerivation(res0: Int, res1: String, res2: Boolean, res3: String, res4: Double) = {
159+
160+
import MonoidSyntax._
161+
import Monoid.typeClass._
162+
163+
val fooCombined = Foo(13, "foo") |+| Foo(23, "bar")
164+
fooCombined should be(Foo(res0, res1))
165+
166+
val barCombined = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
167+
barCombined should be(Bar(res2, res3, res4))
168+
169+
}
170+
171+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package shapelessex
2+
3+
import org.scalatest._
4+
import shapeless._
5+
6+
/** == Coproducts and discriminated unions ==
7+
*
8+
* shapeless has a Coproduct type, a generalization of Scala's `Either` to an arbitrary number of choices. Currently it
9+
* exists primarily to support `Generic` (see the next section), but will be expanded analogously to `HList` in later
10+
* releases. Currently `Coproduct` supports mapping, selection and unification
11+
*
12+
* @param name coproducts
13+
*/
14+
object CoproductExercises extends FlatSpec with Matchers with exercise.Section {
15+
16+
object Helper {
17+
type ISB = Int :+: String :+: Boolean :+: CNil
18+
19+
object sizeM extends Poly1 {
20+
implicit def caseInt = at[Int](i (i, i))
21+
implicit def caseString = at[String](s (s, s.length))
22+
implicit def caseBoolean = at[Boolean](b (b, 1))
23+
}
24+
25+
val isb = Coproduct[ISB]("foo")
26+
}
27+
28+
import Helper._
29+
30+
/** {{{
31+
* type ISB = Int :+: String :+: Boolean :+: CNil
32+
*
33+
* val isb = Coproduct[ISB]("foo")
34+
* }}}
35+
*/
36+
def selection(res0: Option[Int], res1: Option[String]) = {
37+
isb.select[Int] should be(res0)
38+
39+
isb.select[String] should be(res1)
40+
}
41+
42+
/** Coproduct also supports mapping given a polymorphic function such as
43+
* {{{
44+
* object sizeM extends Poly1 {
45+
* implicit def caseInt = at[Int](i => (i, i))
46+
* implicit def caseString = at[String](s => (s, s.length))
47+
* implicit def caseBoolean = at[Boolean](b => (b, 1))
48+
* }
49+
* }}}
50+
*/
51+
def mapping(res0: Option[(String, Int)]) = {
52+
val m = isb map sizeM
53+
54+
m.select[(String, Int)] should be(res0)
55+
}
56+
57+
/** In the same way that adding labels To the elements of an HList gives us a record,
58+
* adding labels to the elements of a Coproduct gives us a discriminated union.
59+
*/
60+
def unionE(res0: Option[Int], res1: Option[String], res2: Option[Boolean]) = {
61+
import record._, union._, syntax.singleton._
62+
63+
type U = Union.`'i -> String, 's -> String, 'b -> Boolean`.T
64+
65+
val u = Coproduct[U]('s ->> "foo") // Inject a String into the union at label 's
66+
67+
u.get('i) should be(res0)
68+
u.get('s) should be(res1)
69+
u.get('b) should be(res2)
70+
}
71+
72+
}

0 commit comments

Comments
 (0)