Skip to content

Commit

Permalink
Merge changes from @d6y
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Gurnell committed Nov 1, 2016
2 parents b08e044 + 2ca88ca commit 95e632c
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 162 deletions.
Binary file modified dist/shapeless-guide.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.6")
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.5-SNAPSHOT")
4 changes: 3 additions & 1 deletion src/meta/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pages:
- poly/index.md
- poly/poly.md
- poly/map-flatmap-fold.md
- poly/type-class.md
- poly/product-mapper.md
- poly/summary.md

- nat/index.md
Expand All @@ -72,6 +72,8 @@ pages:
- nat/ops.md
- nat/summary.md

- conclusion/index.md

- notes/todos.md
- links.md
...
32 changes: 32 additions & 0 deletions src/pages/conclusion/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Conclusion #{-}

With Part II's look at `shapeless.ops`,
we have arrived at the end of this guide to shapeless.
We hope you found it useful for understanding
this fascinating and powerful library.

As functional programmers
we value abstraction above all else.
Concepts like functors and monads
arise from years of programming research:
writing code, spotting patterns,
and making abstractions to remove redundancy.
Shapeless raises the bar for abstraction in Scala.
Tools like `Generic` and `LabelledGeneric`
provide an interface for abstracting over data types
that we previously thought unique and distinct.

There have traditionally been two barriers to entry
for aspiring new shapeless users.
The first is the wealth of theoretical knowledge
and implementation detail
required to understand the bigger picture.
Hopefully this guide has done its job in this regard.
The second barrier is the fear and uncertainty
surrounding a library that is seen
as "academic" and "advanced".
We can overcome this by sharing knowledge
and showing each other the use cases,
advantages, and disadvantages of its use.
So please share this book with a friend...
and let's scrap some boilerplate together!
5 changes: 2 additions & 3 deletions src/pages/intro/acknowledgements.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
## Acknowledgements

Thanks to Miles Sabin, Travis Brown,
my colleagues at [Underscore][link-underscore],
and all our [fellow space-farers on Github][link-contributors]
Thanks to Miles Sabin, Richard Dallaway, Noel Welsh, Travis Brown,
and our [fellow space-farers on Github][link-contributors]
for their invaluable help and feedback.

Special thanks to Sam Halliday for this excellent workshop
Expand Down
5 changes: 3 additions & 2 deletions src/pages/intro/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ We assume shapeless 2.3.2 and either
Typelevel Scala 2.11.8+ or Lightbend Scala 2.11.9+ / 2.12.1+.

Shapeless is large library,
so rather than cover everything it has to offer,
so rather than cover everything it has to offer
we will concentrate on a few compelling use cases
and use them to build a picture of the tools and patterns available.
and use them to build a picture
of the tools and patterns available.

Before we start, let's talk about what generic programming is
and why shapeless is so exciting to Scala developers.
19 changes: 10 additions & 9 deletions src/pages/intro/thisbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,17 @@ we provide a theoretical primer in three chapters:
our own version of Scalacheck's `Arbitrary`.

<div class="callout callout-info">
*Source code for examples*
*Source code and examples*

We've placed source code for
many of the examples in this guide
in the accompanying Github repo:
This book is open source.
See the inside cover for licensing information.
You can find the Markdown source on Github:

`https://github.com/underscoreio/shapeless-guide-code`
`https://github.com/underscoreio/shapeless-guide`

There are also complete implementations of
each of the major examples in this repo.
See the README for details:

The `exercises` branch contains
skeleton Scala files with `TODOs` to fill in,
and the `solutions` branch contains
completed implementations.
`https://github.com/underscoreio/shapeless-guide-code`
</div>
15 changes: 9 additions & 6 deletions src/pages/nat/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
From time to time we need to count things at the type level.
For example, we may need to know the length of an `HList`
or the number of terms we have expanded so far in a computation.
We can represent numbers as values easily enough,
but if we want to influence implicit resolution
we need to represent them as the type level.
This chapter covers the theory behind counting with types,
and provides some use cases related to type class derivation.
and provides some compelling use cases for type class derivation.

## Representing numbers as types

Shapeless uses "Church encoding"
to represent natural numbers at the type level.
It provides a type `Nat` with two subtypes:
`_0` representing zero,
and `Succ[N]` representing the successor of `N`:
and `Succ[N]` representing `N+1`:

```tut:book:silent
import shapeless.{Nat, Succ}
Expand All @@ -23,8 +26,7 @@ type Two = Succ[One]
// etc...
```

shapeless provides aliases for the first 22 `Nats`
as `Nat._N`:
Shapeless provides aliases for the first 22 `Nats` as `Nat._N`:

```tut:book:silent
Nat._1
Expand All @@ -48,8 +50,9 @@ toInt.apply()
```

The `Nat.toInt` method provides
a convenient shorthand for calling `nat.apply()`:
a convenient shorthand for calling `nat.apply()`.
It accepts the instance of `ToInt` as an implicit parameter:

```tut:book
Nat.toInt[Succ[Succ[Succ[Nat._0]]]]
Nat.toInt[Nat._3]
```
13 changes: 6 additions & 7 deletions src/pages/nat/length.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Sizing generic representations
## Length of generic representations

One use case for `Nat` is
determining the size of `HLists` and `Coproducts`.
determining the length of `HLists` and `Coproducts`.
Shapeless provides the
`shapeless.ops.hlist.Length` and
`shapeless.ops.coproduct.Length` type classes for this.
Expand All @@ -23,8 +23,7 @@ val coproductLength = coproduct.Length[Double :+: Char :+: CNil]

Instances of `Length` have a type member `Out`
that represents the length as a `Nat`.
We can either summon a `ToInt` ourselves
to turn the `Nat` into an `Int`:
We can either summon an instance of `ToInt` ourselves:

```tut:book
implicitly[ToInt[hlistLength.Out]].apply()
Expand All @@ -45,6 +44,9 @@ and exposes it as a simple `Int`:
trait SizeOf[A] {
def value: Int
}
def sizeOf[A](implicit size: SizeOf[A]): Int =
size.value
```

To create an instance of `SizeOf` we need three things:
Expand All @@ -71,9 +73,6 @@ implicit def genericSizeOf[A, L <: HList, N <: Nat](
We can test our code as follows:

```tut:book:silent
def sizeOf[A](implicit size: SizeOf[A]): Int =
size.value
case class IceCream(name: String, numCherries: Int, inCone: Boolean)
```

Expand Down
26 changes: 13 additions & 13 deletions src/pages/nat/random.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## Case study: random value generator

Property-based testing libraries like [ScalaCheck][link-scalacheck]
use type classes to generate random data for use in unit tests.
For example, ScalaCheck has the `Arbitrary` type class
use type classes to generate random data for unit tests.
For example, ScalaCheck provides the `Arbitrary` type class
that we can use as follows:

```tut:book:silent
Expand All @@ -23,7 +23,7 @@ This makes shapeless integration via libraries like

In this section we will create a simple `Random` type class
to generate random values of user-defined ADTs.
We will show how `Length` and `Nat` form
We will show how `Length` and `Nat` form
a crucial part of the implementation:

```tut:book:invisible
Expand Down Expand Up @@ -64,7 +64,7 @@ implicit val booleanRandom: Random[Boolean] =
createRandom(() => scala.util.Random.nextBoolean)
```

We can use these simple generators
We can use these simple generators
via the `random` method as follows:

```tut:book
Expand All @@ -75,7 +75,7 @@ for(i <- 1 to 3) println(random[Char])
### Random products

We can create random values for products
using the `Generic` and `HList` techniques
using the `Generic` and `HList` techniques
from Chapter [@sec:generic]:

```tut:book:silent
Expand All @@ -95,7 +95,7 @@ implicit def hlistRandom[H, T <: HList](
implicit
hRandom: Random[H],
tRandom: Lazy[Random[T]]
): Random[H :: T] =
): Random[H :: T] =
createRandom(() => hRandom.get :: tRandom.value.get)
```

Expand Down Expand Up @@ -131,7 +131,7 @@ implicit def coproductRandom[H, T <: Coproduct](
}
```

There problems with this implementation
There problems with this implementation
lie in the 50/50 choice in calculating `chooseH`.
This creates an uneven probability distribution.
For example, consider the following type:
Expand All @@ -144,10 +144,10 @@ case object Green extends Light
```

The `Repr` for `Light` is `Red :+: Amber :+: Green :+: CNil`.
An instance of `Random` for this type
will choose `Red` 50% of the time
An instance of `Random` for this type
will choose `Red` 50% of the time
and `Amber :+: Green :+: CNil` 50% of the time.
A correct distribution would be
A correct distribution would be
33% `Red` and 67% `Amber :+: Green :+: CNil`.

And that's not all.
Expand All @@ -167,10 +167,10 @@ for(i <- 1 to 100) random[Light]
// ...
```

To fix this problem we have to alter
To fix this problem we have to alter
the probability of choosing `H` over `T`.
The correct behaviour should be to choose
`H` `1/n`^th^ of the time,
`H` `1/n`^th^ of the time,
where `n` is the length of the coproduct.
This ensures an even probability distribution
across the subtypes of the coproduct
Expand All @@ -197,7 +197,7 @@ implicit def coproductRandom[H, T <: Coproduct, L <: Nat](
```

With these modifications
With these modifications
we can generate random values of any product or coproduct:

```tut:book
Expand Down
22 changes: 14 additions & 8 deletions src/pages/nat/summary.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
## Summary

In this chapter we discussed
In this chapter we discussed
how shapeless represents natural numbers
and how to calculate the length
of an `HList` or `Coproduct`.
and how we can use them in type classes.
We saw some predefined ops type classes
that let us do things like calculate lengths
and access elements by index,
and created our own type classes
that use `Nat` in other ways.

Such calculations involve two parts:
one at the type level involving
the `Length` type classes and the `Nat` type,
and one at the value level involving
the `ToInt` type class and regular `Ints`.
Between `Nat`, `Poly`, and the variety of
type classes and examples we have seen in Part II,
we have seen just a small fraction of
the toolbox provided in `shapeless.ops`.
There are many other ops type classes
that provide a comprehensive foundation
on which to build our own code.
12 changes: 6 additions & 6 deletions src/pages/notes/todos.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TODOs
# TODOs #{-}

Still to do:

Expand All @@ -15,15 +15,15 @@ Still to do:
- **DONE** Counting with `Nat`
- **DONE** Generating `Arbitrary` instances as an example
- Function interop
- Callout box on quirkiness of type inference with poly:
- `val len1: Int = lengthPoly("foo")` fails, but...
- `val len2 = lengthPoly("foo")` compiles, but...
- `val len3: Int = lengthPoly[String]("foo")` fails
- **DONE** Callout box on quirkiness of type inference with poly:
- **DONE** `val len1: Int = lengthPoly("foo")` fails, but...
- **DONE** `val len2 = lengthPoly("foo")` compiles, but...
- **DONE** `val len3: Int = lengthPoly[String]("foo")` fails
- Built-in record operations
- Performance
- `cachedImplicit`
- Maybe `Cached`
- Maybe
- Check cross references
- Final summary
- **DONE** Final summary
- **SHIP IT!**
Loading

0 comments on commit 95e632c

Please sign in to comment.