├── .gitignore
├── .travis.yml
├── Gruntfile.coffee
├── LICENSE
├── README.md
├── book_deploy.sh
├── book_deploy_rsa.enc
├── build.sbt
├── docker-compose.yml
├── go.sh
├── package.json
├── project
├── build.properties
└── plugins.sbt
├── sbt.sh
├── src
├── covers
│ ├── epub-cover.ai
│ ├── epub-cover.png
│ ├── gumroad-cover.ai
│ └── gumroad-cover.png
├── css
│ └── book.less
├── meta
│ ├── epub.yaml
│ ├── html.yaml
│ ├── metadata.yaml
│ └── pdf.yaml
├── pages
│ ├── classes
│ │ ├── apply.md
│ │ ├── case-classes.md
│ │ ├── classes.md
│ │ ├── companion-objects.md
│ │ ├── conclusions.md
│ │ ├── index.md
│ │ ├── pattern-matching.md
│ │ ├── scala-type-hierarchy.pdf
│ │ ├── scala-type-hierarchy.sketch
│ │ └── scala-type-hierarchy.svg
│ ├── collections-redux
│ │ ├── arrays-and-strings.md
│ │ ├── index.md
│ │ ├── iterators.md
│ │ ├── java-interop.md
│ │ ├── mutable-seq.md
│ │ ├── seq-implementations.md
│ │ └── traversable.md
│ ├── collections
│ │ ├── for-comprehensions-redux.md
│ │ ├── for-comprehensions.md
│ │ ├── index.md
│ │ ├── map-and-set.md
│ │ ├── meeting-monads.md
│ │ ├── options.md
│ │ ├── random-data.md
│ │ ├── random-data.scala
│ │ ├── ranges.md
│ │ ├── seq.md
│ │ └── working-with-seq.md
│ ├── conclusions.md
│ ├── dsl
│ │ ├── control.md
│ │ ├── index.md
│ │ ├── macros.md
│ │ └── operators.md
│ ├── fp
│ │ ├── ho-functions.md
│ │ ├── index.md
│ │ ├── modelling-errors.md
│ │ ├── modelling-with-types.md
│ │ ├── monads.md
│ │ └── structural-recursion.md
│ ├── getting-started
│ │ ├── index.md
│ │ ├── scala-ide-completed-worksheet.png
│ │ ├── scala-ide-empty-project.png
│ │ ├── scala-ide-empty-worksheet.png
│ │ ├── scala-ide-empty-workspace.png
│ │ ├── scala-ide-hello-world.png
│ │ ├── scala-ide-new-object.png
│ │ ├── scala-ide-new-project.png
│ │ ├── scala-ide-new-worksheet.png
│ │ ├── scala-ide-single-file.png
│ │ ├── scala-ide-website.png
│ │ └── scala-ide-workspace-chooser.png
│ ├── implicits
│ │ ├── conversions.md
│ │ ├── creating.md
│ │ ├── enrichment.md
│ │ ├── equal.scala
│ │ ├── htmlwriter.scala
│ │ ├── implicit-parameters.md
│ │ ├── implicit-resolution.md
│ │ ├── index.md
│ │ ├── instances.md
│ │ ├── json.md
│ │ ├── order.scala
│ │ ├── ordering.scala
│ │ ├── organising.md
│ │ ├── rational-packaging.scala
│ │ ├── rational.scala
│ │ └── using-type-classes.md
│ ├── index.md
│ ├── intro
│ │ ├── conclusion.md
│ │ ├── expressions.md
│ │ ├── guiding-principles.md
│ │ ├── history.md
│ │ ├── index.md
│ │ ├── literals.md
│ │ ├── object-literals.md
│ │ ├── objects.md
│ │ └── writing-methods.md
│ ├── links.md
│ ├── object-functional
│ │ ├── monad-error-handling.md
│ │ └── pattern-matching.md
│ ├── pattern-matching
│ │ ├── extractors.md
│ │ ├── extractors.scala
│ │ └── index.md
│ ├── sbt
│ │ └── index.md
│ ├── scala-basics
│ │ ├── definitions.md
│ │ ├── expressions.md
│ │ ├── index.md
│ │ ├── more-expressions.md
│ │ ├── scala-basics.key
│ │ ├── statements.md
│ │ └── types.md
│ ├── sequencing
│ │ ├── calculator.scala
│ │ ├── conclusions.md
│ │ ├── function-types.md
│ │ ├── functions.md
│ │ ├── generics.md
│ │ ├── index.md
│ │ ├── intlist.scala
│ │ ├── linkedlist.scala
│ │ ├── maybe.scala
│ │ ├── modelling-data.md
│ │ ├── sequencing-computation.md
│ │ ├── sum.scala
│ │ ├── tree.md
│ │ ├── variance.md
│ │ └── working-with-data.md
│ ├── solutions.md
│ ├── start-of-appendix.md
│ ├── thanks.md
│ └── traits
│ │ ├── calculation.scala
│ │ ├── calculator.scala
│ │ ├── conclusions.md
│ │ ├── extended-examples.md
│ │ ├── feline.scala
│ │ ├── generics.md
│ │ ├── index.md
│ │ ├── intlist.scala
│ │ ├── json.scala
│ │ ├── linked-list.sketch
│ │ ├── Data
│ │ ├── QuickLook
│ │ │ ├── Preview.png
│ │ │ └── Thumbnail.png
│ │ ├── metadata
│ │ └── version
│ │ ├── linked-list.svg
│ │ ├── modelling-data-with-traits.md
│ │ ├── recursive-data.md
│ │ ├── sealed-traits.md
│ │ ├── structural-recursion.md
│ │ ├── traits-as-mixins.md
│ │ ├── traits.md
│ │ ├── tree.scala
│ │ └── working-with-data.md
└── template
│ ├── cover-notes.html
│ └── cover-notes.tex
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | _site/*
2 | _theme_packages/*
3 |
4 | Thumbs.db
5 | .DS_Store
6 |
7 | !.gitkeep
8 |
9 | .rbenv-version
10 | .rvmrc
11 | /bower_components/
12 | /dist/
13 | /essential-scala/
14 | /essential-scala.zip
15 | /node_modules/
16 |
17 | essential-scala.sublime-workspace
18 | core-scala.sublime-workspace
19 | essential-scala
20 |
21 | target/
22 | project/target
23 |
24 | book_deploy_rsa
25 |
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | services:
3 | - docker
4 | language: scala
5 | scala:
6 | - 2.12.2
7 | cache:
8 | directories:
9 | - "$HOME/.ivy2/cache"
10 | - "$HOME/.sbt/boot"
11 | - "$HOME/.cache/.coursier"
12 | script:
13 | - sudo mkdir .sbt
14 | - sudo mkdir dist
15 | - docker-compose run book npm install
16 | - docker-compose run book ./sbt.sh pdf html epub ; export SBT_RESULT=$?
17 | - test "$SBT_RESULT" == "0"
18 | after_success:
19 | - openssl aes-256-cbc -K $encrypted_6be077f829fc_key -iv $encrypted_6be077f829fc_iv
20 | -in book_deploy_rsa.enc -out book_deploy_rsa -d
21 | - "./book_deploy.sh"
22 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | #global module:false
2 |
3 | "use strict"
4 |
5 | ebook = require 'underscore-ebook-template'
6 |
7 | module.exports = (grunt) ->
8 | ebook(grunt, {
9 | dir: {
10 | page: "target/pages"
11 | }
12 | })
13 | return
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Essential Scala
2 |
3 | Written by [Dave Gurnell](http://twitter.com/davegurnell) and
4 | [Noel Welsh](http://twitter.com/noelwelsh).
5 | Copyright [Underscore Consulting LLP](http://underscore.io), 2015-2017.
6 |
7 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
8 |
9 |
10 | ## Overview
11 |
12 | [Essential Scala][essential-scala] is an introduction to Scala for professional developers.
13 | Essential Scala covers the core Scala patterns that new developers need to be productive in the language.
14 | It's designed to get you productive as quickly as possible, and avoid the dark and confusing corners of the language.
15 |
16 |
17 | ## Building
18 |
19 | Essential Scala uses [Underscore's ebook build system][ebook-template].
20 |
21 | The simplest way to build the book is to use [Docker Compose](http://docker.com):
22 |
23 | - install Docker Compose (`brew install docker-compose` on OS X; or download from [docker.com](http://docker.com/)); and
24 | - run `go.sh` (or `docker-compose run book bash` if `go.sh` doesn't work).
25 |
26 | This will open a `bash` shell running inside the Docker container which contains all the dependencies to build the book. From the shell run:
27 |
28 | - `npm install`; and then
29 | - `sbt`.
30 |
31 | Within `sbt` you can issue the commands `pdf`, `html`, `epub`, or `all` to build the desired version(s) of the book. Targets are placed in the `dist` directory:
32 |
33 | [ebook-template]: https://github.com/underscoreio/underscore-ebook-template
34 | [essential-scala]: http://underscore.io/books/essential-scala/
35 |
--------------------------------------------------------------------------------
/book_deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Deploy the PDF, HTML, EPUB files of an Underscore book (source)
4 | # into another Git repository (target)
5 | #
6 | set -e
7 |
8 | # Configuration
9 | # 1. The key for writing into the other repository.
10 | # This is committed to the repo, encrypted by Travis,
11 | # with filename extension '.enc'.
12 | export KEY_FILENAME="book_deploy_rsa"
13 | # 2. The branch we deploy from.
14 | # Only deploy for non-PR commits to this branch.
15 | export DEPLOY_BRANCH="develop"
16 | # 3. Folder inside target of where to place the artifacts:
17 | export TARGET_DIR=books/essential-scala/
18 | # 4. Commit message prefix (for the "books" repository)
19 | export COMMIT_PREFIX="deploy essential scala via travis"
20 | # End of configuration
21 |
22 | if [[ "${TRAVIS_PULL_REQUEST}" == "false" && "${TRAVIS_BRANCH}" == "${DEPLOY_BRANCH}" ]]; then
23 | echo "Starting deploy to Github Pages"
24 | mkdir -p "~/.ssh"
25 | echo -e "Host github.com\n\tStrictHostKeyChecking no\nIdentityFile ~/.ssh/${KEY_FILENAME}\n" >> ~/.ssh/config
26 | cp ${KEY_FILENAME} ~/.ssh/${KEY_FILENAME}
27 | chmod 600 ~/.ssh/${KEY_FILENAME}
28 |
29 | git config --global user.email "hello@underscore.io"
30 | git config --global user.name "Travis Build"
31 |
32 | export SRC_DIR=`pwd` # e.g., /home/travis/build/underscoreio/insert-book-name-here
33 |
34 | export TEMP_DIR=/tmp/dist
35 | mkdir -p $TEMP_DIR
36 | cd $TEMP_DIR
37 | git clone git@github.com:underscoreio/books.git
38 |
39 | mkdir -p $TARGET_DIR
40 | cd $TARGET_DIR
41 |
42 | cp $SRC_DIR/dist/*.pdf .
43 | cp $SRC_DIR/dist/*.html .
44 | cp $SRC_DIR/dist/*.epub .
45 |
46 | git add .
47 | git commit -m "$COMMIT_PREFIX $TRAVIS_JOB_NUMBER $TRAVIS_COMMIT [ci skip]"
48 | git push git@github.com:underscoreio/books.git master:master
49 |
50 | rm -rf $TEMP_DIR
51 | fi
52 |
--------------------------------------------------------------------------------
/book_deploy_rsa.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/book_deploy_rsa.enc
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | lazy val root = project.in(file("."))
2 | .settings(tutSettings)
3 |
4 | tutSourceDirectory := sourceDirectory.value / "pages"
5 |
6 | tutTargetDirectory := target.value / "pages"
7 |
8 | // scalaOrganization in ThisBuild := "org.typelevel"
9 | scalaVersion in ThisBuild := "2.11.8"
10 |
11 | scalacOptions ++= Seq(
12 | "-deprecation",
13 | "-encoding", "UTF-8",
14 | "-unchecked",
15 | "-feature",
16 | "-Xlint",
17 | "-Xfatal-warnings",
18 | "-Ywarn-dead-code"
19 | //"-Yliteral-types"
20 | )
21 |
22 | resolvers ++= Seq(Resolver.sonatypeRepo("snapshots"))
23 |
24 | libraryDependencies ++= Seq()
25 |
26 | lazy val pdf = taskKey[Unit]("Build the PDF version of the book")
27 | lazy val html = taskKey[Unit]("Build the HTML version of the book")
28 | lazy val epub = taskKey[Unit]("Build the ePub version of the book")
29 | lazy val json = taskKey[Unit]("Build the JSON version of the book")
30 | lazy val all = taskKey[Unit]("Build all versions of the book")
31 |
32 | pdf := { tutQuick.value ; "grunt pdf".! }
33 | html := { tutQuick.value ; "grunt html".! }
34 | epub := { tutQuick.value ; "grunt epub".! }
35 | json := { tutQuick.value ; "grunt json".! }
36 |
37 | all := {
38 | tutQuick.value
39 | "grunt pdf".!
40 | "grunt html".!
41 | "grunt epub".!
42 | }
43 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | book:
4 | image: underscoreio/book:latest
5 | volumes:
6 | - .:/source
7 | - ~/.ivy2:/root/.ivy2
8 | - ~/.cache/.coursier:/root/.cache/.coursier
9 |
--------------------------------------------------------------------------------
/go.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | docker-compose run book bash
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "essesntial-scala",
3 | "version": "1.0.0",
4 | "devDependencies": {
5 | "bootstrap": "3.3.1",
6 | "coffeeify": "1.0.0",
7 | "jquery": "2.1.1",
8 | "uglifyify": "2.6.0",
9 | "underscore": "1.7.0",
10 | "underscore-ebook-template": "git+https://github.com/underscoreio/underscore-ebook-template.git#0.5.0"
11 | },
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.15
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.7")
2 | addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC3")
3 |
--------------------------------------------------------------------------------
/sbt.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export JAVA_OPTS="-Xmx3g -XX:+TieredCompilation -XX:ReservedCodeCacheSize=256m -XX:+UseNUMA -XX:+UseParallelGC -XX:+CMSClassUnloadingEnabled"
4 |
5 | sbt "$@"
6 |
--------------------------------------------------------------------------------
/src/covers/epub-cover.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/covers/epub-cover.ai
--------------------------------------------------------------------------------
/src/covers/epub-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/covers/epub-cover.png
--------------------------------------------------------------------------------
/src/covers/gumroad-cover.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/covers/gumroad-cover.ai
--------------------------------------------------------------------------------
/src/covers/gumroad-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/covers/gumroad-cover.png
--------------------------------------------------------------------------------
/src/css/book.less:
--------------------------------------------------------------------------------
1 | @book-color: #e8515b;
2 |
--------------------------------------------------------------------------------
/src/meta/epub.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | ...
--------------------------------------------------------------------------------
/src/meta/html.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | ...
--------------------------------------------------------------------------------
/src/meta/metadata.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Used by Pandoc to print covers:
3 | title: Essential Scala
4 | author: "Noel Welsh and Dave Gurnell"
5 | date: "Version 1.3, April 2017"
6 | copyright: "2014-2017"
7 | # Used by Pandoc to generate the PDF cover...
8 | # see src/css/book.css for HTML/ePub covers:
9 | coverColor: "E8515B"
10 | # Used by Grunt to name output files:
11 | filenameStem: "essential-scala"
12 | # Used by Grunt to create a ZIP of source code:
13 | exercisesRepo: null # "git@github.com:underscoreio/essential-scala-code.git"
14 | # Used by Grunt to assemble pandoc command line:
15 | pages:
16 | - index.md
17 | - thanks.md
18 | - getting-started/index.md
19 |
20 | - intro/index.md
21 | - intro/objects.md
22 | - intro/literals.md
23 | - intro/object-literals.md
24 | - intro/writing-methods.md
25 | - intro/expressions.md
26 | - intro/conclusion.md
27 |
28 | - classes/index.md
29 | - classes/classes.md
30 | - classes/apply.md
31 | - classes/companion-objects.md
32 | - classes/case-classes.md
33 | - classes/pattern-matching.md
34 | - classes/conclusions.md
35 |
36 | - traits/index.md
37 | - traits/traits.md
38 | - traits/sealed-traits.md
39 | - traits/modelling-data-with-traits.md
40 | - traits/working-with-data.md
41 | - traits/recursive-data.md
42 | - traits/extended-examples.md
43 | - traits/conclusions.md
44 |
45 | - sequencing/index.md
46 | - sequencing/generics.md
47 | - sequencing/functions.md
48 | - sequencing/working-with-data.md
49 | - sequencing/modelling-data.md
50 | - sequencing/sequencing-computation.md
51 | - sequencing/variance.md
52 | - sequencing/conclusions.md
53 |
54 | - collections/index.md
55 | - collections/seq.md
56 | - collections/working-with-seq.md
57 | - collections/for-comprehensions.md
58 | - collections/options.md
59 | - collections/meeting-monads.md
60 | - collections/for-comprehensions-redux.md
61 | - collections/map-and-set.md
62 | - collections/ranges.md
63 | - collections/random-data.md
64 |
65 | - implicits/index.md
66 | - implicits/instances.md
67 | - implicits/organising.md
68 | - implicits/creating.md
69 | - implicits/implicit-parameters.md
70 | - implicits/enrichment.md
71 | - implicits/using-type-classes.md
72 | - implicits/conversions.md
73 | - implicits/json.md
74 |
75 | - conclusions.md
76 |
77 | - start-of-appendix.md
78 |
79 | - pattern-matching/index.md
80 | - pattern-matching/extractors.md
81 |
82 | - collections-redux/index.md
83 | - collections-redux/seq-implementations.md
84 | - collections-redux/arrays-and-strings.md
85 | - collections-redux/iterators.md
86 | - collections-redux/traversable.md
87 | - collections-redux/java-interop.md
88 | - collections-redux/mutable-seq.md
89 |
90 | - solutions.md
91 | - links.md
92 |
93 | previewPages:
94 | - index.md
95 | - thanks.md
96 | - getting-started/index.md
97 |
98 | - intro/index.md
99 | - intro/objects.md
100 | - intro/literals.md
101 | - intro/object-literals.md
102 | - intro/writing-methods.md
103 | - intro/expressions.md
104 | - intro/conclusion.md
105 |
106 | - classes/index.md
107 | - classes/classes.md
108 | - classes/apply.md
109 | - classes/companion-objects.md
110 | - classes/case-classes.md
111 | - classes/pattern-matching.md
112 | - classes/conclusions.md
113 |
114 | - traits/index.md
115 | - traits/traits.md
116 | - traits/sealed-traits.md
117 | - traits/modelling-data-with-traits.md
118 | - traits/working-with-data.md
119 | - traits/recursive-data.md
120 | - traits/extended-examples.md
121 | - traits/conclusions.md
122 |
123 | - links.md
124 | ...
125 |
--------------------------------------------------------------------------------
/src/meta/pdf.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | papersize: "a5paper"
3 | fontsize: "11pt"
4 | geometry: "margin=.75in"
5 | ...
--------------------------------------------------------------------------------
/src/pages/classes/apply.md:
--------------------------------------------------------------------------------
1 | ## Objects as Functions
2 |
3 | In the final exercise of the previous section, we defined a class called `Adder`:
4 |
5 | ```tut:book:silent
6 | class Adder(amount: Int) {
7 | def add(in: Int): Int = in + amount
8 | }
9 | ```
10 |
11 | In the discussion we described an `Adder` as an object representing a computation---a bit like having a method that we can pass around as a value.
12 |
13 | This is such a powerful concept that Scala has a fully blown set of language features for creating objects that behave like computations. These objects are called *functions*, and are the basis of *functional programming*.
14 |
15 | ### The apply method
16 |
17 | For now we are going to look at just one of Scala's features supporting functional programming---*function application syntax*.
18 |
19 | In Scala, by convention, an object can be "called" like a function if it has a method called `apply`. Naming a method `apply` affords us a special shortened call syntax: `foo.apply(args)` becomes `foo(args)`.
20 |
21 | For example, let's rename the `add` method in `Adder` to `apply`:
22 |
23 | ```tut:book:silent
24 | class Adder(amount: Int) {
25 | def apply(in: Int): Int = in + amount
26 | }
27 | ```
28 |
29 | ```tut:book
30 | val add3 = new Adder(3)
31 | add3.apply(2)
32 | add3(4) // shorthand for add3.apply(4)
33 | ```
34 |
35 | With this one simple trick, objects can "look" syntactically like functions. There are lots of things that we can do with objects that we can't do with methods, including assign them to variables and pass them around as arguments.
36 |
37 |
38 |
39 | #### Function Application Syntax {-}
40 |
41 | The method call `object.apply(parameter, ...)` can also be written as `object(parameter, ...)`
42 |
43 |
44 |
45 | ### Take home points
46 |
47 | In this section we looked at *function application syntax*, which lets us "call" an object as if it is a function.
48 |
49 | Function application syntax is available for any object defining an `apply` method.
50 |
51 | With function application syntax, we now have first class values that behave like computations. Unlike methods, objects can be passed around as data. This takes us one step closer towards true functional programming in Scala.
52 |
53 | ### Exercises
54 |
55 | #### When is a Function not a Function?
56 |
57 | We'll get a chance to write some code at the end of the next section. For now we should think about an important theoretical question:
58 |
59 | How close does function application syntax get us to creating truly reusable objects to do computations for us? What are we missing?
60 |
61 |
62 | The main thing we're missing is *types*, which are the way we properly abstract across values.
63 |
64 | At the moment we can define a class called `Adder` to capture the idea of adding to a number, but that code isn't properly portable across codebases---other developers need to know about our specific class to use it.
65 |
66 | We could define a library of common function types with names like `Handler`, `Callback`, `Adder`, `BinaryAdder`, and so on, but this quickly becomes impractical.
67 |
68 | Later on we will see how Scala copes with this problem by defining a generic set of function types that we can use in a wide variety of situations.
69 |
70 |
--------------------------------------------------------------------------------
/src/pages/classes/conclusions.md:
--------------------------------------------------------------------------------
1 | ## Conclusions
2 |
3 | In this section we've explored *classes*. We have seen that classes allow us to abstract over objects. That is, to define objects that share properties in common and have a common type.
4 |
5 | We also looked at *companion objects*, which are used in Scala to define auxillary constructors and other utility methods that don't belong on a class.
6 |
7 | Finally, we introduced *case classes*, which greatly reduce boilerplate code and allow *pattern-matching*, a new way of interacting with objects, in addition to method calls.
8 |
--------------------------------------------------------------------------------
/src/pages/classes/index.md:
--------------------------------------------------------------------------------
1 | # Objects and Classes
2 |
3 | In the previous chapter we saw how to create objects and interact with them via method calls. In this section we're going to see how we can abstract over objects using *classes*. Classes are a template for constructing objects. Given a class we can make many objects that have the same type and share common properties.
4 |
--------------------------------------------------------------------------------
/src/pages/classes/pattern-matching.md:
--------------------------------------------------------------------------------
1 | ## Pattern Matching
2 |
3 | Until now we have interacted with objects by calling methods or accessing fields. With case classes we can interact in another way, via *pattern matching*.
4 |
5 | Pattern matching is like an extended `if` expression that allows us to evaluate an expression depending on the "shape" of the data. Recall the `Person` case class we've seen in previous examples:
6 |
7 | ```tut:book:silent
8 | case class Person(firstName: String, lastName: String)
9 | ```
10 |
11 | Now imagine we wanted to implement a `Stormtrooper` that is looking for members of the rebellion. We could use pattern matching like this:
12 |
13 | ```tut:book:silent
14 | object Stormtrooper {
15 | def inspect(person: Person): String =
16 | person match {
17 | case Person("Luke", "Skywalker") => "Stop, rebel scum!"
18 | case Person("Han", "Solo") => "Stop, rebel scum!"
19 | case Person(first, last) => s"Move along, $first"
20 | }
21 | }
22 | ```
23 |
24 | Notice the syntax for a pattern (`Person("Luke", "Skywalker")`) matches the syntax for constructing the object the pattern matches (`Person("Luke", "Skywalker")`).
25 |
26 | Here it is in use:
27 |
28 | ```tut:book
29 | Stormtrooper.inspect(Person("Noel", "Welsh"))
30 | Stormtrooper.inspect(Person("Han", "Solo"))
31 | ```
32 |
33 |
34 | #### Pattern Matching Syntax {-}
35 |
36 | The syntax of a pattern matching expression is
37 |
38 | ```scala
39 | expr0 match {
40 | case pattern1 => expr1
41 | case pattern2 => expr2
42 | ...
43 | }
44 | ```
45 |
46 | where
47 |
48 | - the expression `expr0` evaluates to the value we match;
49 | - the patterns, or *guards*, `pattern1`, `pattern2`, and so on are checked against this value in order; and
50 | - the right-hand side expression (`expr1`, `expr2`, and so on) of the first pattern that matches is evaluated[^compilation].
51 |
52 | Pattern matching is itself an expression and thus evaluates to a value---the value of the matched expression.
53 |
54 |
55 | [^compilation]: In reality patterns are compiled to a more efficient form than a sequence of tests, but the semantics are the same.
56 |
57 |
58 | ### Pattern Syntax
59 |
60 | Scala has an expressive syntax for writing patterns or guards. For case classes the pattern syntax matches the constructor syntax. Take the data
61 |
62 | ```tut:book
63 | Person("Noel", "Welsh")
64 | ```
65 |
66 | A pattern to match against the `Person` type is written
67 |
68 | ```scala
69 | Person(pat0, pat1)
70 | ```
71 |
72 | where `pat0` and `pat1` are patterns to match against the `firstName` and `lastName` respectively. There are four possible patterns we could use in place of `pat0` or `pat1`:
73 |
74 | 1. A name, which matches any value at that position and binds it to the given name. For example, the pattern `Person(first, last)` binds the name `first` to the value `"Noel"`, and the name `last` to the value `"Welsh"`.
75 |
76 | 2. An underscore (`_`), which matches any value and ignores it. For example, as Stormtroopers only care about the first name of ordinary citizens we could just write `Person(first, _)` to avoid binding a name to the value of the `lastName`.
77 |
78 | 3. A literal, which successfully matches only the value the literal respresents. So , for example, the pattern `Person("Han", "Solo")` matches the `Person` with first name `"Han"` and last name `"Solo"`.
79 |
80 | 4. Another case class using the same constructor style syntax.
81 |
82 | Note there is a lot more we can do with pattern matching, and pattern matching is actually extensible. We'll look at these features in a later section.
83 |
84 |
85 | ### Take Home Points
86 |
87 | Case classes allow a new form of interaction, called *pattern matching*. Pattern matching allows us to take apart a case class, and evaluate different expressions depending on what the case class contains.
88 |
89 | The syntax for pattern matching is
90 |
91 | ```scala
92 | expr0 match {
93 | case pattern1 => expr1
94 | case pattern2 => expr2
95 | ...
96 | }
97 | ```
98 |
99 | A pattern can be one of
100 |
101 | 1. a name, binding any value to that name;
102 | 2. an underscore, matching any value and ignoring it;
103 | 3. a literal, matching the value the literal denotes; or
104 | 4. a constructor-style pattern for a case class.
105 |
106 | ### Exercises
107 |
108 | #### Feed the Cats
109 |
110 | Define an object `ChipShop` with a method `willServe`. This method should accept a `Cat` and return true if the cat’s favourite food is chips, and false otherwise. Use pattern matching.
111 |
112 |
113 | We can start by writing the skeleton suggested by the problem text.
114 |
115 | ```tut:book:silent
116 | case class Cat(name: String, colour: String, food: String)
117 | ```
118 |
119 | ```scala
120 | object ChipShop {
121 | def willServe(cat: Cat): Boolean =
122 | cat match {
123 | case Cat(???, ???, ???) => ???
124 | }
125 | }
126 | ```
127 |
128 | As the return type is `Boolean` we know we need at least two cases, one for true and one for false. The text of the exercise tells us what they should be: cats that prefer chips, and all other cats. We can implement this with a literal pattern and an `_` pattern.
129 |
130 | ```tut:book:silent
131 | object ChipShop {
132 | def willServe(cat: Cat): Boolean =
133 | cat match {
134 | case Cat(_, _, "Chips") => true
135 | case Cat(_, _, _) => false
136 | }
137 | }
138 | ```
139 |
140 |
141 |
142 | #### Get Off My Lawn!
143 |
144 | In this exercise we're going to write a simulator of my Dad, the movie critic. It's quite simple: any movie directed by Clint Eastwood gets a rating 10.0, any movie directed by John McTiernan gets a 7.0, while any other movie gets a 3.0. Implement an object called `Dad` with a method `rate` which accepts a `Film` and returns a `Double`. Use pattern matching.
145 |
146 |
147 | ```scala
148 | object Dad {
149 | def rate(film: Film): Double =
150 | film match {
151 | case Film(_, _, _, Director("Clint", "Eastwood", _)) => 10.0
152 | case Film(_, _, _, Director("John", "McTiernan", _)) => 7.0
153 | case _ => 3.0
154 | }
155 | }
156 | ```
157 |
158 | Pattern matching is becoming quite verbose in this case. Later on we'll learn how we can use pattern matching to match a particular value, called a *constant pattern*.
159 |
160 |
--------------------------------------------------------------------------------
/src/pages/classes/scala-type-hierarchy.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/classes/scala-type-hierarchy.pdf
--------------------------------------------------------------------------------
/src/pages/classes/scala-type-hierarchy.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/classes/scala-type-hierarchy.sketch
--------------------------------------------------------------------------------
/src/pages/collections-redux/arrays-and-strings.md:
--------------------------------------------------------------------------------
1 | ## Arrays and Strings
2 |
3 | Arrays and strings in Scala correspond to Java's arrays and strings.
4 |
5 | ```tut:book
6 | "this is a string"
7 | ```
8 |
9 | Yet all the familiar collection methods are available on them.
10 |
11 | ```tut:book
12 | "is it true?".map(elt => true)
13 |
14 | Array(1, 2, 3).map(_ * 2)
15 | ```
16 |
17 | This conversion is done automatically using implicit conversions. There are two conversions. The Wrapped conversions (`WrappedArray` and `WrappedString`) wrap the original array or string in an object supporting the `Seq` methods. Operations on such a wrapped object return another wrapped object.
18 |
19 | ```tut:book
20 | val sequence = new scala.collection.immutable.WrappedString("foo")
21 |
22 | sequence.reverse
23 | ```
24 |
25 | The Ops conversions (`ArrayOps` and `StringOps`) add methods that return an object of the original type. Thus these objects are short-lived.
26 |
27 | ```tut:book
28 | val sequence = new scala.collection.immutable.StringOps("foo")
29 |
30 | sequence.reverse
31 | ```
32 |
33 | The choice of conversion is based on the required type. If we use a string, say, where a `Seq` is expected the string will be wrapped. If we just want to use a `Seq` method on a string then an Op conversion will be used.
34 |
35 | ```tut:book
36 | val sequence: Seq[Char] = "foo"
37 |
38 | sequence.getClass
39 | ```
40 |
41 | ### Performance
42 |
43 | You might be worried about the performance of implicit conversions. The Ops conversions are normally optimised away. The Wrapped conversions can give a small performance hit which may be an issue in particularly performance sensitive code.
44 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/index.md:
--------------------------------------------------------------------------------
1 | # Collections Redux
2 |
3 | This optional section covers some more details of the collections framework that typically aren't used in day-to-day programming. This includes the different sequence implementations available, details of collections operations on arrays and strings, some of the core traits in the framework, and details of Java interoperation.
4 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/iterators.md:
--------------------------------------------------------------------------------
1 | ## Iterators and Views
2 |
3 | Iterators and views are two parts of the collection library that don't find much use outside of a few special cases.
4 |
5 | ### Iterators
6 |
7 | Scala's iterators are like Java's iterators. You can use them to walk through the elements of a collection, but only once. Iterators have `hasNext` and `next` methods, with the obvious semantics. Otherwise they behave like sequences, though they don't inherit from `Seq`.
8 |
9 | Iterators don't find a great deal of use in Scala. Two primary use cases are operating on collections that are too large to fit in memory or in particularly high performance code.
10 |
11 |
12 | ### Views
13 |
14 | When performing a sequence of transformations on a collection, a number of intermediate collections will be constructed. For example, in the below example two intermediate collections will be created by the first and second call to `map`.
15 |
16 | ```tut:book
17 | Seq(1, 2, 3).map(_ * 2).map(_ + 4).map(_.toString)
18 | ```
19 |
20 | It is as if we'd written
21 |
22 | ```tut:book:silent
23 | val intermediate1 = Seq(1, 2, 3).map(_ * 2)
24 | val intermediate2 = intermediate1.map(_ + 4)
25 | val result = intermediate2.map(_.toString)
26 | ```
27 |
28 | These intermediate collections are not strictly necessary. We could instead do the full sequence of transformations on an element-by-element basis. Views allows this. We create a view by calling the `view` method on any collection. Any traversals of a view are only applied when the `force` method is called.
29 |
30 | ```tut:book
31 | val view = Seq(1, 2, 3).view.map(_ * 2).map(_ + 4).map(_.toString)
32 |
33 | view.force
34 | ```
35 |
36 | Note that when a view is forced the original type is retained.
37 |
38 | For very large collections of items with many stages of transformations a view can be worthwhile. For modest sizes views are usually slower than creating the intermediate data structures.
39 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/java-interop.md:
--------------------------------------------------------------------------------
1 | ## Java Interoperation
2 |
3 | The prefered way to convert between Scala and Java collections is use the `JavaConverters` implicit conversions. We use it by importing `scala.collection.JavaConverters._` and then methods `asJava` and `asScala` become available on many of the collections.
4 |
5 | ```tut:book:silent
6 | import scala.collection.JavaConverters._
7 | ```
8 |
9 | ```tut:book
10 | Seq(1, 2, 3).asJava
11 | ```
12 |
13 | Java does not distinguish mutable and immutable collections at the type level but the conversions do preserve this property by throwing `UnsupportOperationException` as appropriate.
14 |
15 | ```tut:book
16 | val javaCollection = Seq(1, 2, 3).asJava
17 | ```
18 |
19 | ```tut:book:fail:silent
20 | javaCollection.set(0, 5)
21 | // java.lang.UnsupportedOperationException
22 | // at java.util.AbstractList.set(AbstractList.java:115)
23 | // ...
24 | ```
25 |
26 | The conversions go the other way as well.
27 |
28 | ```tut:book
29 | val list: java.util.List[Int] = new java.util.ArrayList[Int]()
30 |
31 | list.asScala
32 | ```
33 |
34 | Note that the Scala equivalent is a mutable collection. If we mutate an element we see that the underlying Java collection is also changed. This holds for all conversions; they always share data and are not copied.
35 |
36 | ```tut:book
37 | list.asScala += 5
38 |
39 | list
40 | ```
41 |
42 | ### JavaConversions
43 |
44 | There is another set of conversions in `scala.collection.JavaConversions`, which perform conversions without needing the calls to `asJava` or `asScala`. Many people find this confusing in large systems and thus it is not recommended.
45 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/mutable-seq.md:
--------------------------------------------------------------------------------
1 | ## Mutable Sequences
2 |
3 | Most of the interfaces we've have covered so far do not have any side-effects---like the `copy` method on a case class, they return a new copy of the sequence. Sometimes, however, we need mutable collections. Fortunately, Scala provides two parallel collections hierarchies, one in the `scala.collection.mutable` package and one in the `scala.collection.immutable` package.
4 |
5 | The default `Seq` is defined to be `scala.collection.immutable.Seq`. If we want a mutable sequence we can use `scala.collection.mutable.Seq`.
6 |
7 | ```tut:book
8 | val mutableCollection = scala.collection.mutable.Seq(1, 2, 3)
9 | ```
10 |
11 | Note that the concrete implementation class is now an `ArrayBuffer` and not a `List`.
12 |
13 | ### Destructive update
14 |
15 | In addition to all the methods of an immutable sequence, a mutable sequence can be updated using the `update` method. Note that `update` returns `Unit`, so no value is printed in the REPL after this call. When we print the original sequence we see it is changed:
16 |
17 | ```tut:book:silent
18 | mutableCollection.update(0, 5)
19 | ```
20 |
21 | ```tut:book
22 | mutableCollection
23 | ```
24 |
25 | A more idiomatic way of calling `update` is to use *assignment operator syntax*, which is another special syntax built in to Scala, similar to infix operator syntax and function application syntax:
26 |
27 | ```tut:book:silent
28 | mutableCollection(1) = 7
29 | ```
30 |
31 | ```tut:book
32 | mutableCollection
33 | ```
34 |
35 | ### Immutable methods on mutable sequences
36 |
37 | Methods defined on both mutable and immutable sequences will never perform destructive updates. For example, `:+` always returns a new copy of the sequence without updating the original:
38 |
39 | ```tut:book
40 | val mutableCollection = scala.collection.mutable.Seq[Int](1, 2, 3)
41 |
42 | mutableCollection :+ 4
43 |
44 | mutableCollection
45 | ```
46 |
47 |
48 | #### Using Mutable Collections Safely
49 |
50 | Scala programmers tend to favour immutable collections and only bring in mutable ones in specific circumstances. Using `import scala.collection.mutable._` at the top of a file tends to create a whole series of naming collisions that we have to work around.
51 |
52 | To work around this, I suggest importing the `mutable` package itself rather than its contents. We can then explicitly refer to any mutable collection using the package name as a prefix, leaving the unprefixed names referring to the immutable versions:
53 |
54 | ```tut:book:silent
55 | import scala.collection.mutable
56 | ```
57 |
58 | ```tut:book
59 | mutable.Seq(1, 2, 3)
60 |
61 | Seq(1, 2, 3)
62 | ```
63 |
64 |
65 | ### In summary
66 |
67 | Scala's collections library includes mutable sequences in the `scala.collection.mutable` package. The main extra operation is `update`:
68 |
69 | +------------+------------+-------------------+-------------+
70 | | Method | We have | We provide | We get |
71 | +============+============+===================+=============+
72 | | `update` | `Seq[A]` | `Int`, `A` | `Unit` |
73 | +------------+------------+-------------------+-------------+
74 |
75 |
76 | ### Exercises
77 |
78 | #### Animals
79 |
80 | Create a `Seq` containing the `String`s `"cat"`, `"dog"`, and `"penguin"`. Bind it to the name `animals`.
81 |
82 |
95 |
96 | What happens if you prepend the `Int` `2` to `animals`? Why? Try it out... were you correct?
97 |
98 |
99 | The returned sequence has type `Seq[Any]`. It is perfectly valid to return a supertype (in this case `Seq[Any]`) from a non-destructive operation.
100 |
101 | ```scala
102 | 2 +: animals
103 | ```
104 |
105 | You might expect a type error here, but Scala is capable of determining the least upper bound of `String` and `Int` and setting the type of the returned sequence accordingly.
106 |
107 | In most real code appending an `Int` to a `Seq[String]` would be an error. In practice, the type annotations we place on methods and fields protect against this kind of type error, but be aware of this behaviour just in case.
108 |
109 |
110 | Now create a mutable sequence containing `"cat"`, `"dog"`, and `"penguin"` and `update` an element to be an `Int`. What happens?
111 |
112 |
113 | If we try to mutate a sequence we *do* get a type error:
114 |
115 | ```tut:book
116 | val mutable = scala.collection.mutable.Seq("cat", "dog", "penguin")
117 | ```
118 |
119 | ```tut:book:silent:fail
120 | mutable(0) = 2
121 | // :9: error: type mismatch;
122 | // found : Int(2)
123 | // required: String
124 | // mutable(0) = 2
125 | // ^
126 | ```
127 |
128 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/seq-implementations.md:
--------------------------------------------------------------------------------
1 | ## Sequence Implementations
2 |
3 | We've seen that the Scala collections separate interface from implementation. This means we can work with all collections in a generic manner. However different concrete implementations have different performance characteristics, so we must be aware of the available implementations so we can choose appropriately. Here we look at the mostly frequently used implementations of `Seq`. For full details on all the available implementation see [the docs](http://docs.scala-lang.org/overviews/collections/introduction.html).
4 |
5 | ### Performance Characteristics
6 |
7 | The collections framework distinguishes at the type level two general classes of sequences. Sequences implementing `IndexedSeq` have efficient `apply`, `length`, and (if mutable) `update` operations, while `LinearSeq`s have efficient `head` and `tail` operations. Neither have any additional operations over `Seq`.
8 |
9 | ### Immutable Implementations
10 |
11 | The main immutable `Seq` implementations are `List`, and `Stream`, and `Vector`.
12 |
13 | #### List
14 |
15 | A `List` is a singly linked list. It has constant time access to the first element and remainder of the list (`head`, and `tail`) and is thus a `LinearSeq`. It also has constant time prepending to the front of the list, but linear time appending to the end. `List` is the default `Seq` implementation.
16 |
17 | #### Stream
18 |
19 | A `Stream` is like a list except its elements are computed on demand, and thus it can have infinite size. Like other collections we can create streams by calling the `apply` method on the companion object.
20 |
21 | ```tut:book
22 | Stream(1, 2, 3)
23 | ```
24 |
25 | Note that only the first element is printed. The others will be computed when we try to access them.
26 |
27 | We can also use the `#::` method to construct a stream from individual elements, starting from `Stream.empty`.
28 |
29 | ```tut:book
30 | Stream.empty.#::(3).#::(2).#::(1)
31 | ```
32 |
33 | We can also use the more natural operator syntax.
34 |
35 | ```tut:book
36 | 1 #:: 2 #:: 3 #:: Stream.empty
37 | ```
38 |
39 | This method allows us to create a infinite stream. Here's an infinite stream of 1s:
40 |
41 | ```tut:book:silent
42 | def streamOnes: Stream[Int] = 1 #:: streamOnes
43 | ```
44 |
45 | ```tut:book
46 | streamOnes
47 | ```
48 |
49 | Because elements are only evaluated as requested, calling `streamOnes` doesn't lead to infinite recursion. When we take the first five elements (and convert them to a `List`, so they'll all print out) we see we have what we want.
50 |
51 | ```tut:book
52 | streamOnes.take(5).toList
53 | ```
54 |
55 | #### Vector
56 |
57 | `Vector` is the final immutable sequence we'll consider. Unlike `Stream` and `List` it is an `IndexedSeq`, and thus offers fast random access and updates. It is the default immutable `IndexedSeq`, which we can see if we create one.
58 |
59 | ```tut:book
60 | scala.collection.immutable.IndexedSeq(1, 2, 3)
61 | ```
62 |
63 | Vectors are a good choice if you want both random access and immutability.
64 |
65 |
66 | ### Mutable Implementations
67 |
68 | The mutable collections are probably more familiar. In addition to linked lists and arrays (which we discuss in more detail later) there are buffers, which allow for efficient construction of certain data structures.
69 |
70 | #### Buffers
71 |
72 | `Buffer`s are used when you want to efficiently create a data structure an item at a time. An `ArrayBuffer` is an `IndexedSeq` which also has constant time appends. A `ListBuffer` is like a `List` with constant time prepend *and* append (though note it is mutable, unlike `List`).
73 |
74 | Buffers' add methods to support destructive prepends and appends. For example, the `+=` is destructive append.
75 |
76 | ```tut:book
77 | val buffer = new scala.collection.mutable.ArrayBuffer[Int]()
78 |
79 | buffer += 1
80 |
81 | buffer
82 | ```
83 |
84 |
85 | #### StringBuilder
86 |
87 | A `StringBuilder` is essentially a buffer for building strings. It is mostly the same as Java's `StringBuilder` except that it implements standard Scala collections method where there is a conflict. So, for example, the `reverse` method creates a new `StringBuilder` unlike in Java.
88 |
89 | #### LinkedLists
90 |
91 | Mutable singly `LinkedList`s and `DoubleLinkedList`s work for the most part just like `List`. A `DoubleLikeList` maintains both a `prev` and `next` pointer and so allows for efficient removal of an element.
92 |
--------------------------------------------------------------------------------
/src/pages/collections-redux/traversable.md:
--------------------------------------------------------------------------------
1 | ## Traversable and Iterable
2 |
3 | So far we've avoided discussing the finer details of the collection class hierarchy. As we near the end of this section it is time to quickly go over some of the intricacies.
4 |
5 | ### Traversable
6 |
7 | The trait `Traversable` sits at the top of the collection hierarchy and represents a collection that allows traversal of its contents. The only abstract operation is `foreach`. Most of the collection methods are implemented in `Traversable`, though classes extending it may reimplement methods for performance.
8 |
9 | #### TraversableOnce
10 |
11 | `TraversableOnce` represents a collection that can be traversed one or more times. It is primarily used to reduce code duplication between `Iterator`s and `Traversable`.
12 |
13 | ### Iterable
14 |
15 | `Iterable` is the next trait below `Traversable`. It has a single abstract method `iterator` that should return an `Iterator` over the collection's contents. The `foreach` method is implemented in terms of this. It adds a few methods to `Traversable` that can only be efficiently implemented when an iterator is available.
16 |
--------------------------------------------------------------------------------
/src/pages/collections/for-comprehensions-redux.md:
--------------------------------------------------------------------------------
1 | ## For Comprehensions Redux
2 |
3 | Earlier we looked at the fundamentals of for comprehensions. In this section we're going to looking at some handy additional features they offer, and at idiomatic solutions to common problems.
4 |
5 | ### Filtering
6 |
7 | It's quite common to only process selected elements. We can do this with comprehensions by adding an `if` clause after the generator expression. So to process only the positive elements of sequence we could write
8 |
9 | ```tut:book
10 | for(x <- Seq(-2, -1, 0, 1, 2) if x > 0) yield x
11 | ```
12 |
13 | The code is converted to a `withFilter` call, or if that doesn't exist to `filter`.
14 |
15 | Note that, unlike the normal `if` expression, an `if` clause in a generator does not have parentheses around the condition. So we write `if x > 0` not `if(x > 0)` in a for comprehension.
16 |
17 | ### Parallel Iteration
18 |
19 | Another common problem is to iterate over two or more collections in parallel. For example, say we have the sequences `Seq(1, 2, 3)` and `Seq(4, 5, 6)` and we want to add together elements with the same index yielding `Seq(5, 7 , 9)`. If we write
20 |
21 | ```tut:book
22 | for {
23 | x <- Seq(1, 2, 3)
24 | y <- Seq(4, 5, 6)
25 | } yield x + y
26 | ```
27 |
28 | we see that iterations are nested. We traverse the first element from the first sequence and then all the elements of the second sequence, then the second element from the first sequence and so on.
29 |
30 | The solution is to `zip` together the two sequences, giving a sequence containing pairs of corresponding elements
31 |
32 | ```tut:book
33 | Seq(1, 2, 3).zip(Seq(4, 5, 6))
34 | ```
35 |
36 | With this we can easily compute the result we wanted
37 |
38 | ```tut:book
39 | for(x <- Seq(1, 2, 3).zip(Seq(4, 5, 6))) yield { val (a, b) = x; a + b }
40 | ```
41 |
42 | Sometimes you want to iterate over the values in a sequence and their indices. For this case the `zipWithIndex` method is provided.
43 |
44 | ```tut:book
45 | for(x <- Seq(1, 2, 3).zipWithIndex) yield x
46 | ```
47 |
48 | Finally note that `zip` and `zipWithIndex` are available on all collection classes, including `Map` and `Set`.
49 |
50 | ### Pattern Matching
51 |
52 | The pattern on the left hand side of a generator is not named accidentally. We can include any pattern there and only process results matching the pattern. This provides another way of filtering results. So instead of:
53 |
54 | ```tut:book
55 | for(x <- Seq(1, 2, 3).zip(Seq(4, 5, 6))) yield { val (a, b) = x; a + b }
56 | ```
57 |
58 | we can write:
59 |
60 | ```tut:book
61 | for((a, b) <- Seq(1, 2, 3).zip(Seq(4, 5, 6))) yield a + b
62 | ```
63 |
64 | ### Intermediate Results
65 |
66 | It is often useful to create an intermediate result within a sequence of generators. We can do this by inserting an assignment expression like so:
67 |
68 | ```tut:book
69 | for {
70 | x <- Seq(1, 2, 3)
71 | square = x * x
72 | y <- Seq(4, 5, 6)
73 | } yield square * y
74 | ```
75 |
--------------------------------------------------------------------------------
/src/pages/collections/for-comprehensions.md:
--------------------------------------------------------------------------------
1 | ## For Comprehensions
2 |
3 | We've discussed the main collection transformation functions---`map`, `flatMap`, `foldLeft`, `foldRight`, and `foreach`---and seen that they provide a powerful way of working with collections. They can become unwieldy to work with when dealing with many collections or many nested transformations. Fortunately Scala has special syntax for working with collections (in fact any class that implements `map` and `flatMap`) that makes complicated operations simpler to write. This syntax is known as a *for comprehension*.
4 |
5 |
6 | #### Not Your Father's For Loops {-}
7 |
8 | *for comprehensions* in Scala are very different to the C-style *for loops* in Java. There is no direct equivalent of either language's syntax in the other.
9 |
10 |
11 | Let's start with a simple example. Say we have the sequence `Seq(1, 2, 3)` and we wish to create a sequence with every element doubled. We know we can write
12 |
13 | ```tut:book
14 | Seq(1, 2, 3).map(_ * 2)
15 | ```
16 |
17 | The equivalent program written with a for comprehension is:
18 |
19 | ```tut:book
20 | for {
21 | x <- Seq(1, 2, 3)
22 | } yield x * 2
23 | ```
24 |
25 | We call the expression containing the `<-` a *generator*, with a *pattern* on the left hand side and a *generator expression* on the right. A for comprehension iterates over the elements in the generator, binding each element to the pattern and calling the `yield` expression. It combines the yielded results into a sequence of the same type as the original generator.
26 |
27 | In simple examples like this one we don't really see the power of for comprehensions---direct use of `map` and `flatMap` are often more compact in the simplest case. Let's try a more complicated example. Say we want to double all the numbers in `Seq(Seq(1), Seq(2, 3), Seq(4, 5, 6))` and return a flattened sequence of the results. To do this with `map` and `flatMap` we must nest calls:
28 |
29 | ```tut:book
30 | val data = Seq(Seq(1), Seq(2, 3), Seq(4, 5, 6))
31 |
32 | data.flatMap(_.map(_ * 2))
33 | ```
34 |
35 | This is getting complicated. The equivalent for comprehension is much more ... comprehensible.
36 |
37 | ```tut:book
38 | for {
39 | subseq <- data
40 | element <- subseq
41 | } yield element * 2
42 | ```
43 |
44 | This gives us an idea of what the for comprehensions does. A general for comprehension:
45 |
46 | ```tut:book:invisible
47 | val a: Seq[Int] = Seq.empty
48 | val b: Seq[Int] = Seq.empty
49 | val c: Seq[Int] = Seq.empty
50 | val e: Int = 0
51 | ```
52 |
53 | ```tut:book:silent
54 | for {
55 | x <- a
56 | y <- b
57 | z <- c
58 | } yield e
59 | ```
60 |
61 | translates to:
62 |
63 | ```tut:book:silent
64 | a.flatMap(x => b.flatMap(y => c.map(z => e)))
65 | ```
66 |
67 | The intuitive understanding of the code is to iterate through all of the sequences in the generators, mapping the `yield` expression over every element therein, and accumulating a result of the same type as sequence fed into the first generator.
68 |
69 | Note that if we omit the `yield` keyword before the final expression, the overall type of the `for` comprehension becomes `Unit`. This version of the `for` comprehension is executed purely for its side-effects, and any result is ignored. Revisiting the doubling example from earlier, we can print the results instead of returning them:
70 |
71 | ```tut:book:silent
72 | for {
73 | seq <- Seq(Seq(1), Seq(2, 3))
74 | elt <- seq
75 | } println(elt * 2) // Note: no 'yield' keyword
76 | // 2
77 | // 4
78 | // 6
79 | ```
80 |
81 | The equivalent method calls use `flatMap` as usual and `foreach` in place of the final `map`:
82 |
83 | ```scala
84 | a.flatMap(x => b.flatMap(y => c.foreach(z => e)))
85 | ```
86 |
87 | We can use parentheses instead of braces to delimit the generators in a for loop. However, we must use semicolons to separate the generators if we do. Thus:
88 |
89 | ```tut:book:silent
90 | for (
91 | x <- a;
92 | y <- b;
93 | z <- c
94 | ) yield e
95 | ```
96 |
97 | is equivalent to:
98 |
99 | ```tut:book:silent
100 | for {
101 | x <- a
102 | y <- b
103 | z <- c
104 | } yield e
105 | ```
106 |
107 | Some developers prefer to use parentheses when there is only one generator and braces otherwise:
108 |
109 | ```tut:book:silent
110 | for(x <- Seq(1, 2, 3)) yield {
111 | x * 2
112 | }
113 | ```
114 |
115 | We can also use braces to wrap the yield expression and convert it to a *block* as usual:
116 |
117 | ```scala
118 | for {
119 | // ...
120 | } yield {
121 | // ...
122 | }
123 | ```
124 |
125 | ### Exercises
126 |
127 | *(More) Heroes of the Silver Screen*
128 |
129 | Repeat the following exercises from the previous section *without using `map` or `flatMap`*:
130 |
131 | *Nolan Films*
132 |
133 | List the names of the films directed by Christopher Nolan.
134 |
135 |
136 | ```tut:book:invisible
137 | case class Film(name: String, imdbRating: Double)
138 | case class Director(name: String, films: Seq[Film])
139 | val nolan = Director("Christopher Nolan", Seq.empty)
140 | val directors: Seq[Director] = Seq(nolan)
141 | ```
142 |
143 | ```tut:book:silent
144 | for {
145 | film <- nolan.films
146 | } yield film.name
147 | ```
148 |
149 |
150 | *Cinephile*
151 |
152 | List the names of all films by all directors.
153 |
154 |
155 | ```tut:book:silent
156 | for {
157 | director <- directors
158 | film <- director.films
159 | } yield film.name
160 | ```
161 |
162 |
163 | *High Score Table*
164 |
165 | Find all films sorted by descending IMDB rating:
166 |
167 |
168 | This one's a little trickier. We have to calculate the complete list of films first before sorting them with `sortWith`. Precedence rules require us to wrap the whole `for / yield` expression in parentheses to achieve this in one expression:
169 |
170 | ```tut:book:silent
171 | (for {
172 | director <- directors
173 | film <- director.films
174 | } yield film).sortWith((a, b) => a.imdbRating > b.imdbRating)
175 | ```
176 |
177 | Many developers prefer to use a temporary variable to make this code tidier:
178 |
179 | ```tut:book:silent
180 | val films = for {
181 | director <- directors
182 | film <- director.films
183 | } yield film
184 |
185 | films sortWith { (a, b) =>
186 | a.imdbRating > b.imdbRating
187 | }
188 | ```
189 |
190 |
191 | *Tonight's Listings*
192 |
193 | Print the following for every film: `"Tonight only! FILM NAME by DIRECTOR!"`
194 |
195 |
196 | We can drop the `yield` keyword from the `for` expression to achieve `foreach`-like semantics:
197 |
198 | ```tut:book:silent
199 | for {
200 | director <- directors
201 | film <- director.films
202 | } println(s"Tonight! ${film.name} by ${director.name}!")
203 | ```
204 |
205 |
--------------------------------------------------------------------------------
/src/pages/collections/index.md:
--------------------------------------------------------------------------------
1 | # Collections
2 |
3 | We hardly need to state how important collection classes are. The Collections API was one of the most significant additions to Java, and Scala's collections framework, completely revised and updated in 2.8, is an equally important addition to Scala.
4 |
5 | In this section we're going to look at three key datastructures in Scala's collection library: *sequences, options, and maps*.
6 |
7 | We will start with *sequences*. We begin with basic operations on sequences, and then briefly examine the distinction Scala makes between interface and implementation, and mutable and immutable sequences. We then explore in depth the methods Scala provides to transform sequences.
8 |
9 | After covering the main collection types we turn to *for comprehensions*, a syntax that allows convenient specification of operations on collections.
10 |
11 | With for comprehensions under our belt we will move onto *options*, which are used frequently in the APIs for sequences and maps. Options provide a means to sequence computations and are an essential companion to for comprehensions.
12 |
13 | We'll then look at *monads*, which we have introduced before, and see how they work with for comprehensions.
14 |
15 | Next we will cover the other main collection classes: *maps and sets*. We will discover that they share a great deal in common with sequences, so most of our knowledge transfers directly.
16 |
17 | We finish with discussion of *ranges*, which can represent large sequences of integers without storing every intermediate value in memory.
18 |
19 | In the previous two chapters we have been focused on Scala concepts. The focus in this chapter is not on fundamental concepts, but on gaining practice with an important API and reinforcing concepts we have previously seen.
20 |
--------------------------------------------------------------------------------
/src/pages/collections/meeting-monads.md:
--------------------------------------------------------------------------------
1 | ## Monads
2 |
3 | We've seen that by implementing a few methods (`map`, `flatMap`, and optionally `filter` and `foreach`), we can use any class with a *for comprehension*. In the previous chapter we learned that such a class is called a *monad*. Here we are going to look in a bit more depth at monads.
4 |
5 | ### What's in a Monad?
6 |
7 | The concept of a monad is notoriously difficult to explain because it is so general. We can get a good intuitive understanding by comparing some of the types of monad that we will deal with on a regular basis.
8 |
9 | Broadly speaking, a monad is a generic type that allows us to sequence computations while abstracting away some technicality. We do the sequencing using *for comprehensions*, worrying only about the programming logic we care about. The code hidden in the monad's `map` and `flatMap` methods does all of the plumbing for us. For example:
10 |
11 | - `Option` is a monad that allows us to sequence computations on optional values without worrying about the fact that they may or may not be present;
12 |
13 | - `Seq` is a monad that allows us to sequence computations that return multiple possible answers without worrying about the fact that there are lots of possible combinations involved;
14 |
15 | - `Future` is another popular monad that allows us to sequence asynchronous computations without worrying about the fact that they are asynchronous.
16 |
17 | To demonstrate the generality of this principle, here are some examples. This first example calculates the sum of two numbers that may or may not be there:
18 |
19 | ```tut:book:invisible
20 | def getFirstNumber: Option[Int] = Some(2)
21 | def getSecondNumber: Option[Int] = Some(5)
22 | def getFirstNumbers: Seq[Int] = Seq(2, 3)
23 | def getSecondNumbers: Seq[Int] = Seq(5, 6)
24 | ```
25 |
26 | ```tut:book:silent
27 | for {
28 | a <- getFirstNumber // getFirstNumber returns Option[Int]
29 | b <- getSecondNumber // getSecondNumber returns Option[Int]
30 | } yield a + b
31 |
32 | // The final result is an Option[Int]---the result of
33 | // applying `+` to `a` and `b` if both values are present
34 | ```
35 |
36 | This second example calculate the sums of all possible pairs of numbers from two sequences:
37 |
38 | ```tut:book:silent
39 | for {
40 | a <- getFirstNumbers // getFirstNumbers returns Seq[Int]
41 | b <- getSecondNumbers // getSecondNumbers returns Seq[Int]
42 | } yield a + b
43 |
44 | // The final result is a Seq[Int]---the results of
45 | // applying `+` to all combinations of `a` and `b`
46 | ```
47 |
48 | This third example asynchronously calculates the sum of two numbers that can only be obtained asynchronously (all without blocking):
49 |
50 | ```tut:book:silent
51 | for {
52 | a <- getFirstNumber // getFirstNumber returns Future[Int]
53 | b <- getSecondNumber // getSecondNumber returns Future[Int]
54 | } yield a + b
55 |
56 | // The final result is a Future[Int]---a data structure
57 | // that will eventually allow us to access the result of
58 | // applying `+` to `a` and `b`
59 | ```
60 |
61 | The important point here is that, if we ignore the comments, *these three examples look identical*. Monads allow us to forget about one part of the problem at hand---optional values, multiple values, or asynchronously available values---and focus on just the part we care about---adding two numbers together.
62 |
63 | There are many other monads that can be used to simplify problems in different circumstances. You may come across some of them in your future use of Scala. In this course we will concentrate entirely on `Seq` and `Option`.
64 |
65 | ### Exercises
66 |
67 | #### Adding All the Things ++
68 |
69 | We've already seen how we can use a for comprehension to neatly add together three optional values. Let's extend this to other monads. Use the following definitions:
70 |
71 | ```tut:book:silent
72 | import scala.util.Try
73 |
74 | val opt1 = Some(1)
75 | val opt2 = Some(2)
76 | val opt3 = Some(3)
77 |
78 | val seq1 = Seq(1)
79 | val seq2 = Seq(2)
80 | val seq3 = Seq(3)
81 |
82 | val try1 = Try(1)
83 | val try2 = Try(2)
84 | val try3 = Try(3)
85 | ```
86 |
87 | Add together all the options to create a new option. Add together all the sequences to create a new sequence. Add together all the trys to create a new try. Use a for comprehension for each. It shouldn't take you long!
88 |
89 |
90 | ```tut:book:silent
91 | for {
92 | x <- opt1
93 | y <- opt2
94 | z <- opt3
95 | } yield x + y + z
96 |
97 | for {
98 | x <- seq1
99 | y <- seq2
100 | z <- seq3
101 | } yield x + y + z
102 |
103 | for {
104 | x <- try1
105 | y <- try2
106 | z <- try3
107 | } yield x + y + z
108 | ```
109 |
110 | How's that for a cut-and-paste job?
111 |
112 |
--------------------------------------------------------------------------------
/src/pages/collections/random-data.scala:
--------------------------------------------------------------------------------
1 | val subjects = List("Noel", "The cat", "The dog")
2 | val verbs = List("wrote", "chased", "slept on")
3 | val objects = List("the book", "the ball", "the bed")
4 |
5 | def allSentences: List[(String, String, String)] =
6 | for {
7 | subject <- subjects
8 | verb <- verbs
9 | obj <- objects
10 | } yield (subject, verb, obj)
11 |
12 | def verbsFor(subject: String): List[String] =
13 | subject match {
14 | case "Noel" => List("wrote", "chased", "slept on")
15 | case "The cat" => List("meowed at", "chased", "slept on")
16 | case "The dog" => List("barked at", "chased", "slept on")
17 | }
18 |
19 | def objectsFor(verb: String): List[String] =
20 | verb match {
21 | case "wrote" => List("the book", "the letter", "the code")
22 | case "chased" => List("the ball", "the dog", "the cat")
23 | case "slept on" => List("the bed", "the mat", "the train")
24 | case "meowed at" => List("Noel", "the door", "the food cupboard")
25 | case "barked at" => List("the postman", "the car", "the cat")
26 | }
27 |
28 | def allSentencesConditional: List[(String, String, String)] =
29 | for {
30 | subject <- subjects
31 | verb <- verbsFor(subject)
32 | obj <- objectsFor(verb)
33 | } yield (subject, verb, obj)
34 |
35 | final case class Distribution[A](events: List[(A, Double)]) {
36 | def map[B](f: A => B): Distribution[B] =
37 | Distribution(events map { case (a, p) => f(a) -> p })
38 |
39 | def flatMap[B](f: A => Distribution[B]): Distribution[B] =
40 | Distribution(events flatMap { case (a, p1) =>
41 | f(a).events map { case (b, p2) => b -> (p1 * p2) }
42 | }).compact.normalize
43 |
44 | def normalize: Distribution[A] = {
45 | val totalWeight = (events map { case (a, p) => p }).sum
46 | Distribution(events map { case (a,p) => a -> (p / totalWeight) })
47 | }
48 |
49 | def compact: Distribution[A] = {
50 | val distinct = (events map { case (a, p) => a }).distinct
51 | def prob(a: A): Double =
52 | (events filter { case (x, p) => x == a } map { case (a, p) => p }).sum
53 |
54 | Distribution(distinct map { a => a -> prob(a) })
55 | }
56 | }
57 | object Distribution {
58 | def uniform[A](atoms: List[A]): Distribution[A] = {
59 | val p = 1.0 / atoms.length
60 | Distribution(atoms.map(a => a -> p))
61 | }
62 | def discrete[A](events: List[(A,Double)]): Distribution[A] =
63 | Distribution(events).compact.normalize
64 | }
65 |
66 | sealed trait Coin
67 | case object Heads extends Coin
68 | case object Tails extends Coin
69 |
70 | val fairCoin: Distribution[Coin] = Distribution.uniform(List(Heads, Tails))
71 | val threeFlips =
72 | for {
73 | c1 <- fairCoin
74 | c2 <- fairCoin
75 | c3 <- fairCoin
76 | } yield (c1, c2, c3)
77 |
78 | // We assume cooked food makes delicious smells with probability 1.0, and raw
79 | // food makes no smell with probability 0.0.
80 | sealed trait Food
81 | case object Raw extends Food
82 | case object Cooked extends Food
83 |
84 | val food: Distribution[Food] =
85 | Distribution.discrete(List(Cooked -> 0.3, Raw -> 0.7))
86 |
87 | sealed trait Cat
88 | case object Asleep extends Cat
89 | case object Harassing extends Cat
90 |
91 | def cat(food: Food): Distribution[Cat] =
92 | food match {
93 | case Cooked => Distribution.discrete(List(Harassing -> 0.8, Asleep -> 0.2))
94 | case Raw => Distribution.discrete(List(Harassing -> 0.4, Asleep -> 0.6))
95 | }
96 |
97 | val foodModel: Distribution[(Food, Cat)] =
98 | for {
99 | f <- food
100 | c <- cat(f)
101 | } yield (f, c)
102 |
103 | // Probability the cat is harassing me
104 | val pHarassing: Double =
105 | foodModel.events filter {
106 | case ((_, Harassing), _) => true
107 | case ((_, Asleep), _) => false
108 | } map { case (a, p) => p } sum
109 |
110 | // Probability the food is cooked given the cat is harassing me
111 | val pCookedGivenHarassing: Option[Double] =
112 | foodModel.events collectFirst[Double] {
113 | case ((Cooked, Harassing), p) => p
114 | } map (_ / pHarassing)
115 |
--------------------------------------------------------------------------------
/src/pages/collections/ranges.md:
--------------------------------------------------------------------------------
1 | ## Ranges
2 |
3 | So far we've seen lots of ways to iterate over sequences but not much in the way of iterating over numbers. In Java and other languages it is common to write code like
4 |
5 | ```java
6 | for(i = 0; i < array.length; i++) {
7 | doSomething(array[i])
8 | }
9 | ```
10 |
11 | We've seen that for comprehensions provide a succinct way of implementing these programs. But what about classics like this?
12 |
13 | ```java
14 | for(i = 99; i > 0; i--) {
15 | System.out.println(i + "bottles of beer on the wall!")
16 | // Full text omitted for the sake of brevity
17 | }
18 | ```
19 |
20 | Scala provides the `Range` class for these occasions. A `Range` represents a sequence of integers from some starting value to less than the end value with a non-zero step. We can construct a `Range` using the `until` method on `Int`.
21 |
22 | ```tut:book
23 | 1 until 10
24 | ```
25 |
26 | By default the step size is 1, so trying to go from high to low gives us an empty `Range`.
27 |
28 | ```tut:book
29 | 10 until 1
30 | ```
31 |
32 | We can rectify this by specifying a different step, using the `by` method on `Range`.
33 |
34 | ```tut:book
35 | 10 until 1 by -1
36 | ```
37 |
38 | Now we can write the Scala equivalent of our Java program.
39 |
40 | ```tut:book:silent
41 | for(i <- 99 until 0 by -1) println(i + " bottles of beer on the wall!")
42 | // 99 bottles of beer on the wall!
43 | // 98 bottles of beer on the wall!
44 | // 97 bottles of beer on the wall!
45 | ```
46 |
47 | This gives us a hint of the power of ranges. Since they are sequences we can combine them with other sequences in interesting ways. For example, to create a range with a gap in the middle we can concatenate two ranges:
48 |
49 | ```tut:book
50 | (1 until 10) ++ (20 until 30)
51 | ```
52 |
53 | Note that the result is a `Vector` not a `Range` but this doesn't matter. As they are both sequences we can use both them in a for comprehension without any code change!
54 |
--------------------------------------------------------------------------------
/src/pages/conclusions.md:
--------------------------------------------------------------------------------
1 | # Conclusions
2 |
3 | This completes Essential Scala. To recap our journey, we have learned Scala via the major patterns of usage:
4 |
5 | - algebraic data types and structural recursion;
6 | - sequencing computations using `map`, `flatMap`, and `fold`; and
7 | - type classes.
8 |
9 | These are the patterns we use daily in our Scala coding, which we have found work well across many Scala projects, and they make up by the far the majority of our Scala code. They will serve you well.
10 |
11 | We have tried to emphasise that if you can model the problem correctly the code follows in an almost mechanical way. Learning how to think in the Scala way (or, more broadly, in a functional way) is by far the most important lesson of this book.
12 |
13 | We have introduced language features as they support the patterns. In the appendices you will find additional material covering some inessential functionality we have skipped over in the main text. Scala has a few other features, such as self types, that we have found so little use for in our years of programming Scala that we have omitted them entirely in this introductory text.
14 |
15 | ## What Now?
16 |
17 | The journey to mastering Scala has not finished with this book. You will benefit greatly from active participation in the Scala community. We have setup an [online chat room](http://gitter.im/underscoreio/scala) for discussion of all Scala related matters. Any and all Scala related questions are welcome there. There are many other forums, conferences, and user groups where you can find an enthusiastic and welcoming community of fellow programmers.
18 |
19 | If you have enjoyed Essential Scala we hope you'll consider our followup book [Advanced Scala](http://underscore.io/training/courses/advanced-scala). As the name suggests, it covers more advanced concepts with an emphasis on patterns for larger programs.
20 |
21 | Finally, we would love hear your thoughts on Essential Scala. Any feedback---good or bad---helps to improve the book. We can be reached at [hello@underscore.io](mailto:hello@underscore.io). Any improvements we make to Essential Scala will of course be made available to every reader as part of our policy of free lifetime updates.
22 |
23 | Thank you for reading Essential Scala, and we hope you future coding in Scala is productive and fun.
24 |
--------------------------------------------------------------------------------
/src/pages/dsl/control.md:
--------------------------------------------------------------------------------
1 | ## Custom Control Structures
2 |
3 | Consider implementing logging. Often we'll have a log statement like `logger.debug(anExpensiveOperation())` which we only want to execute if debug logging is enabled. In Java, we'd have to write something like
4 |
5 | ```java
6 | if(logger.isDebugEnabled() {
7 | logger.debug(anExpensiveOperation());
8 | }
9 | ```
10 |
11 | the reason being that we can't control the order of evaluation---a function's arguments are always evaluated before the function is called. Scala allows us to delay evaluating a functions arguments, a feature known as call-by-name parameters.
12 |
13 | ### Syntax
14 |
15 | We declare a call by name parameter by specifying it's type as `=> Result`. That is, like a function but without a parameter list. For example, here's a simple logger implementation using call-by-name parameters
16 |
17 | ```scala
18 | object Logger {
19 | var debugEnabled = true
20 |
21 | def debug(msg: => String): Unit =
22 | if(debugEnabled)
23 | println("DEBUG "+ msg)
24 | else
25 | ()
26 | }
27 |
28 | scala> Logger.debug("This is a debug message")
29 | DEBUG This is a debug message
30 |
31 | scala> Logger.debugEnabled = false
32 | Logger.debugEnabled: Boolean = false
33 |
34 | scala> Logger.debug("This is a debug message")
35 |
36 | ```
37 |
38 | We can prove that the `msg` parameter is not being evaluated by wrapping a `println` expression in with it.
39 |
40 | ```scala
41 | scala> Logger.debug({ println("Is this thing on?"); "This is a debug message" })
42 |
43 | scala> Logger.debugEnabled = true
44 | Logger.debugEnabled: Boolean = true
45 |
46 | scala> Logger.debug({ println("Is this thing on?"); "This is a debug message" })
47 | Is this thing on?
48 | DEBUG This is a debug message
49 |
50 | ```
51 |
52 | Note that call-by-name parameters are evaluated every time they are invoked. If you're used to Haskell's call-by-need evaluation, where a parameter is evaluated once and the result stored for later use, this different may be trip you up.
53 |
54 | ### Exercises
55 |
56 | It's time to get our Javascript on! Write a method or function `withTimeout` that invokes a call-by-name parameter after a certain time has passed. Use `Thread.sleep` to wait. You should be able to write code like
57 |
58 | ```scala
59 | withTimeout(1000, println("It's about time!"))
60 | ```
61 |
62 | and `"It's about time"` will be printed after 1000 milliseconds.
63 |
64 |
65 | Implement a ternary operator in Scala. It should be possible to write
66 |
67 | ```scala
68 | scala> ?(1 < 2) { "Numbers still work!" } { "Oh dear!" }
69 | res13: String = Numbers still work!
70 |
71 | ```
72 |
73 | Hint: use multiple parameter lists to allow this syntax.
74 |
--------------------------------------------------------------------------------
/src/pages/dsl/index.md:
--------------------------------------------------------------------------------
1 | # Growing the Language
2 |
3 | Creating domain specific languages (DSLs) is one of the more powerful features of Scala. With great power comes great responsibility, and we must use this power wisely to avoid creating impenetrable code. In this section we look at how Scala facilitates DSLs and discuss how best to use these features.
4 |
--------------------------------------------------------------------------------
/src/pages/dsl/macros.md:
--------------------------------------------------------------------------------
1 | ## Syntax Extensions
2 |
3 | From 2.10 onwards Scala supports a flexible syntax extension method called [macros](http://scalamacros.org/). We're not going to cover them here, but be aware of their existence if you want to ultimately excel in DSL flexibility!
4 |
--------------------------------------------------------------------------------
/src/pages/dsl/operators.md:
--------------------------------------------------------------------------------
1 | ## Operators
2 |
3 | Recall that is Scala we can use any method as an operator. In particular
4 |
5 | ```scala
6 | a b c
7 | ```
8 |
9 | is equivalent to
10 |
11 | ```scala
12 | a.b(c)
13 | ```
14 |
15 | This simple feature allows us to create a more natural notation in many domains. A straightforward example is representing mathematical notation for extended numeric types. Here we implement a simple vector type with element-wise addition and multiplication.
16 |
17 | ```scala
18 | class Vector(val data: Array[Double]) {
19 | def +(other: Vector):Vector =
20 | new Vector(
21 | data.zip(other.data).map {
22 | case (v1, v2) => v1 + v2
23 | }
24 | )
25 |
26 | def *(other: Vector): Vector =
27 | new Vector(
28 | data.zip(other.data).map {
29 | case (v1, v2) => v1 * v2
30 | }
31 | )
32 | }
33 |
34 | object Vector {
35 | def apply(xs: Double*): Vector = new Vector(xs.toArray)
36 | }
37 |
38 | scala> val vector = Vector(1.0, 2.0) + Vector(3.0, 4.0)
39 | vector: Vector = Vector@58bead4f
40 |
41 | scala> vector.data
42 | res6: Array[Double] = Array(4.0, 6.0)
43 | ```
44 |
45 | As a bonus we also defined a companion object with an `apply` constructor. This shows how we use variable arguments in Scala.
46 |
47 | ### Exercise
48 |
49 | Add an element-wise exponentiation operator `**` to `Vector`, which raises each element to the given power. Use `scala.math.pow` for the per-element computation. Return a `Vector` with the results.
50 |
51 | ## Special Methods
52 |
53 | Scala makes heavy use of convention in method names to reduce boilerplate syntax. We've already seen some examples of this:
54 |
55 | - Use of single-argument methods as infix operators
56 | - argumentless methods for field accessor syntax
57 | - `apply` for function application syntax `foo(...)`
58 | - `update` for function application update `foo(...) = bar`
59 | - `unapply` for extending pattern matching
60 |
61 | There are some more special method names that we either haven't yet seen or have only mentioned in passing:
62 |
63 | - `foo_=` methods for assignment syntax
64 | - `unary_foo` methods where `foo` can be `+`, `-`, `~`, or `!`.
65 |
66 |
67 | ### Custom Setters
68 |
69 | The first allows us to write custom setters. For example, imagine a bank account class where we want to log to an audit trail whenever we change the balance. We can implement this as follows:
70 |
71 | ```scala
72 | object Account {
73 | private var _balance = 0
74 | def balance = _balance
75 | def balance_=(newBalance: Int):Unit = {
76 | println("AUDIT TRAIL: Changed balance to "+newBalance)
77 | _balance = newBalance
78 | }
79 | }
80 |
81 | scala> Account.balance
82 | res0: Int = 0
83 |
84 | scala> Account.balance = 100
85 | AUDIT TRAIL: Changed balance to 100
86 | Account.balance: Int = 100
87 |
88 | scala> Account.balance
89 | res1: Int = 100
90 | ```
91 |
92 | Note how we can write `Account.balance = 100` and the method `balance_=` is called. These setter methods can only be used in tandem with a getter. If a getter is not defined we get a runtime error.
93 |
94 | ```scala
95 | object NoGetter {
96 | private var _balance = 0
97 | def balance_=(newBalance: Int):Unit = {
98 | println("AUDIT TRAIL: Changed balance to "+newBalance)
99 | _balance = newBalance
100 | }
101 | }
102 |
103 | scala> NoGetter.balance = 100
104 | :11: error: value balance is not a member of object NoGetter
105 | val $ires1 = NoGetter.balance
106 | ^
107 | :8: error: value balance is not a member of object NoGetter
108 | NoGetter.balance = 100
109 | ^
110 | ```
111 |
112 | Note that whenever we write a `val` under the covers it is implemented use a getter and setter.
113 |
114 |
115 | ### Unary Methods
116 |
117 | The unary methods allow us to define prefix operators that can be used with our objects. We can only define `+`, `-`, `~`, or `!`. Let's add a `-` prefix operator to our Vector:
118 |
119 | ```scala
120 | class Vector(val data: Array[Double]) {
121 | def +(other: Vector):Vector =
122 | new Vector(
123 | data.zip(other.data).map {
124 | case (v1, v2) => v1 + v2
125 | }
126 | )
127 |
128 | def *(other: Vector): Vector =
129 | new Vector(
130 | data.zip(other.data).map {
131 | case (v1, v2) => v1 * v2
132 | }
133 | )
134 | def unary_- : Vector =
135 | new Vector(
136 | data.map (elt => -elt)
137 | )
138 | }
139 |
140 | object Vector {
141 | def apply(xs: Double*): Vector = new Vector(xs.toArray)
142 | }
143 |
144 | scala> Vector(1.0, 2.0, 3.0).unary_-.data
145 | res6: Array[Double] = Array(-1.0, -2.0, -3.0)
146 |
147 | scala> (- Vector(1.0, 2.0, 3.0)).data
148 | res7: Array[Double] = Array(-1.0, -2.0, -3.0)
149 | ```
150 |
151 |
152 | ### Exercise
153 |
154 | Extend our `Vector` implementation to support
155 |
156 | - an `apply` method providing element access.
157 | - an `update` method supporting element mutation.
158 |
159 |
160 | ## Operator Associativity
161 |
162 | Operator associativity is the final wrinkle in Scala's support for operators. Any methods that ends with a colon (`:`) is right-associative. That means `a op: b` is equivalent to `b.op:(a)` rather than `a.op:(b)`. We've seen this used extensively in the collection classes. For example, `+:` is the prepend operation on `Seq`.
163 |
164 | ## Final Words
165 |
166 | Operator syntax, and particularly symbolic operators, are a powerful way of creating a concise syntax to express domain specific operations. However it can also be a great way to obscure your code. When creating symbolic operators you must consider the costs and benefits. Generally, if an operator has a well known meaning (e.g. the arthimetic operators) or is commonly known within a specific domain *and* dealing with the domain is a major part of the program you're writing then symbolic operators can be a good approach. In other cases caution is advised.
167 |
--------------------------------------------------------------------------------
/src/pages/fp/ho-functions.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Higher-Order Functions
4 | ---
5 |
6 | A **higher-order function** is a function that accepts another function. We have seen several uses already, particularly with collections. Here we're going to use higher-order functions in some novel situations, with the aim of solidifying our understanding. This section will be heavy on the examples.
7 |
8 | We're going to spend a lot of time with Scala's `List` type, so a quick overview:
9 |
10 | * A `List[A]` is either `Nil` (the empty list) or a pair `::(a: A, as: List[A])`
11 | * You can write the pair constructor using infix syntax `a :: as`.
12 | * You can pattern match on the two cases as they are both case classes.
13 |
14 | A `List` is built recursively. For example, given the empty list we can prepend an element like so:
15 |
16 | ```scala
17 | scala> 1 :: Nil
18 | res17: List[Int] = List(1)
19 | ```
20 |
21 | We can build longer lists in the same way.
22 |
23 | ```scala
24 | scala> 1 :: (2 :: Nil)
25 | res19: List[Int] = List(1, 2)
26 | ```
27 |
28 | As `::` is right associative we can drop the brackets.
29 |
30 | ```scala
31 | scala> 1 :: 2 :: Nil
32 | res20: List[Int] = List(1, 2)
33 | ```
34 |
35 | ## Spinning Wheels
36 |
37 | Write a method that copies a `List`.
38 |
39 | Hint: A `List` is `::` or `Nil`. Remember how we learned to deal with sum types?
40 |
41 |
42 | ```scala
43 | def copy(in: List[Int]): List[Int] =
44 | in match {
45 | case Nil => Nil
46 | case (x :: xs) => x :: copy(xs)
47 | }
48 | ```
49 |
50 |
51 | This is a simple function that illustrates how the shape of the code follows the shape of the data. A list has two cases, and so does our method. Notice how the individual cases follow the shape of the data. `Nil` goes to `Nil`. The pair (`::`) goes to a pair with a recursive call to `copy`.
52 |
53 | ## A Map of the Territory
54 |
55 | Write a method to add one to all the elements of `List[Int]`. Don't use any methods defined on `List` to do so.
56 |
57 |
66 |
67 | Alter your method to accept any `Int => Int` function.
68 |
69 |
70 | ```scala
71 | def map(f: Int => Int, in: List[Int]): List[Int] =
72 | in match {
73 | case Nil => Nil
74 | case (x :: xs) => f(x) :: map(f, xs)
75 | }
76 | ```
77 |
78 |
79 | Make your function generic in the type of the `List`.
80 |
81 |
82 | ```scala
83 | def map[A](f: A => A, in: List[A]): List[A] =
84 | in match {
85 | case Nil => Nil
86 | case (x :: xs) => f(x) :: map(f, xs)
87 | }
88 | ```
89 |
90 |
91 | There are a few concepts in this example. The first is the idea of the passing functions as reusable computations. We've already seen in the section on collections the benefits this brings.
92 |
93 | More interesting is the similarity of this code to the code for `copy` above. Notice once again the pattern: two cases to match the two cases of `List`, and the computation follows the shape of the data.
94 |
95 | ## Folding Up the Map
96 |
97 | Write a method to sum up the elements of `List[Int]`. Don't use any methods defined on `List` to do so.
98 |
99 |
100 | ```scala
101 | def sum(in: List[Int]): Int =
102 | in match {
103 | case Nil => 0
104 | case (x :: xs) => x + sum(xs)
105 | }
106 | ```
107 |
108 |
109 | Now generalise your method to accept any function `(Int, Int) => Int` in place of addition. You'll need to make another change, but I'll leave you work out what that is.
110 |
111 |
112 | ```scala
113 | def accumulate(f: (Int, Int) => Int, zero: Int, in: List[Int]): Int =
114 | in match {
115 | case Nil => zero
116 | case (x :: xs) => f(x, accumulate(xs))
117 | }
118 | ```
119 |
120 |
121 | Now generalise your method to be generic over the type of `List`. What is this function conventionally called?
122 |
123 |
124 | ```scala
125 | def foldRight[A, B](f: (A, B) => B, zero: B, in: List[A]): B =
126 | in match {
127 | case Nil => zero
128 | case (x :: xs) => f(x, foldRight(f, zero, xs))
129 | }
130 | ```
131 |
132 |
133 | Notice it's the same pattern again, though slightly generalised from before!
134 |
135 |
136 | ## Calculus the Easy Way (Optional)
137 |
138 | You perhaps encountered differentiation in school. When we differentiate a function we create another function that calculates it's rate of change, called its derivative. In many cases we can do this symbolically, but we're going to do it the CS way---numerically.
139 |
140 | Implement a function `derivative` that takes a function `Double => Double` and returns the derivative (also `Double => Double`).
141 |
142 | Hint 1: We can approximate the derivative by calculating the *centered difference* `(f(x + h) - f(x - h)) / 2h` for small `h`.
143 |
144 | Hint 2: The `math` package contains several useful methods such as `abs`. Note the derivative of `math.exp(x)` is itself, and the derivative of `math.sin` is `math.cos`. You can use these properties to test your function.
145 |
146 |
155 |
156 | Choosing a fixed value of `h` is not always a good idea. Make a function `makeDerivative` that allows you to set the value of `h` and returns `derivative`.
157 |
158 |
168 |
169 | Now we can adjust `h` till we have calculated `derivative` at a point to within a given tolerance. Write a function `solve` that solves the derivative of a function at a point to within a given tolerance.
170 |
171 |
186 |
--------------------------------------------------------------------------------
/src/pages/fp/index.md:
--------------------------------------------------------------------------------
1 | # Functional Programming
2 |
3 |
4 | In section we pull together some of the main themes of the course so far:
5 |
6 | - modelling data with sum and product types; and
7 | - sequencing computations with `map`, `flatMap`, and `fold`.
8 |
9 | By looking briefly at the theoretical underpinning of structural recursion we will see a deep relationship between these concepts. We'll then gain some more experience implementing our own classes with higher-order functions.
10 |
--------------------------------------------------------------------------------
/src/pages/fp/modelling-errors.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Modelling Errors
4 | ---
5 |
6 | In this previous section we saw how we can use `Option` (or `Maybe` as we called it) to handle errors by returning the empty case. This unfortunately throws away useful information, namely the cause of the error. It would be nice to preserve this information so we can take appropriate action.
7 |
8 | Exceptions are the way we handle errors in Java. However, exceptions suffer from the same problem as `null`: there is nothing in the type system that will fail if we don't properly handle exceptions[^concurrency]. Scala doesn't have checked exceptions like Java. While it does have unchecked exceptions we would like a more functional way of modelling errors using types.
9 |
10 | [^concurrency]: Exceptions are also a bad idea in concurrent systems where we don't have a single stack to capture the scope of the error.
11 |
12 | ## Try and Try Again
13 |
14 | `Throwable` is the supertype of all the exceptions we should be catching. Implement a type `Try` that represents a successful computation or a `Throwable` on error.
15 |
16 |
17 | Tip: Although we're using `Throwable` we aren't actually `throwing` any exceptions in this exercise. We are simply using the `Throwable` type to represent an error, wrapping them in an instance of `Try` to return them when an error happens.
18 |
19 |
20 |
21 | ```scala
22 | sealed trait Try[+A]
23 | final case class Success[A](elt: A) extends Try[A]
24 | final case class Failure[A](error: Throwable) extends Try[A]
25 | ```
26 |
27 | This is just like `Maybe` except we store a `Throwable` in the failure case. Using a `Throwable` as an error type has the advantage that we can print the location of the error using `throwable.printStackTrace`:
28 |
29 | ```scala
30 | scala> val result: Try[Int] = Failure(new Exception("Boom!"))
31 | result: Try[Int] = Failure(java.lang.Exception: Boom!)
32 |
33 | scala> result match {
34 | | case Success(elt) => println(elt)
35 | | case Failure(error) => error.printStackTrace
36 | | }
37 | java.lang.Exception: Boom!
38 | // and so on...
39 | ```
40 |
41 |
42 | ### Exercise: Using Try
43 |
44 | In the previous section we saw that `fold`, `map`, and `flatMap` were useful mehtods on `Maybe`. There are also useful methods on `Try`. Let's implement them.
45 |
46 |
47 | ```scala
48 | sealed trait Try[+A] {
49 | def fold[B](failure: Throwable => B, success: A => B): B
50 |
51 | def map[B](f: A => B): Try[B] =
52 | flatMap[B](e => Success(f(e)))
53 |
54 | def flatMap[B](f: A => Try[B]): Try[B] =
55 | fold[Try[B]](failure = t => Failure(t), success = e => f(e))
56 |
57 | def foreach(f: A => Unit): Unit = {
58 | map(f)
59 | ()
60 | }
61 | }
62 |
63 | final case class Failure[A](error: Throwable) extends Try[A] {
64 | def fold[B](failure: Throwable => B, success: A => B): B =
65 | failure(error)
66 | }
67 |
68 | final case class Success[A](elt: A) extends Try[A] {
69 | def fold[B](failure: Throwable => B, success: A => B): B =
70 | success(elt)
71 | }
72 | ```
73 |
74 | In this case I've implemented the auxillary functions in terms of `fold`, which saves on code duplication.
75 |
76 |
77 | ## Try in Scala
78 |
79 | Scala (2.9.3+) comes with `scala.util.Try`, which works the same as our `Try` with the exception that, like `Option`, it lacks a `fold` method and instead uses `map` and `getOrElse`.
80 |
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-completed-worksheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-completed-worksheet.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-empty-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-empty-project.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-empty-worksheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-empty-worksheet.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-empty-workspace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-empty-workspace.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-hello-world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-hello-world.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-new-object.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-new-object.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-new-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-new-project.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-new-worksheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-new-worksheet.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-single-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-single-file.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-website.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-website.png
--------------------------------------------------------------------------------
/src/pages/getting-started/scala-ide-workspace-chooser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/getting-started/scala-ide-workspace-chooser.png
--------------------------------------------------------------------------------
/src/pages/implicits/conversions.md:
--------------------------------------------------------------------------------
1 | ## Implicit Conversions
2 |
3 | So far we have seen two programming patterns using implicits: *type enrichment*, which we implement using *implicit classes*, and *type classes*, which we implement using *implicit values and parameter lists*.
4 |
5 | Scala has a third implicit mechanism called *implicit conversions* that we will cover here for completeness. Implicit conversions can be seen as a more general form of implicit classes, and can be used in a wider variety of contexts.
6 |
7 |
8 | #### The Dangers of Implicit Conversions {-}
9 |
10 | As we shall see later in this section, undisciplined use of implicit conversions can cause as many problems as it fixes for the beginning programmer. Scala even requires us to write a special import statement to silence compiler warnings resulting from the use of implicit conversions:
11 |
12 | ```tut:book:silent
13 | import scala.language.implicitConversions
14 | ```
15 |
16 | We recommend using implicit classes and implicit values/parameters over implicit conversions wherever possible. By sticking to the type enrichment and type class design patterns you should find very little cause to use implicit conversions in your code.
17 |
18 | You have been warned!
19 |
20 |
21 | ### Implicit conversions
22 |
23 | Implicit conversions are a more general form of implicit classes. We can tag any single-argument method with the `implicit` keyword to allow the compiler to implicitly use the method to perform automated conversions from one type to another:
24 |
25 | ```tut:book:silent
26 | class B {
27 | def bar = "This is the best method ever!"
28 | }
29 |
30 | class A
31 |
32 | implicit def aToB(in: A): B = new B()
33 | ```
34 |
35 | ```tut:book
36 | new A().bar
37 | ```
38 |
39 | Implicit classes are actually just syntactic sugar for the combination of a regular class and an implicit conversion. With an implicit class we have to define a new type as a target for the conversion; with an implicit method we can convert from any type to any other type as long as an implicit is available in scope.
40 |
41 | ### Designing with Implicit Conversions
42 |
43 | The power of implicit conversions tends to cause problems for newer Scala developers. We can easily define very general type conversions that play strange games with the semantics of our programs:
44 |
45 | ```tut:book:silent
46 | implicit def intToBoolean(int: Int) = int == 0
47 | ```
48 |
49 | ```tut:book
50 | if(1) "yes" else "no"
51 |
52 | if(0) "yes" else "no"
53 | ```
54 |
55 | This example is ridiculous, but it demonstrates the potential problems implicits can cause. `intToBoolean` could be defined in a library in a completely different part of our codebase, so how would we debug the bizarre behaviour of the `if` expressions above?
56 |
57 | Here are some tips for designing using implicits that will prevent situations like the one above:
58 |
59 | - Wherever possible, stick to the type enrichment and type class programming patterns.
60 |
61 | - Wherever possible, use implicit classes, values, and parameter lists over implicit conversions.
62 |
63 | - Package implicits clearly, and bring them into scope only where you need them. We recommend using the packaging guidelines introduced earlier this chapter.
64 |
65 | - Avoid creating implicit conversions that convert from one general type to another general type---the more specific your types are, the less likely the implicit is to be applied incorrectly.
66 |
67 | ### Exercises
68 |
69 | #### Implicit Class Conversion
70 |
71 | Any implicit class can be reimplemented as a class paired with an implicit method. Re-implement the `IntOps` class from the *type enrichment* section in this way. Verify that the class still works the same way as it did before.
72 |
73 |
74 | Here is the solution. The methods `yeah` and `times` are exactly as we implemented them previously. The only differences are the removal of the `implicit` keyword on the `class` and the addition of the `implicit def` to do the job of the implicit constructor:
75 |
76 | ```tut:book:silent
77 | object IntImplicits {
78 | class IntOps(n: Int) {
79 | def yeah() =
80 | times(_ => println("Oh yeah!"))
81 |
82 | def times(func: Int => Unit) =
83 | for(i <- 0 until n) func(i)
84 | }
85 |
86 | implicit def intToIntOps(value: Int) =
87 | new IntOps(value)
88 | }
89 | ```
90 |
91 | The code still works the same way it did previously. The implicit conversion is not available until we bring it into scope:
92 |
93 | ```tut:book:fail
94 | 5.yeah()
95 | ```
96 |
97 | Once the conversion has been brought into scope, we can use `yeah` and `times` as usual:
98 |
99 | ```tut:book:silent
100 | import IntImplicits._
101 | ```
102 |
103 | ```tut:book
104 | 5.yeah()
105 | ```
106 |
107 |
--------------------------------------------------------------------------------
/src/pages/implicits/equal.scala:
--------------------------------------------------------------------------------
1 | trait Equal[A] {
2 | def equal(v1: A, v2: A): Boolean
3 | }
4 | object Equal {
5 | def apply[A](implicit instance: Equal[A]): Equal[A] =
6 | instance
7 |
8 | implicit class ToEqual[A](in: A) {
9 | def ===(other: A)(implicit equal: Equal[A]): Boolean =
10 | equal.equal(in, other)
11 | }
12 | }
13 |
14 | case class Person(name: String, email: String)
15 |
16 | object EmailEqual extends Equal[Person] {
17 | def equal(v1: Person, v2: Person): Boolean =
18 | v1.email == v2.email
19 | }
20 |
21 | object NameEmailEqual extends Equal[Person] {
22 | def equal(v1: Person, v2: Person): Boolean =
23 | v1.email == v2.email && v1.name == v2.name
24 | }
25 |
26 | object Eq {
27 | def apply[A](v1: A, v2: A)(implicit equal: Equal[A]): Boolean =
28 | equal.equal(v1, v2)
29 | }
30 |
31 | object NameAndEmailImplicit {
32 | implicit object NameEmailEqual extends Equal[Person] {
33 | def equal(v1: Person, v2: Person): Boolean =
34 | v1.email == v2.email && v1.name == v2.name
35 | }
36 | }
37 |
38 | object EmailImplicit {
39 | implicit object EmailEqual extends Equal[Person] {
40 | def equal(v1: Person, v2: Person): Boolean =
41 | v1.email == v2.email
42 | }
43 | }
44 |
45 | object Examples {
46 | def byNameAndEmail = {
47 | import NameAndEmailImplicit._
48 | Eq(Person("Noel", "noel@example.com"), Person("Noel", "noel@example.com"))
49 | }
50 |
51 | def byEmail = {
52 | import EmailImplicit._
53 | Eq(Person("Noel", "noel@example.com"), Person("Dave", "noel@example.com"))
54 | }
55 |
56 | def companionObjectInterface = {
57 | import NameAndEmailImplicit._
58 | Equal[Person].equal(Person("Noel", "noel@example.com"), Person("Noel", "noel@example.com"))
59 | }
60 | }
61 |
62 | object SyntaxExample {
63 | implicit val caseInsensitiveEquals = new Equal[String] {
64 | def equal(s1: String, s2: String) =
65 | s1.toLowerCase == s2.toLowerCase
66 | }
67 |
68 | import Equal._
69 |
70 | "foo".===("FOO")
71 | }
72 |
--------------------------------------------------------------------------------
/src/pages/implicits/htmlwriter.scala:
--------------------------------------------------------------------------------
1 | case class Person(name: String, email: String)
2 |
3 | trait HtmlWriter[T] {
4 | def write(in: T): String
5 | }
6 | object HtmlWriter {
7 | def apply[A](implicit writer: HtmlWriter[A]): HtmlWriter[A] =
8 | writer
9 | }
10 |
11 | implicit object PersonWriter extends HtmlWriter[Person] {
12 | def write(person: Person) = s"${person.name} <${person.email}>"
13 | }
14 |
15 | object HtmlUtil {
16 | def htmlify[T](data: T)(implicit writer: HtmlWriter[T]): String = {
17 | writer.write(data)
18 | }
19 | }
20 |
21 | implicit object ApproximationWriter extends HtmlWriter[Int] {
22 | def write(in: Int): String =
23 | s"It's definitely less than ${((in / 10) + 1) * 10}"
24 | }
25 |
26 | HtmlUtil.htmlify(2)
27 | HtmlWriter[Person].write(Person("Noel", "noel@example.org"))
28 |
--------------------------------------------------------------------------------
/src/pages/implicits/implicit-resolution.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Implicit Resolution
4 | ---
5 |
6 | ## Implicit Resolution Rules
7 |
8 | Scala has three types of implicits---implicit classes, implicit values, and implicit conversions. Each works in the same way---the compiler detects a type error in our code, locates a matching implicit, and applies it to fix the error. This is a powerful mechanism, but we need to control it very carefully to prevent the compiler changing our code in ways we don't expect. For this reason, there is a strict set of **implicit resolution rules** that we can use to dictate the compiler's behaviour:
9 |
10 | 1. **Explicits first rule**---if the code already type checks, the compiler ignores implicits altogether;
11 | 2. **Marking rule**---the compiler only uses definitions marked with the `implicit` keyword;
12 | 3. **Scope rule**---the compiler only uses definitions that are *in scope* at the current location in the code (see below);
13 | 4. **Non-ambiguity rule**---the compiler only applies an implicit if it is the only candidate available;
14 | 5. **One-at-a-time rule**---the compiler never chains implicits together to fix type errors---doing so would drastically increase compile times;
15 |
16 | Note that the name of the implicit doesn't come into play in this process.
17 |
18 | ### Implicit Scope
19 |
20 | The *scope rule* of implicit resolution uses a special set of scoping rules that allow us to package implicits in useful ways. These rules, collectively referred to as **implicit scope**, form a search path that the compiler uses to locate implicits:
21 |
22 | 1. **Local scope**---First look locally for any identifier that is tagged as `implicit`. This must be a single identifier (i.e. `a`, not `a.b`), and can be defined locally or in the surrounding class, object, or trait, or `imported` from elsewhere.
23 |
24 | 2. **Companion objects**---If an implicit cannot be found locally, the compiler looks in the companion objects of types involved in the type error. Will see more of this rule in the next section.
25 |
26 | ## Packaging Implicit Values
27 |
28 | Implicits **cannot be defined at the top level** (except in the Scala console). They must be wrapped in an outer trait, class, or singleton object. The typical way of packaging an implicit value is to define it inside a trait called `SomethingImplicits` and extend that trait to create a singleton of the same name:
29 |
30 | ```tut:book:silent
31 | trait VowelImplicits {
32 | implicit class VowelOps(str: String) {
33 | val vowels = Seq('a', 'e', 'i', 'o', 'u')
34 | def numberOfVowels =
35 | str.toList.filter(vowels contains _).length
36 | }
37 | }
38 |
39 | object VowelImplicits extends VowelImplicits
40 | ```
41 |
42 | This gives developers two convenient ways of using our code:
43 |
44 | 1. quickly bring our implicit into scope via the singleton object using an `import`:
45 |
46 | ```tut:book:silent
47 | // `VowelOps` is not in scope here
48 |
49 | def testMethod = {
50 | import VowelImplicits._
51 |
52 | // `VowelOps` is in scope here
53 |
54 | "the quick brown fox".numberOfVowels
55 | }
56 |
57 | // `VowelOps` is no longer in scope here
58 | ```
59 |
60 | 2. stack our trait with a set of other traits to produce a library of implicits that can be brought into scope using inheritance or an `import`:
61 |
62 | ```tut:invisible
63 | trait VowelImplicits
64 | trait MoreImplicits
65 | trait YetMoreImplicits
66 | ```
67 |
68 | ```tut:book:silent
69 | object AllTheImplicits extends VowelImplicits with MoreImplicits with YetMoreImplicits
70 |
71 | import AllTheImplicits._
72 |
73 | // `VowelOps` is in scope here
74 | // along with other implicit classes
75 | ```
76 |
77 |
78 | **Implicits tip:** Some Scala developers dislike implicits because they can be hard to debug. The reason for this is that an implicit definition at one point in our codebase can have an invisible affect on the meaning of a line of code written elsewhere.
79 |
80 | While this is a valid criticism of implicits, the solution is not to abandon them altogether but to apply strict design principles to regulate their use. Here are some tips:
81 |
82 | 1. Keep tight control over the scope of your implicits. Package them into traits and objects and only import them where you want to use them.
83 |
84 | 2. Package all your implicits in traits/objects with names ending in `Implicits`. This makes them easy to find using a global search across your codebase.
85 |
86 | 3. Only use implicits on specific types. Defining an implicit class on a general type like `Any` is more likely to cause problems than defining it on a specific type like `WebSiteVisitor`.
87 |
88 | The same resolution rules apply for implicit values as for implicit classes. If the compiler is unable to find suitable candidates for all parameters in the list, we get a compilation error.
89 |
90 | Let's redefine our adapters for `HtmlWriter` so we can bring them all into scope. Note that outside the REPL implicit values are subject to the same packaging restrictions as implicit classes---they have to be defined inside another class, object, or trait. We'll use the packaging convention we discussed in the previous section:
91 |
92 | ```tut:invisible
93 | trait HtmlWriter[A] {
94 | def write(a: A): String
95 | }
96 | case class Person(name: String, email: String)
97 | import java.util.Date
98 | ```
99 |
100 | ```tut:book:silent
101 | object wrapper {
102 | trait HtmlImplicits {
103 | implicit object PersonWriter extends HtmlWriter[Person] {
104 | def write(person: Person) =
105 | s"${person.name} <${person.email}>"
106 | }
107 |
108 | implicit object DateWriter extends HtmlWriter[Date] {
109 | def write(in: Date) = s"${in.toString}"
110 | }
111 | }
112 |
113 | object HtmlImplicits extends HtmlImplicits
114 | }; import wrapper._
115 | ```
116 |
117 | ```tut:invisible
118 | object HtmlUtil {
119 | def htmlify[A](data: A)(implicit writer: HtmlWriter[A]): String = {
120 | writer.write(data)
121 | }
122 | }
123 | ```
124 |
125 | We can now use our adapters with `htmlify`:
126 |
127 | ```tut:book
128 | import HtmlImplicits._
129 |
130 | HtmlUtil.htmlify(Person("John", "john@example.com"))
131 | ```
132 |
133 | This version of the code has much lighter syntax requirements than its predecessor. We have now assembled the complete type class pattern: `HtmlUtil` specifies our HTML rendering functionality, `HtmlWriter` and `HtmlWriters` implement the functionality as a set of adapters, and the implicit argument to `htmlify` implicitly selects the correct adapter for any given argument. However, we can take things one step further to really simplify things.
134 |
--------------------------------------------------------------------------------
/src/pages/implicits/index.md:
--------------------------------------------------------------------------------
1 | # Type Classes {#sec:type-classes}
2 |
3 | *Type classes* are a powerful feature of Scala that allow us to extend existing libraries with new functionality, without using inheritance and without having access to the original library source code. In this chapter we will learn how to use and implement type classes, using a Scala feature called *implicits*.
4 |
5 | In the section on traits we compared object oriented and functional style in terms of extensibility, using this table.
6 |
7 | +--------+-------------------------+-------------------------+
8 | | | Add new method | Add new data |
9 | +========+=========================+=========================+
10 | | *OO* | Change existing code | Existing code unchanged |
11 | +--------+-------------------------+-------------------------+
12 | | *FP* | Existing code unchanged | Change existing code |
13 | +--------+-------------------------+-------------------------+
14 |
15 |
16 | Type classes give us a third implementation technique which is more flexible than either. A type class is like a trait, defining an interface. However, with type classes we can:
17 |
18 | - plug in different implementations of an interface for a given class; and
19 | - implement an interface without modifying existing code.
20 |
21 | This means we can add new methods *or* new data without changing any existing code.
22 |
23 | It's difficult to understand these concepts without an example. We'll start this section by exploring how we can use type classes. We'll then turn to implementing them ourselves. We'll finish with a discussion of best practices.
24 |
--------------------------------------------------------------------------------
/src/pages/implicits/json.md:
--------------------------------------------------------------------------------
1 | ## JSON Serialisation
2 |
3 | In this section we have an extended example involving serializing Scala data to JSON, which is one of the classic use cases for type classes. The typical process for converting data to JSON in Scala involves two steps. First we convert our data types to an intermediate case class representation, then we serialize the intermediate representation to a string.
4 |
5 | Here is a suitable case class representation of a subset of the JSON language. We have a `sealed trait JsValue` that defines a `stringify` method, and a set of subtypes for two of the main JSON data types---objects and strings:
6 |
7 | ```tut:book:silent
8 | sealed trait JsValue {
9 | def stringify: String
10 | }
11 |
12 | final case class JsObject(values: Map[String, JsValue]) extends JsValue {
13 | def stringify = values
14 | .map { case (name, value) => "\"" + name + "\":" + value.stringify }
15 | .mkString("{", ",", "}")
16 | }
17 |
18 | final case class JsString(value: String) extends JsValue {
19 | def stringify = "\"" + value.replaceAll("\\|\"", "\\\\$1") + "\""
20 | }
21 | ```
22 |
23 | You should recognise this as the algebraic data type pattern.
24 |
25 | We can construct JSON objects and serialize them as follows:
26 |
27 | ```tut:book
28 | val obj = JsObject(Map("foo" -> JsString("a"), "bar" -> JsString("b"), "baz" -> JsString("c")))
29 |
30 | obj.stringify
31 | ```
32 |
33 | ### Convert X to JSON
34 |
35 | Let's create a type class for converting Scala data to JSON. Implement a `JsWriter` trait containing a single abstract method `write` that converts a value to a `JsValue`.
36 |
37 |
38 | The *type class* is generic in a type `A`. The `write` method converts a value of type `A` to some kind of `JsValue`.
39 |
40 | ```tut:book:silent
41 | trait JsWriter[A] {
42 | def write(value: A): JsValue
43 | }
44 | ```
45 |
46 |
47 | Now let's create the dispatch part of our type class. Write a `JsUtil` object containing a single method `toJson`. The method should accept a value of an arbitrary type `A` and convert it to JSON.
48 |
49 | Tip: your method will have to accept an implicit `JsWriter` to do the actual conversion.
50 |
51 |
103 |
104 | Given these two definitions we can implement a `JsWriter` for `Visitor` as follows. This uses a new type of pattern -- `a: B` -- which matches any value of type `B` and binds it to a variable `a`:
105 |
106 | ```tut:book:silent
107 | implicit object VisitorWriter extends JsWriter[Visitor] {
108 | def write(value: Visitor) = value match {
109 | case anon: Anonymous => JsUtil.toJson(anon)
110 | case user: User => JsUtil.toJson(user)
111 | }
112 | }
113 | ```
114 |
115 | Finally, verify that your code works by converting the following list of users to JSON:
116 |
117 | ```tut:book:silent
118 | val visitors: Seq[Visitor] = Seq(Anonymous("001", new Date), User("003", "dave@xample.com", new Date))
119 | ```
120 |
121 |
126 |
127 | ### Prettier Conversion Syntax
128 |
129 | Let's improve our JSON syntax by combining type classes and type enrichment. Convert `JsUtil` to an `implicit class` with a `toJson` method. Sample usage:
130 |
131 | ```scala
132 | Anonymous("001", new Date).toJson
133 | ```
134 |
135 |
136 | ```tut:book:silent
137 | implicit class JsUtil[A](value: A) {
138 | def toJson(implicit writer: JsWriter[A]) =
139 | writer write value
140 | }
141 | ```
142 |
143 | In the previous exercise we only defined `JsWriters` for our main case classes. With this convenient syntax, it makes sense for us to have an complete set of `JsWriters` for all the serializable types in our codebase, including `Strings` and `Dates`:
144 |
145 | ```tut:book:silent
146 | implicit object StringWriter extends JsWriter[String] {
147 | def write(value: String) = JsString(value)
148 | }
149 |
150 | implicit object DateWriter extends JsWriter[Date] {
151 | def write(value: Date) = JsString(value.toString)
152 | }
153 | ```
154 |
155 | With these definitions we can simplify our existing `JsWriters` for `Anonymous`, `User`, and `Visitor`:
156 |
157 | ```tut:invisible
158 | // I must repeat this here for some reason or the implicit resolution in the block below fails
159 | implicit class JsUtil[A](value: A) {
160 | def toJson(implicit writer: JsWriter[A]) =
161 | writer write value
162 | }
163 | ```
164 |
165 | ```tut:book:silent
166 | implicit object AnonymousWriter extends JsWriter[Anonymous] {
167 | def write(value: Anonymous) = JsObject(Map(
168 | "id" -> value.id.toJson,
169 | "createdAt" -> value.createdAt.toJson
170 | ))
171 | }
172 |
173 | implicit object UserWriter extends JsWriter[User] {
174 | def write(value: User) = JsObject(Map(
175 | "id" -> value.id.toJson,
176 | "email" -> value.email.toJson,
177 | "createdAt" -> value.createdAt.toJson
178 | ))
179 | }
180 |
181 | implicit object VisitorWriter extends JsWriter[Visitor] {
182 | def write(value: Visitor) = value match {
183 | case anon: Anonymous => anon.toJson
184 | case user: User => user.toJson
185 | }
186 | }
187 | ```
188 |
189 |
--------------------------------------------------------------------------------
/src/pages/implicits/order.scala:
--------------------------------------------------------------------------------
1 | final case class Order(units: Int, unitPrice: Double) {
2 | val totalPrice: Double = units * unitPrice
3 | }
4 |
5 | object Order {
6 | implicit val lessThanOrdering = Ordering.fromLessThan[Order]{ (x, y) =>
7 | x.totalPrice < y.totalPrice
8 | }
9 | }
10 |
11 | object OrderUnitPriceOrdering {
12 | implicit val unitPriceOrdering = Ordering.fromLessThan[Order]{ (x, y) =>
13 | x.unitPrice < y.unitPrice
14 | }
15 | }
16 |
17 | object OrderUnitsOrdering {
18 | implicit val unitsOrdering = Ordering.fromLessThan[Order]{ (x, y) =>
19 | x.units < y.units
20 | }
21 |
--------------------------------------------------------------------------------
/src/pages/implicits/ordering.scala:
--------------------------------------------------------------------------------
1 | import scala.math.Ordering
2 |
3 | val minOrdering = Ordering.fromLessThan[Int](_ < _)
4 |
5 | val maxOrdering = Ordering.fromLessThan[Int](_ > _)
6 |
7 | object ImplicitValueExample {
8 | implicit val ordering = Ordering.fromLessThan[Int](_ < _)
9 | // Not how we don't supply an ordering to `sorted`. The compiler provides it for us
10 | List(2, 4, 3).sorted
11 | List(1, 7 ,5).sorted
12 | }
13 |
14 | object AmbiguousExample {
15 | implicit val minOrdering = Ordering.fromLessThan[Int](_ < _)
16 | implicit val maxOrdering = Ordering.fromLessThan[Int](_ > _)
17 | List(3,4,5).sorted
18 | }
19 |
--------------------------------------------------------------------------------
/src/pages/implicits/rational-packaging.scala:
--------------------------------------------------------------------------------
1 | final case class Rational(numerator: Int, denominator: Int)
2 |
3 | object RationalLessThanOrdering {
4 | implicit val ordering = Ordering.fromLessThan[Rational]((x, y) =>
5 | (x.numerator.toDouble / x.denominator.toDouble) <
6 | (y.numerator.toDouble / y.denominator.toDouble)
7 | )
8 | }
9 |
10 | object RationalGreaterThanOrdering {
11 | implicit val ordering = Ordering.fromLessThan[Rational]((x, y) =>
12 | (x.numerator.toDouble / x.denominator.toDouble) >
13 | (y.numerator.toDouble / y.denominator.toDouble)
14 | )
15 | }
--------------------------------------------------------------------------------
/src/pages/implicits/rational.scala:
--------------------------------------------------------------------------------
1 | object Rational {
2 | final case class Rational(numerator: Int, denominator: Int)
3 |
4 | implicit val ordering = Ordering.fromLessThan[Rational]((x, y) =>
5 | (x.numerator.toDouble / x.denominator.toDouble) <
6 | (y.numerator.toDouble / y.denominator.toDouble)
7 | )
8 |
9 | assert(List(Rational(1, 2), Rational(3, 4), Rational(1, 3)).sorted ==
10 | List(Rational(1, 3), Rational(1, 2), Rational(3, 4)))
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/src/pages/implicits/using-type-classes.md:
--------------------------------------------------------------------------------
1 | ## Using Type Classes
2 |
3 | We have seen how to define type classes. In this section we'll see some conveniences for using them: *context bounds* and the *implicitly* method.
4 |
5 | ### Context Bounds
6 |
7 | When we use type classes we often end up requiring implicit parameters that we pass onward to a type class interface. For example, using our `HtmlWriter` example we might want to define some kind of page template that accepts content rendered by a writer.
8 |
9 | ```tut:invisible
10 | trait HtmlWriter[A] {
11 | def write(in: A): String
12 | }
13 | implicit class HtmlUtil[A](value: A) {
14 | def toHtml(implicit writer: HtmlWriter[A]) =
15 | writer write value
16 | }
17 | ```
18 |
19 | ```tut:book:silent
20 | def pageTemplate[A](body: A)(implicit writer: HtmlWriter[A]): String = {
21 | val renderedBody = body.toHtml
22 |
23 | s"...${renderedBody}"
24 | }
25 | ```
26 |
27 | We don't explicitly use the implicit `writer` in our code, but we need it in scope so the compiler can insert it for the `toHtml` enrichment.
28 |
29 | Context bounds allow us to write this more compactly, with a notation that is reminiscent of a type bound.
30 |
31 | ```tut:book:silent
32 | def pageTemplate[A : HtmlWriter](body: A): String = {
33 | val renderedBody = body.toHtml
34 |
35 | s"...${renderedBody}"
36 | }
37 | ```
38 |
39 | The context bound is the notation `[A : HtmlWriter]` and it expands into the equivalent implicit parameter list in the prior example.
40 |
41 |
42 | #### Context Bound Syntax {-}
43 |
44 | A context bound is an annotation on a generic type variable like so:
45 |
46 | ```scala
47 | [A : Context]
48 | ```
49 |
50 | It expands into a generic type parameter `[A]` along with an implicit parameter for a `Context[A]`.
51 |
52 |
53 | ### Implicitly
54 |
55 | Context bounds give us a short-hand syntax for declaring implicit parameters, but since we don't have an explicit name for the parameter we cannot use it in our methods. Normally we use context bounds when we don't need explicit access to the implicit parameter, but rather just implicitly pass it on to some other method. However if we do need access for some reason we can use the `implicitly` method.
56 |
57 | ```tut:book:silent
58 | case class Example(name: String)
59 | implicit val implicitExample = Example("implicit")
60 | ```
61 |
62 | ```tut:book
63 | implicitly[Example]
64 |
65 | implicitly[Example] == implicitExample
66 | ```
67 |
68 | The `implicitly` method takes no parameters but has a generic type parameters. It returns the implicit matching the given type, assuming there is no ambiguity.
69 |
--------------------------------------------------------------------------------
/src/pages/index.md:
--------------------------------------------------------------------------------
1 | # Foreword #{-}
2 |
3 | This book is aimed at programmers learning Scala for the first time. We assume you have some familiarity with an object-oriented programming language such as Java, but little or no experience with functional programming.
4 |
5 | Our goal is to describe how to use Scala in-the-small. To this end our focus is on the core patterns used in idiomatic Scala code, and we introduce Scala's features in the context of the patterns they enable. We are not aiming for exhaustive coverage of Scala's features, and this text is not a reference manual.
6 |
7 | Except for a few exercises we don't rely on any external libraries. You should be able to complete all the problems inside with only a text editor and Scala's REPL, or an IDE such as the [Scala IDE for Eclipse](http://scala-ide.org/) or [IntelliJ IDEA](http://www.jetbrains.com/idea/).
8 |
9 | Essential Scala was created by [Noel Welsh](http://noelwelsh.com) and [Dave Gurnell](http://davegurnell.com/) of [Underscore](http://underscore.io). It was built using [Underscore's eBook Template](https://github.com/underscoreio/underscore-ebook-template), plain text, and a deep and profound love of functional programming.
10 |
11 | ## Conventions Used in This Book #{-}
12 |
13 | This book contains a lot of technical information and program code. We use the following typographical conventions to reduce ambiguity and highlight important concepts:
14 |
15 | ### Typographical Conventions #{-}
16 |
17 | New terms and phrases are introduced in *italics*. After their initial introduction they are written in normal roman font.
18 |
19 | Terms from program code, filenames, and file contents, are written in `monospace font`. Note that we do not distinguish between singular and plural forms. For example, might write `String` or `Strings` to refer to the `java.util.String` class or objects of that type.
20 |
21 | References to external resources are written as [hyperlinks][link-underscore]. References to API documentation are written using a combination of hyperlinks and monospace font, for example: [`Option`][scala.Option].
22 |
23 | ### Source Code #{-}
24 |
25 | Source code blocks are written as follows. Syntax is highlighted appropriately where applicable:
26 |
27 | ```scala
28 | object MyApp extends App {
29 | println("Hello world!") // Print a fine message to the user!
30 | }
31 | ```
32 |
33 | Some lines of program code are too wide to fit on the page. In these cases we use a *continuation character* (curly arrow) to indicate that longer code should all be written on one line. For example, the following code:
34 |
35 | ```scala
36 | println("This code should all be written ↩
37 | on one line.")
38 | ```
39 |
40 | should actually be written as follows:
41 |
42 | ```scala
43 | println("This code should all be written on one line.")
44 | ```
45 |
46 | ### Callout Boxes #{-}
47 |
48 | We use three types of *callout box* to highlight particular content:
49 |
50 |
51 | Tip callouts indicate handy summaries, recipes, or best practices.
52 |
53 |
54 |
55 | Advanced callouts provide additional information on corner cases or underlying mechanisms. Feel free to skip these on your first read-through---come back to them later for extra information.
56 |
57 |
58 |
59 | Warning callouts indicate common pitfalls and gotchas. Make sure you read these to avoid problems, and come back to them if you're having trouble getting your code to run.
60 |
61 |
--------------------------------------------------------------------------------
/src/pages/intro/conclusion.md:
--------------------------------------------------------------------------------
1 | ## Conclusion
2 |
3 | We have had a very brief introduction to the fundamentals of Scala:
4 |
5 | * expressions, which evaluate to values; and
6 | * declarations, which gives names to values.
7 |
8 | We've seen how we can write literals for many objects, and use method calls and compound expressions to create new objects from existing ones.
9 |
10 | We have also declared our own objects, and constructed methods and fields.
11 |
12 | Next we're going to see how a new kind of declaration, a class, provides a template for creating objects. Classes allow us to reuse code and unify similar objects with a common type.
13 |
--------------------------------------------------------------------------------
/src/pages/intro/expressions.md:
--------------------------------------------------------------------------------
1 | ## Compound Expressions
2 |
3 | We have almost finished our basic introduction to Scala. In this section we are going to look at two special kinds of expressions, *conditionals* and *blocks*, we will need in more complicated programs.
4 |
5 | ### Conditionals
6 |
7 | A conditional allows us to choose an expression to evaluate based on some condition. For example, we can choose a string based on which of two numbers is the smallest.
8 |
9 | ```tut:book
10 | if(1 < 2) "Yes" else "No"
11 | ```
12 |
13 |
14 | #### Conditionals are Expressions {-}
15 |
16 | Scala's `if` statement has the same syntax as Java's. One important difference is that *Scala's conditional is an expression*---it has a type and returns a value.
17 |
18 |
19 | The expression that is not selected does not get evaluated. This is apparent if we use an expression with a side-effect.
20 |
21 | ```tut:book
22 | if(1 < 2) println("Yes") else println("No")
23 | ```
24 |
25 | We can tell the expression `println("No")` is not evaluated because `No` is not output to the console.
26 |
27 |
28 | #### Conditional Expression Syntax {-}
29 |
30 | The syntax for a conditional expression is
31 |
32 | ```scala
33 | if(condition)
34 | trueExpression
35 | else
36 | falseExpression
37 | ```
38 |
39 | where
40 |
41 | - `condition` is an expression with `Boolean` type;
42 | - `trueExpression` is the expression evaluated if `condition` evaluates to `true`; and
43 | - `falseExpression` is the expression evaluated if `condition` evaluates to `false`.
44 |
45 |
46 |
47 | ### Blocks
48 |
49 | Blocks are expressions that allow us to sequence computations together. They are written as a pair of braces containing sub-expressions separated by semicolons or newlines.
50 |
51 | ```tut:book:fail
52 | { 1; 2; 3 }
53 | ```
54 |
55 | As you can see, executing this code causes the console to raise a number of warnings and return the `Int` value `3`.
56 |
57 | A block is a sequence of expressions or declarations surrounded by braces. A block is also an expression: it executes each of its sub-expressions in order and returns the value of the last expression.
58 |
59 | Why execute `1` and `2` if we're going to throw their values away? This is a good question, and is the reason the Scala compiler raised those warnings above.
60 |
61 | One reason to use a block is to use code that produces side-effects before calculating a final value:
62 |
63 | ```tut:book
64 | {
65 | println("This is a side-effect")
66 | println("This is a side-effect as well")
67 | 3
68 | }
69 | ```
70 |
71 | We can also use a block when we want to name intermediate results, such as
72 |
73 | ```tut:book:silent
74 | def name: String = {
75 | val title = "Professor"
76 | val name = "Funkenstein"
77 | title + " " + name
78 | }
79 | ```
80 |
81 | ```tut:book
82 | name
83 | ```
84 |
85 |
86 |
87 | #### Block Expression Syntax {-}
88 |
89 | The syntax of a block expression is
90 |
91 | ```scala
92 | {
93 | declarationOrExpression ...
94 | expression
95 | }
96 | ```
97 |
98 | where
99 |
100 | - the optional `declarationOrExpression`s are declarations or expression; and
101 | - `expression` is an expression determining the type and value of the block expression.
102 |
103 |
104 | ### Take home points
105 |
106 | Conditional expressions allow us to choose an expression to evaluate based on a `Boolean` condition. The syntax is
107 |
108 | ```scala
109 | if(condition)
110 | trueExpression
111 | else
112 | falseExpression
113 | ```
114 |
115 | A conditional, being an expression, has a type and evaluates to an object.
116 |
117 |
118 | A block allows us to sequence expressions and declarations. It is commonly used when we want to sequence expressions with side-effects, or name intermediate results in a computation. The syntax is
119 |
120 | ```scala
121 | {
122 | declarationOrExpression ...
123 | expression
124 | }
125 | ```
126 |
127 | The type and value of a block is that of the last expression in the block.
128 |
129 |
130 | ### Exercises
131 |
132 | #### A Classic Rivalry
133 |
134 | What is the type and value of the following conditional?
135 |
136 | ```tut:book:silent
137 | if(1 > 2) "alien" else "predator"
138 | ```
139 |
140 |
141 | It's a `String` with value `"predator"`. Predators are clearly best:
142 |
143 | ```tut:book
144 | if(1 > 2) "alien" else "predator"
145 | ```
146 |
147 | The type is determined by the upper bound of the types in the *then* and *else* expressions. In this case both expressions are `Strings` so the result is also a `String`.
148 |
149 | The value is determined at runtime. `2` is greater than `1` so the conditional evaluates to the value of the *else* expression.
150 |
151 |
152 | #### A Less Well Known Rivalry
153 |
154 | What about this conditional?
155 |
156 | ```tut:book:silent
157 | if(1 > 2) "alien" else 2001
158 | ```
159 |
160 |
161 | It's a value of type `Any` with value `2001`:
162 |
163 | ```tut:book
164 | if(1 > 2) "alien" else 2001
165 | ```
166 |
167 | This is similar to the previous exercise---the difference is the type of the result. We saw earlier that the type is the *upper bound* of the positive and negative arms of the expression. `"alien"` and `2001` are completely different types - their closest common ancestor is `Any`, which is the grand supertype of all Scala types.
168 |
169 | This is an important observation: types are determined at compile time, before the program is run. The compiler doesn't know which of `1` and `2` is greater before running the program, so it can only make a best guess at the type of the result of the conditional. `Any` is as close as it can get in this program, whereas in the previous exercise it can get all the way down to `String`.
170 |
171 | We'll learn more about `Any` in the following sections. Java programmers shouldn't confuse it with `Object` because it subsumes value types like `Int` and `Boolean` as well.
172 |
173 |
174 | #### An if Without an else
175 |
176 | What about this conditional?
177 |
178 | ```tut:book:silent
179 | if(false) "hello"
180 | ```
181 |
182 |
183 | The result type and value are `Any` and `()` respectively:
184 |
185 | ```tut:book
186 | if(false) "hello"
187 | ```
188 |
189 | All code being equal, conditionals without `else` expressions only evaluate to a value half of the time. Scala works around this by returning the `Unit` value if the `else` branch should be evaluated. We would usually only use these expressions for their side-effects.
190 |
191 |
--------------------------------------------------------------------------------
/src/pages/intro/guiding-principles.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # Guiding Principles
6 |
7 | Remove unnecessary distinctions
8 |
--------------------------------------------------------------------------------
/src/pages/intro/history.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # A Brief History of Scala
6 |
7 | Scala was created by [Martin Odersky](http://lampwww.epfl.ch/~odersky/) a professor at [EPFL](http://epfl.ch/), Switzerland. Martin had extensive experience with Java, having designed Java's generics and several previous languages running on the JVM.
8 |
9 | Scala was designed to fuse object-oriented and functional language features into a practical language
10 |
--------------------------------------------------------------------------------
/src/pages/intro/literals.md:
--------------------------------------------------------------------------------
1 | ## Literal Objects
2 |
3 | We have already covered some of Scala's basic types. In this section we're going to round out that knowledge by covering all of Scala's *literal expressions*. A literal expression represents a fixed value that stands "for itself". Here's an example:
4 |
5 | ```tut:book
6 | 42
7 | ```
8 |
9 | This interaction at the REPL shows us that the literal `42` evaluates to the `Int` `42`.
10 |
11 | Don't confuse a literal with the value it evaluates to! The literal expression is the representation in the program text before the program is run, and the value is the representation in the computer's memory after the program has run.
12 |
13 | If you have prior programming experience, particularly Java experience, the literals in Scala should be familiar to you.
14 |
15 | ### Numbers
16 |
17 | Numbers share the same types available in Java: `Int` for 32-bit integers, `Double` for 64-bit floating point, `Float` for 32-bit floating point, and `Long` for 64-bit integers.
18 |
19 | ```tut:book
20 | 42
21 | 42.0
22 | 42.0f
23 | 42L
24 | ```
25 |
26 | Scala also has 16-bit `Short` integers and 8-bit `Byte`s, but there is no literal syntax for creating them. Instead, we create them using methods called `toShort` and `toByte`.
27 |
28 | ### Booleans
29 |
30 | Booleans are exactly the same as Java: `true` or `false`.
31 |
32 | ```tut:book
33 | true
34 | false
35 | ```
36 |
37 | ### Characters
38 |
39 | `Chars` are 16-bit Unicode values written as a single character enclosed in single quotes.
40 |
41 | ```tut:book
42 | 'a'
43 | ```
44 |
45 |
46 | #### Scala vs Java's Type Hierarchy {-}
47 |
48 | Although they are written with initial capitals, Scala's `Int`, `Double`, `Float`, `Long`, `Short`, `Byte`, `Boolen` and `Char` refer to exactly the same things as `int`, `double`, `float`, `long`, `short`, `byte`, `boolean`, and `char` in Java.
49 |
50 | In Scala all of these types act like objects with methods and fields. However, once your code is compiled, a Scala `Int` is exactly the same as a Java `int`. This makes interoperability between the two languages a breeze.
51 |
52 |
53 | ### Strings
54 |
55 | Strings are exactly Java's strings, and are written the same way.
56 |
57 | ```tut:book
58 | "this is a string"
59 | "the\nusual\tescape characters apply"
60 | ```
61 |
62 | ### Null
63 |
64 | Null is the same as Java, though not used nearly as often. Scala's `null` also has its own type: `Null`.
65 |
66 | ```tut:book
67 | null
68 | ```
69 |
70 |
71 | #### Using Nulls in Scala {-}
72 |
73 | Although `null`s are common in Java code, they are considered very bad practice in Scala.
74 |
75 | The main use of `null` in Java is to implement *optional* values that have some or no value at different points of a program's execution. However, `null` values cannot be checked by the compiler, leading to possible runtime errors in the form of `NullPointerExceptions`.
76 |
77 | Later we will see that Scala has the means to define optional values that *are* checked by the compiler. This removes the necessity of using `null`, making our programs much safer.
78 |
79 |
80 | ### Unit
81 |
82 | Unit, written `()`, is the Scala equivalent of Java's `void`. Unit is the result of expressions that evaluate to no interesting value, such as printing to standard output using `println`. The console doesn't print unit but we can ask for the type of an expression to see that unit is in fact the result of some expressions.
83 |
84 | ```tut:book
85 | ()
86 | ```
87 |
88 | ```scala
89 | :type ()
90 | // Unit
91 | ```
92 |
93 | ```tut:book
94 | println("something")
95 | ```
96 |
97 | ```scala
98 | :type println("something")
99 | // Unit
100 | ```
101 |
102 | Unit is an important concept in Scala. Many of Scala's syntactic constructs are *expressions* that have types and values. We need a placeholder for expressions that don't yield a useful value, and unit provides just that.
103 |
104 | ### Take home points
105 |
106 | In this section we have seen *literal* expressions, which evaluate to basic data types. These basics types are mostly identical to Java, except for `Unit` which has no equivalent.
107 |
108 | We note that every literal expression has a *type*, and evaluates to a *value*---something which is also true for more complex Scala expressions.
109 |
110 | In the next section we will learn how to define our own object literals.
111 |
112 | ### Exercises
113 |
114 | #### Literally Just Literals
115 |
116 | What are the values and types of the following Scala literals?
117 |
118 | ```tut:book:silent
119 | 42
120 |
121 | true
122 |
123 | 123L
124 |
125 | 42.0
126 | ```
127 |
128 |
129 | `42` is an `Int`. `true` is a `Boolean`. `123L` is a `Long`. `42.0` is a `Double`.
130 |
131 | This exercise just gives you some experience using the Scala console or Worksheet.
132 |
133 |
134 | #### Quotes and Misquotes
135 |
136 | What is the difference between the following literals? What is the type and value of each?
137 |
138 | ```tut:book:silent
139 | 'a'
140 |
141 | "a"
142 | ```
143 |
144 |
145 | The first is a literal `Char` and the second is a literal `String`.
146 |
147 |
148 | #### An Aside on Side-Effects
149 |
150 | What is the difference between the following expressions? What is the type and value of each?
151 |
152 | ```tut:book:silent
153 | "Hello world!"
154 |
155 | println("Hello world!")
156 | ```
157 |
158 |
159 | The literal expression `"Hello world!"` evaluates to a `String` value. The expression `println("Hello world!")` evaluates to `Unit` and, as a side-effect, prints `"Hello world!"` on the console.
160 |
161 | This an important distinction between a program that evaluates to a value and a program that prints a value as a side-effect. The former can be used in a larger expression but the latter cannot.
162 |
163 |
164 | #### Learning By Mistakes
165 |
166 | What is the type and value of the following literal? Try writing it on the REPL or in a Scala worksheet and see what happens!
167 |
168 | ```scala
169 | 'Hello world!'
170 | ```
171 |
172 |
173 | You should see an error message. Take the time to read and get used to the error messages in your development environment---you'll see plenty more of them soon!
174 |
175 |
--------------------------------------------------------------------------------
/src/pages/intro/writing-methods.md:
--------------------------------------------------------------------------------
1 | ## Writing Methods
2 |
3 | In the previous section we saw the syntax of methods. One of our main goals in this course is to go beyond syntax and give you systematic methods for constructing Scala programs. This is our first section dealing with such matters. In this section we're going to look at a systematic method for constructing methods. As you gain experience with Scala you can drop some of the steps of this method, but we *strongly* suggest you follow this method during the course.
4 |
5 | To make the advice concrete we'll use this exercise from the previous section as an example:
6 |
7 | *Define an object called `calc` with a method `square` that accepts a `Double` as an argument and... you guessed it... squares its input. Add a method called `cube` that cubes its input, calling `square` as part of its result calculation.*
8 |
9 | ### Identify the Input and Output
10 |
11 | Your first step is to identify the types of the input parameters, if any, and the result of the method.
12 |
13 | In many cases the exercises will tell you the types and you can just read them straight from the description. In the example above the input type is given as `Double`. The result type we can infer is also `Double`.
14 |
15 | ### Prepare Test Cases
16 |
17 | Types alone don't tell all the story. There are many `Double` to `Double` functions, but few that implement squaring. Thus we should prepare some test cases that illustrate the expected behaviour of the method.
18 |
19 | We're not going to use a testing library in this course, as we're trying to avoid external dependencies. We can implement a poor-man's testing library using the `assert` function that Scala provides. For our `square` example we might have test cases like
20 |
21 | ```scala
22 | assert(square(2.0) == 4.0)
23 | assert(square(3.0) == 9.0)
24 | assert(square(-2.0) == 4.0)
25 | ```
26 |
27 | ### Write the Declaration
28 |
29 | With types and test cases ready we can now write the method declaration. We haven't developed the body yet so use `???`, another nifty Scala feature, in its place.
30 |
31 | ```tut:book:silent
32 | def square(in: Double): Double =
33 | ???
34 | ```
35 |
36 | This step should be mechanical given the information gathered in the previous steps.
37 |
38 | ### Run the Code
39 |
40 | Run the code and check it compiles (and thus we haven't made any typos) and also that our tests fail (and thus are testing something). You may need to place the tests after the method declaration.
41 |
42 | ### Write the Body
43 |
44 | We're now ready to write the body of our method. We will develop a number of techniques for this throughout the course. For now, we're going to look at two techniques.
45 |
46 | #### Consider the Result Type
47 |
48 | The first technique is to look at the result type, in this case `Double`. How can we create `Double` values? We could write a literal, but that obviously won't be correct in this case. The other way we know to create a `Double` is to call a method on some object, which brings us to the next technique.
49 |
50 | #### Consider the Input Type
51 |
52 | Our next technique is to look at the type of input parameters to the method. In this case we have a `Double`. We have established we need to create a `Double`, so what methods can we call to create a `Double` from our input? There are many such methods, and here we have to use our domain knowledge to select `*` as the correct method to call.
53 |
54 | We can now write our complete method as
55 |
56 | ```tut:book:silent
57 | def square(in: Double): Double =
58 | in * in
59 | ```
60 |
61 | ### Run the Code, Again
62 |
63 | Finally we should run the code again and check that the tests all pass in this case.
64 |
65 | This is very simple example but practicing the process now will serve you well for the more complicated examples we will encounter later.
66 |
67 |
68 | #### Process for Writing Methods {-}
69 |
70 | We have a six-step process for writing methods in a systematic way.
71 |
72 | 1. Identify the type of the inputs and output of the method.
73 | 2. Write some test cases for the expected output of the method given example input. We can use the `assert` function to write down these cases.
74 | 3. Write the method declaration using `???` for the body like so:
75 |
76 | ```scala
77 | def name(parameter: type, ...): resultType =
78 | ???
79 | ```
80 | 4. Run the code to check the test cases do in fact fail.
81 | 5. Write the body of the method. We currently have two techniques to apply here:
82 | - consider the result type and how we can create an instance of it; and
83 | - consider the input type and methods we can call to transform it to the result type.
84 | 6. Run the code again and check the test cases pass.
85 |
86 |
--------------------------------------------------------------------------------
/src/pages/links.md:
--------------------------------------------------------------------------------
1 | [scala.Option]: https://www.scala-lang.org/api/current/index.html#scala.Option
2 |
3 | [link-advanced-scala]: https://underscore.io/books/advanced-scala
4 | [link-bnf]: https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form
5 | [link-json]: http://www.json.org/
6 | [link-linked-list]: https://en.wikipedia.org/wiki/Linked_list
7 | [link-multiple-inheritance]: http://en.wikipedia.org/wiki/Multiple_inheritance
8 | [link-precedence-rules]: http://stackoverflow.com/questions/2922347/operator-precedence-in-scala
9 | [link-product-types]: http://en.wikipedia.org/wiki/Product_type
10 | [link-tut]: https://github.com/tpolecat/tut
11 | [link-uap]: http://en.wikipedia.org/wiki/Uniform_access_principle
12 | [link-underscore]: https://underscore.io
13 |
--------------------------------------------------------------------------------
/src/pages/object-functional/pattern-matching.md:
--------------------------------------------------------------------------------
1 | # Pattern Matching
2 |
3 | ### Custom Pattern Matching
4 |
5 | The `unapply` and `unapplySeq` methods allow us to implement our own patterns for use in pattern matching.
6 |
--------------------------------------------------------------------------------
/src/pages/pattern-matching/extractors.scala:
--------------------------------------------------------------------------------
1 | object Positive {
2 | def unapply(in: Int): Option[Int] =
3 | if(in > 0)
4 | Some(in)
5 | else
6 | None
7 | }
8 |
9 | assert(
10 | "No" ==
11 | (0 match {
12 | case Positive(_) => "Yes"
13 | case _ => "No"
14 | })
15 | )
16 |
17 | assert(
18 | "Yes" ==
19 | (42 match {
20 | case Positive(_) => "Yes"
21 | case _ => "No"
22 | })
23 | )
24 |
25 | object Titlecase {
26 | def unapply(str: String): Option[String] = {
27 | Some(str.split(" ").toList.map {
28 | case "" => ""
29 | case word => word.substring(0, 1).toUpperCase + word.substring(1)
30 | }.mkString(" "))
31 | }
32 | }
33 |
34 | assert(
35 | "Sir Lord Doctor David Gurnell" ==
36 | ("sir lord doctor david gurnell" match {
37 | case Titlecase(str) => str
38 | })
39 | )
40 |
--------------------------------------------------------------------------------
/src/pages/pattern-matching/index.md:
--------------------------------------------------------------------------------
1 | # Pattern Matching
2 |
3 | We have seen the duality between algebraic data types and pattern matching. Armed with this information, we are in a good position to return to pattern matching and see some of its more powerful features.
4 |
5 | As we discussed earlier, patterns are written in their own DSL that only superficially resembles regular Scala code. *Patterns serve as tests that match a specific set of Scala values*. The `match` expression compares a value to each pattern in turn, finds the first pattern that matches, and executes the corresponding block of Scala code.
6 |
7 | *Some patterns bind values to variables* that can be used on the right hand side of the corresponding `=>` symbol, and *some patterns contain other patterns*, allowing us to build complex tests that simultaneously examine many parts of a value. Finally, *we can create our own custom patterns*, implemented in Scala code, to match any cross-section of values we see fit.
8 |
9 | We have already seen case class patterns and certain types of sequence patterns. Each of the remaining types of pattern is described below together with an example of its use.
10 |
11 | ## Standard patterns
12 |
13 | ### Literal patterns
14 |
15 | Literal patterns match a particular value. Any Scala literals work except function literals: primitive values, `Strings`, `nulls`, and `()`:
16 |
17 | ```tut:invisible
18 | case class Person(name: String, surname: String)
19 | ```
20 |
21 | ```tut:book
22 | (1 + 1) match {
23 | case 1 => "It's one!"
24 | case 2 => "It's two!"
25 | case 3 => "It's three!"
26 | }
27 |
28 | Person("Dave", "Gurnell") match {
29 | case Person("Noel", "Welsh") => "It's Noel!"
30 | case Person("Dave", "Gurnell") => "It's Dave!"
31 | }
32 |
33 | println("Hi!") match {
34 | case () => "It's unit!"
35 | }
36 | ```
37 |
38 | ### Constant patterns
39 |
40 | Identifiers starting with an uppercase letter are *constants* that match a single predefined constant value:
41 |
42 | ```tut:book
43 | val X = "Foo"
44 |
45 | val Y = "Bar"
46 |
47 | val Z = "Baz"
48 |
49 | "Bar" match {
50 | case X => "It's foo!"
51 | case Y => "It's bar!"
52 | case Z => "It's baz!"
53 | }
54 | ```
55 |
56 | ### Alternative patterns
57 |
58 | Vertical bars can be used to specify alternatives:
59 |
60 | ```tut:book
61 | "Bar" match {
62 | case X | Y => "It's foo or bar!"
63 | case Z => "It's baz!"
64 | }
65 | ```
66 |
67 |
68 | ### Variable capture
69 |
70 | Identifiers starting with lowercase letters bind values to variables. The variables can be used in the code to the right of the `=>`:
71 |
72 | ```tut:book
73 | Person("Dave", "Gurnell") match {
74 | case Person(f, n) => f + " " + n
75 | }
76 | ```
77 |
78 | The `@` operator, written `x @ y`, allows us to capture a value in a variable `x` while also matching it against a pattern `y`. `x` must be a variable pattern and `y` can be any type of pattern. For example:
79 |
80 | ```tut:book
81 | Person("Dave", "Gurnell") match {
82 | case p @ Person(_, s) => s"The person $p has the surname $s"
83 | }
84 | ```
85 |
86 | ### Wildcard patterns
87 |
88 | The `_` symbol is a pattern that matches any value and simply ignores it. This is useful in two situations: when nested inside other patterns, and when used on its own to provide an "else" clause at the end of a match expression:
89 |
90 | ```tut:book
91 | Person("Dave", "Gurnell") match {
92 | case Person("Noel", _) => "It's Noel!"
93 | case Person("Dave", _) => "It's Dave!"
94 | }
95 |
96 | Person("Dave", "Gurnell") match {
97 | case Person(name, _) => s"It's $name!"
98 | }
99 |
100 | Person("John", "Doe") match {
101 | case Person("Noel", _) => "It's Noel!"
102 | case Person("Dave", _) => "It's Dave!"
103 | case _ => "It's someone else!"
104 | }
105 | ```
106 |
107 | ### Type patterns
108 |
109 | A type pattern takes the form `x: Y` where `Y` is a type and `x` is a wildcard pattern or a variable pattern. The pattern matches any value of type `Y` and binds it to `x`:
110 |
111 | ```tut:invisible
112 | trait Shape
113 | case class Circle(radius: Double) extends Shape
114 | case class Rectangle(side1: Double, side2: Double) extends Shape
115 | case class Square(side: Double) extends Shape
116 | ```
117 |
118 | ```tut:book
119 | val shape: Shape = Rectangle(1, 2)
120 |
121 | shape match {
122 | case c : Circle => s"It's a circle: $c!"
123 | case r : Rectangle => s"It's a rectangle: $r!"
124 | case s : Square => s"It's a square: $s!"
125 | }
126 | ```
127 |
128 | ### Tuple patterns
129 |
130 | Tuples of any arity can be matched with parenthesised expressions as follows:
131 |
132 | ```tut:book
133 | (1, 2) match {
134 | case (a, b) => a + b
135 | }
136 | ```
137 |
138 | ### Guard expressions
139 |
140 | This isn't so much a pattern as a feature of the overall `match` syntax. We can add an extra condition to any `case` clause by suffixing the pattern with the keyword `if` and a regular Scala expression. For example:
141 |
142 | ```tut:book
143 | 123 match {
144 | case a if a % 2 == 0 => "even"
145 | case _ => "odd"
146 | }
147 | ```
148 |
149 | To reiterate, the code between the `if` and `=>` keywords is a regular Scala expression, not a pattern.
150 |
--------------------------------------------------------------------------------
/src/pages/sbt/index.md:
--------------------------------------------------------------------------------
1 | # SBT
2 |
3 | SBT, or "Simple Build Tool", is the recommended build tool for Scala. It is similar to the Maven build tool, but allows you to write configuration files using Scala code as opposed to XML. SBT projects has the benefit of being significantly terser than their Maven equivalents.
4 |
5 | In this section we will show you the basics of setting up an SBT project. The intention is to give you a quick way of writing, compiling, and running Scala code. We won't go into the details of writing build configurations---there is plenty of information for the budding SBT users online in the [SBT Wiki](https://github.com/harrah/xsbt/wiki) on Github and the [SBT community website](http://www.scala-sbt.org/).
6 |
7 | ## Requirements
8 |
9 | In order to get started with Core Scala, you will need to have the following softwar installed and configured:
10 |
11 | - a Java 6 or 7 compatible JVM;
12 | - a recent version of Git.
13 |
14 | ## Getting started
15 |
16 | To start with, clone our template SBT project from Github:
17 |
18 | ```scala
19 | bash:~$ git clone git://github.com/underscoreconsulting/essential-scala-template.git myproject
20 | Cloning into myproject...
21 |
22 | bash:~$ cd myproject
23 |
24 | bash:~/myproject$
25 | ```
26 |
27 | The SBT launcher script is provided for you in the file `sbt`. Run it now to start a Scala console. SBT will download JAR files for Scala and various dependencies and cache them on your hard drive. At the end of the process you will see a `scala>` command prompt:
28 |
29 | ```scala
30 | Trip:~/myproject$ ./sbt console
31 | Detected sbt version 0.11.3
32 | Using /Users/me/.sbt/0.11.3 as sbt dir, -sbt-dir to override.
33 | [info] Loading project definition from /Users/me/myproject/project
34 | [info] Updating {file:/Users/me/myproject/project/}default-02b1d7...
35 | [info] Resolving org.scala-sbt#sbt_2.9.1;0.11.3 ...
36 |
37 | ### .... Lots of "Resolving" lines ....
38 |
39 | [info] Done updating.
40 | [info] Set current project to essential-scala (in build file:/Users/me/myproject/)
41 | [info] Updating {file:/Users/me/myproject/}default-2c3445...
42 | [info] Resolving org.scala-lang#scala-library;2.9.2 ...
43 | [info] Done updating.
44 | [info] Starting scala interpreter...
45 | [info]
46 | Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_35).
47 | Type in expressions to have them evaluated.
48 | Type :help for more information.
49 |
50 | scala>
51 | ```
52 |
53 | Congratulations - you have installed Scala! You can now start interactively playing with Scala code:
54 |
55 | ```scala
56 | scala> 1 + 1
57 | res0: Int = 2
58 |
59 | scala> println("Hello world!")
60 | Hello world!
61 | ```
62 |
63 | Issue the command `:quit` to leave the Scala console and return to your OS:
64 |
65 | ```bash
66 | scala> :quit
67 |
68 | [success] Total time: 408 s, completed Sep 18, 2012 12:07:19 PM
69 |
70 | bash:~/myproject$
71 | ```
72 |
73 | ## SBT: The Rebel Cut
74 |
75 | Our template uses an [SBT Launcher script](https://github.com/paulp/sbt-extras/blob/master/sbt) written by Paul Phillips. The launcher, which helps you select and manage multiple versions of SBT and Scala from the bash command line, will become invaluable once you start working with different Scala projects that span multiple versions of Scala and SBT.
76 |
77 | Once the course is over, we recommend you get hold of the latest version of the script from Paul's Github project and add it to your `$PATH`.
78 |
--------------------------------------------------------------------------------
/src/pages/scala-basics/expressions.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # Expressions
6 |
7 | Expressions are the most important program construct in Scala. The defining characteristic of an expression is that when it runs it produces a value. More concisely, we say that expressions evaluate to values.
8 |
9 | ## Simple Literals
10 |
11 | The simplest expressions are literals. A literal represents a fixed value. Here's an example:
12 |
13 | ```scala
14 | scala> 42
15 | res0: Int = 42
16 | ```
17 |
18 | This interaction at the REPL shows us that the literal `42` evaluates to the `Int` `42`. Don't confuse a literal with the value it evaluates too! The literal expression is the representation in the program text before the program is run. The value is the representation in the computer's memory after the program has run.
19 |
20 | If you have prior programming experience you won't be surprised at the available literals in Java. Here's a quick run down of the major ones.
21 |
22 | ### Numbers
23 |
24 | Numbers share the same types available in Java: `Int` for 32-bit integers, `Double` for 64-bit floating point, `Float` for 32-bit floating point, and `Long` for 64-bit integers.
25 |
26 | ```scala
27 | scala> 42
28 | res0: Int = 42
29 |
30 | scala> 42.0
31 | res1: Double = 42.0
32 |
33 | scala> 42.0f
34 | res2: Float = 42.0
35 |
36 | scala> 42.0l
37 | res3: Long = 42
38 | ```
39 |
40 | Scala also has 16-bit `Short` integers and 8-bit `Byte`s, but there is no literal syntax for creating them. We'll see how to create them in a bit.
41 |
42 | ### String
43 |
44 | Strings are exactly Java's strings, and written the same way.
45 |
46 | ```scala
47 | scala> "this is a string"
48 | res8: String = this is a string
49 |
50 | scala> "the\nusual\tescape characters apply"
51 | res9: String =
52 | the
53 | usual escape characters apply
54 | ```
55 |
56 | ### Booleans
57 |
58 | Booleans are exactly the same as Java.
59 |
60 | ```scala
61 | scala> true
62 | res11: Boolean = true
63 |
64 | scala> false
65 | res12: Boolean = false
66 | ```
67 |
68 | ### Char
69 |
70 | Characters (`Char`s) are 16-bit Unicode values written as a single character enclosed in single quotes.
71 |
72 | ```scala
73 | scala> 'a'
74 | res34: Char = a
75 | ```
76 |
77 | ### Null
78 |
79 | Null is the same as Java, though not used nearly as often
80 |
81 | ```scala
82 | scala> null
83 | res13: Null = null
84 | ```
85 |
86 | ### Unit
87 |
88 | Unit, written `()` is the Scala equivalent of Java's `void`. Unit is the result of expressions that evaluate to no interesting value, such as printing to standard output using `println`. The REPL doesn't print unit be can ask for the type of an expression to see that unit is in fact the result of some expressions.
89 |
90 | ```scala
91 | scala> ()
92 |
93 | scala> :type ()
94 | Unit
95 |
96 | scala> println("something")
97 | something
98 |
99 | scala> :type println("something")
100 | Unit
101 | ```
102 |
103 |
104 | ## Compound Expressions
105 |
106 | Literals on their own aren't very interesting. It's only when we combine expressions into larger expressions that useful programs can be created.
107 |
108 | You're probably used to simple arthimetic expressions.
109 |
110 | ```scala
111 | scala> 43 - 3 + 2
112 | res14: Int = 42
113 | ```
114 |
115 | You are probably also familiar with the dot-notation to call methods on objects. Here's one way to create shorts and bytes:
116 |
117 | ```scala
118 | scala> 42.toShort()
119 | res16: Short = 42
120 |
121 | scala> 42.toByte()
122 | res17: Byte = 42
123 | ```
124 |
125 | ### Conditionals
126 |
127 | Conditionals are an essential part of any programming language. Scala's `if` statement has the same syntax as Java's. One difference in Scala is that a conditional returns a value.
128 |
129 | ```scala
130 | scala> if(true) {
131 | 42
132 | } else {
133 | 40
134 | }
135 | res45: Int = 42
136 | ```
137 |
138 | You can drop the brackets if a single expression follows an arm, and even write a conditional on one line.
139 |
140 | ```scala
141 | scala> if(true) 42 else 40
142 | res47: Int = 42
143 | ```
144 |
145 | ### Operators versus Methods
146 |
147 | In Scala **everything is an object**. This means that `Int` and other primitive types in Java are actually objects in Scala. If this is the case, then `+`, `-`, and so on should be methods on `Int` not operators. Is this correct? Yes!
148 |
149 | ```scala
150 | scala> 43 - 3 + 2
151 | res18: Int = 42
152 |
153 | scala> (43).-(3).+(2)
154 | res20: Int = 42
155 | ```
156 |
157 | (Note in the second example above I had to bracket `43` to stop `43.` being interpreted as a `Double`.)
158 |
159 | This is a general rule in Scala. Any expression you can write as `a.b(c)` you can also write as `a b c`. This is known as *operator style*. Note that `a b c d` is equivalent to `a.b(c).d`, not `a.b(c, d)`. You can only operator style with methods that take one or no arguments.
160 |
161 | ### Operator shortcuts
162 |
163 | Scala has a few other shortcuts in addition to operator style. If an object `foo` has a method called `apply` we can call that method using `foo(args)`. For example, `String` has an apply method (through a mysterious mechansim we [explain later](/collections/arrays-and-strings.html)) that allows to us to index characters within the string.
164 |
165 | ```scala
166 | scala> "hi there!"(0)
167 | res35: Char = h
168 | ```
169 |
170 | Be aware there is no dot before the parenthesis. Adding one is an error!
171 |
172 | ```scala
173 | scala> "hi there!".(0)
174 | :1: error: identifier expected but '(' found.
175 | "hi there!".(0)
176 | ```
177 |
178 |
188 |
--------------------------------------------------------------------------------
/src/pages/scala-basics/index.md:
--------------------------------------------------------------------------------
1 | # Scala Basics
2 |
3 | In this section we explore the fundamental constructs of Scala. We look at the basic building blocks of programs, expressions, definitions, and statements, and see how we can combine them to construct complex and interesting programs.
4 |
5 | ## Table of Contents
6 |
7 | - [Introducing Expressions](expressions.html)
8 | - [Introducing Scala's Type System](types.html)
9 | - [Introducing Definitions](definitions.html)
10 | - [Introducing Statements](statements.html)
11 | - [More Complex Programs](more-expressions.html)
12 |
--------------------------------------------------------------------------------
/src/pages/scala-basics/more-expressions.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # More Complex Programs
6 |
7 | We're now going to look at two more data types that have literal representations: tuples and functions. We will then briefly cover generic types.
8 |
9 | ## Tuples
10 |
11 | Tuples are a container for a *fixed number* of other values. They allow us to easily store values of different types without having to create a class for them. For example, to define a tuple containing an `Int`, and `String`, and a `Boolean` we could write:
12 |
13 | ```scala
14 | scala> (1, "tuple", true)
15 | res48: (Int, String, Boolean) = (1,tuple,true)
16 | ```
17 |
18 | We can access the elements of tuples using the `_n` method, where `n` is the index (starting from 1) of the element.
19 |
20 | ```scala
21 | scala> (1, "tuple", true)._1
22 | res49: Int = 1
23 |
24 | scala> (1, "tuple", true)._2
25 | res50: String = tuple
26 |
27 | scala> (1, "tuple", true)._3
28 | res51: Boolean = true
29 | ```
30 |
31 | We will see more elegant ways of doing this later.
32 |
33 | The `->` method provides a shortcut way of constructing tuples of two elements, sometimes called pairs.
34 |
35 | ```scala
36 | scala> 1 -> "foo"
37 | res52: (Int, String) = (1,foo)
38 |
39 | scala> 1.->"foo"
40 | res53: (Double, String) = (1.0,foo)
41 | ```
42 |
43 | ## Functions
44 |
45 | Functions, like methods, allow us to abstract over values. For example, here is a function that squares any `Int` it is passed as a parameter.
46 |
47 | ```scala
48 | scala> (x: Int) => x * x
49 | res16: Int => Int =
50 | ```
51 |
52 | A function literal consists of two parts, a parameter list and a body, separated by `=>`. Note we must give types to the parameters. If the body is a single expression we don't have to enclose it in braces but we must do so if it contains more than one expression.
53 |
54 | ```scala
55 | scala> (x: Int) => { x * x }
56 | res17: Int => Int =
57 |
58 | scala> (x: Int) => {
59 | | x + 1 // This has no useful purpose
60 | | x * x
61 | | }
62 | res18: Int => Int =
63 | ```
64 |
65 | Functions are very much like methods, but there is a critical difference: they are values. This means we can bind a `val` to a function, pass a function as a parameter to a method or function, and return a function from a method or function.
66 |
67 | ```scala
68 | scala> val square = (x: Int) => x * x
69 | square: Int => Int =
70 | ```
71 |
72 | In Scala all values are objects. A function is just an object with a method called `apply`. We can call (or apply) a function in the same way we'd call such an object.
73 |
74 | ```scala
75 | scala> square.apply(2)
76 | res9: Int = 4
77 |
78 | scala> square(2)
79 | res10: Int = 4
80 |
81 | scala> square apply 2
82 | res11: Int = 4
83 | ```
84 |
85 |
86 | ## Generic Types
87 |
88 | How can we create a method that accepts an object of any type and returns it? Recalling that `Any` is the top of the type hierarchy we could write:
89 |
90 | ```scala
91 | scala> def foo(x: Any) = x
92 | foo: (x: Any)Any
93 | ```
94 |
95 | This works but it loses type information. For example, when we pass in an `Int` the result has type `Any` and as such we can't use it in arthimetic expressions.
96 |
97 | ```scala
98 | scala> foo(1)
99 | res32: Any = 1
100 |
101 | scala> foo(1) - 1
102 | :9: error: value - is not a member of Any
103 | foo(1) - 1
104 | ^
105 | ```
106 |
107 | What we want is a *generic type*, so we can say our method accepts a value of some type `A` and returns the same type. Here's how we write it.
108 |
109 | ```scala
110 | scala> def foo[A](x: A) = x
111 | foo: [A](x: A)A
112 |
113 | scala> foo(1)
114 | res34: Int = 1
115 |
116 | scala> foo(1) - 1
117 | res35: Int = 0
118 |
119 | scala> foo("hi!")
120 | res36: String = hi!
121 | ```
122 |
123 | As we can see above, when we actually use `foo` the concrete type of it's argument is substituted for `A`.
124 |
125 | ### Type Bounds
126 |
127 | We've seen how to define generics that match any type. Sometimes we want to restrict the type to be a subtype or supertype of some other type. This is known as a type bound. So we want to implement a function that can handle only subtypes of `Enumeration`. We can use a type bounds like `[A <: Enumeration]` to do this.
128 |
129 | ```scala
130 | scala> def foo[A <: Enumeration](enum: Enumeration) = enum.maxId
131 | foo: [A <: Enumeration](enum: Enumeration)Int
132 |
133 | scala> foo(scala.swing.Dialog.Result)
134 | res3: Int = 3
135 | ```
136 |
137 | You can also declare bounds in the other direction (an upper bound) using `[A >: Enumeration]` and also declare upper and lower bounds.
138 |
--------------------------------------------------------------------------------
/src/pages/scala-basics/scala-basics.key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/scala-basics/scala-basics.key
--------------------------------------------------------------------------------
/src/pages/scala-basics/statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # Statements
6 |
7 | Statements are the final type of program component, and basically cover anything that isn't an expression or a definition. The most important kinds are assignment and package statements.
8 |
9 | ## Assignment
10 |
11 | We've already seen assignment statements when we introduced `var`s. Here they are again.
12 |
13 | ```scala
14 | scala> var theVar = 1
15 | theVar: Int = 1
16 |
17 | scala> theVar = 2
18 | theVar: Int = 2
19 | ```
20 |
21 | ## Package and Imports
22 |
23 | Packages are much like Java's packages, but there are a few critical differences.
24 |
25 | ### Imports
26 |
27 | Let's start by looking at import statements. We can import all the members of a package, using the mighty underscore instead of the star.
28 |
29 | ```scala
30 | scala> import scala.math._
31 | import scala.math._
32 | ```
33 |
34 | We can also import just one or a few members.
35 |
36 | ```scala
37 | scala> import scala.math.sin
38 | import scala.math.sin
39 |
40 | scala> import scala.math.{sin, cos, tan}
41 | import scala.math.{sin, cos, tan}
42 | ```
43 |
44 | We can rename imports as well.
45 |
46 | ```scala
47 | scala> import scala.math.{sin => Sine, cos => Cosine, tan => Tangent}
48 | import scala.math.{sin=>Sine, cos=>Cosine, tan=>Tangent}
49 |
50 | scala> Sine(0.0)
51 | res3: Double = 0.0
52 | ```
53 |
54 | We can also import just a package, and refer to members of that package using dot notation.
55 |
56 | ```scala
57 | scala> import scala.math
58 | import scala.math
59 |
60 | scala> math.sin(1.0)
61 | res4: Double = 0.8414709848078965
62 | ```
63 |
64 | Note that when we import just a package it stays around in the namespace, and we can later import members of that package without specifying the full path to that package.
65 |
66 | ```scala
67 | scala> import math.sin
68 | import math.sin
69 |
70 | scala> sin(1.0)
71 | res6: Double = 0.8414709848078965
72 | ```
73 |
74 | This is a fantastic way of making code unreadable, as it obscures where imports come from. Even better, we can end up aliasing other packages.
75 |
76 | ```scala
77 | scala> import java.awt.event
78 | import java.awt.event
79 |
80 | scala> import javax.imageio.event
81 | import javax.imageio.event
82 |
83 | scala> import event.ActionEvent // Which event package are we importing from?
84 | :29: error: ActionEvent is not a member of event
85 | import event.ActionEvent // Which event package are we importing from?
86 | ^
87 | ```
88 |
89 | We can get around this aliasing by specifying a complete path to a package, using the `_root_` specifier if necessary.
90 |
91 | ```scala
92 | scala> import _root_.java.awt.event.ActionEvent
93 | import _root_.java.awt.event.ActionEvent
94 | ```
95 |
96 | Note in the example above we didn't need to specify `_root_`. However it is easy to get into a situation where we do need to. If we follow Java naming convention and our package root is, say, `uk.org` and we want to import, say, `org.apache.commons` then we must fully qualify the import to let Scala know we aren't looking for `apache.commons` within our `uk.org` package.
97 |
98 | ### Packages
99 |
100 | Package are similar to Java but again there are some wrinkles. Note that we can't define packages at the REPL! You'll have to compile this code to try it out.
101 |
102 | We define packages in the usual way.
103 |
104 | ```scala
105 | package foo
106 |
107 | // Code goes here. In scope of package foo.
108 | ```
109 |
110 | We can also nest packages like in Java.
111 |
112 | ```scala
113 | package foo.bar
114 |
115 | // Code goes here. In scope of package foo.bar
116 | ```
117 |
118 | There is another form of nesting which has different semantics.
119 |
120 | ```scala
121 | package foo
122 | package bar
123 |
124 | // Code goes here. In scope of package foo.bar, and foo._ is imported
125 | ```
126 |
127 | In this form everything inside package `foo` is visible to our code, as well as everyting in `foo.bar`. This is sometimes convenient and sometimes confusing. Use whichever you feel most comfortable with.
128 |
--------------------------------------------------------------------------------
/src/pages/scala-basics/types.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | ---
4 |
5 | # Introducing Scala's Type System
6 |
7 | So far we've seen some simple expressions. Every expression has a type, and here we're going to look at some of the basics of the type system.
8 |
9 | ## What is a Type?
10 |
11 | Before we get going, let's define what a type is: *A type is any property of a program that we can establish without evaluating the program.* This seems simple enough but the implication is profound. In old languages like C types were used to specify the machine representation of data, essentially providing optimisation hints to the compiler. In modern languages like Scala types are used by the programmer to ensure that important program properties are maintained. For example, error handling is an important part of many programs. We can encode error handling in the type system (using, for example, the `Either` type, Scalaz's `Validation`, or Scala 2.10's `Try`) and then the compiler will ensure we always handle errors correctly. Appropriate use of the type system is a mark of an accomplished Scala developer.
12 |
13 | ## The Type Hierarchy
14 |
15 | Unlike Java, which separates primitive and object types, in Scala everything is an object. As a result the "primitive" types must live in the object hierarchy. `AnyVal` is their commmon supertype. `AnyRef` is how you spell `java.lang.Object` in Scala and is the supertype of all reference types. `Any` is the root of the class hierarchy. At the bottom of the hierarchy is `Nothing`. It is a subtype of all types, but no values with type `Nothing` exist. Until Scala 2.10 all Scala objects also extended `ScalaObject`. Since 2.10 `ScalaObject` has been removed.
16 |
17 | ## Value Types
18 |
19 | Remember that Scala's value types (`Int`, `Double`, and so) are exactly equal to Java's primitive types (`int`, `double`, and so). Yet in Scala they are objects. How can this be? The answer is that Scala does autoboxing just like Java to give the appearance that they are objects when it is necessary. When it isn't necessary a Scala `Int` is exactly a Java `int` with the same performance and space usage.
20 |
21 | In Scala 2.10 and onwards we'll be able to define our own value types, using a method known as [Value Classes](http://docs.scala-lang.org/sips/pending/value-classes.html). This is moderately advanced so we're not going to discuss it here, but it may be useful to be aware of this possibility.
22 |
23 | ## Type Declarations
24 |
25 | We can declare a type for an expression by following the expression with a colon and then a type. For example, to type an `Int` expression as an `AnyVal` we could write:
26 |
27 | ```scala
28 | scala> 42 : AnyVal
29 | res40: AnyVal = 42
30 | ```
31 |
32 | Note once we've done so we can only call methods that exist on `AnyVal`.
33 |
34 | ```scala
35 | scala> (42: AnyVal).-(2)
36 | :8: error: value - is not a member of AnyVal
37 | (42: AnyVal).-(2)
38 | ^
39 |
40 | scala> (42: AnyVal).hashCode
41 | res44: Int = 42
42 | ```
43 |
44 | ## Type Inference
45 |
46 | In all of our programs so far we haven't needed to declare types. This is because Scala performs *type inference*, a process by which is works out the types of expressions.
47 |
48 | Scala uses a simple type inference algorithm, where types flow from parameters to result of methods, and left to right across expressions and parameter lists. This algorithms makes it easy to reason about type inference, but does mean we have to provide more type declarations than we might need to with a more complex algorithm.
49 |
--------------------------------------------------------------------------------
/src/pages/sequencing/calculator.scala:
--------------------------------------------------------------------------------
1 | sealed trait Sum[+A, +B] {
2 | def fold[C](failure: A => C, success: B => C): C =
3 | this match {
4 | case Failure(v) => failure(v)
5 | case Success(v) => success(v)
6 | }
7 | def map[C](f: B => C): Sum[A, C] =
8 | this match {
9 | case Failure(v) => Failure(v)
10 | case Success(v) => Success(f(v))
11 | }
12 | def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] =
13 | this match {
14 | case Failure(v) => Failure(v)
15 | case Success(v) => f(v)
16 | }
17 | }
18 | final case class Failure[A](value: A) extends Sum[A, Nothing]
19 | final case class Success[B](value: B) extends Sum[Nothing, B]
20 |
21 | sealed trait Expression {
22 | def eval: Sum[String, Double] =
23 | this match {
24 | case Addition(l, r) => lift2(l, r, (left, right) => Success(left + right))
25 | case Subtraction(l, r) => lift2(l, r, (left, right) => Success(left - right))
26 | case Division(l, r) => lift2(l, r, (left, right) =>
27 | if(right == 0)
28 | Failure("Division by zero")
29 | else
30 | Success(left / right)
31 | )
32 | case SquareRoot(v) =>
33 | v.eval flatMap { value =>
34 | if(value < 0)
35 | Failure("Square root of negative number")
36 | else
37 | Success(Math.sqrt(value))
38 | }
39 | case Number(v) => Success(v)
40 | }
41 |
42 | def lift2(l: Expression, r: Expression, f: (Double, Double) => Sum[String, Double]) =
43 | l.eval flatMap { left =>
44 | r.eval flatMap { right =>
45 | f(left, right)
46 | }
47 | }
48 | }
49 | final case class Addition(left: Expression, right: Expression) extends Expression
50 | final case class Subtraction(left: Expression, right: Expression) extends Expression
51 | final case class Division(left: Expression, right: Expression) extends Expression
52 | final case class SquareRoot(value: Expression) extends Expression
53 | final case class Number(value: Double) extends Expression
54 |
55 | assert(Addition(Number(1), Number(2)).eval == Success(3))
56 | assert(SquareRoot(Number(-1)).eval == Failure("Square root of negative number"))
57 | assert(Division(Number(4), Number(0)).eval == Failure("Division by zero"))
58 | assert(Division(Addition(Subtraction(Number(8), Number(6)), Number(2)), Number(2)).eval == Success(2.0))
59 |
--------------------------------------------------------------------------------
/src/pages/sequencing/conclusions.md:
--------------------------------------------------------------------------------
1 | ## Conclusions
2 |
3 | In this section we have explored generic types and functions, which allow us to abstract over types and methods respectively.
4 |
5 | We have seen new patterns for generic algebraic types, and generic structural recursion. Using these building blocks we have seen some common patterns for working with generic types, namely *fold*, *map*, and *flatMap*.
6 |
7 | In the next section we will explore these topics further by working with the collections classes in Scala.
8 |
--------------------------------------------------------------------------------
/src/pages/sequencing/function-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Function Types
4 | ---
5 |
6 | In the previous section we described how to write function literals and function types, but we glossed over how exactly function types are implemented. Scala has 23 built-in generic classes for functions of 0 to 22 arguments. Here's what they look like:
7 |
8 | ```tut:book:silent
9 | trait Function0[+R] {
10 | def apply: R
11 | }
12 |
13 | trait Function1[-A, +B] {
14 | def apply(a: A): B
15 | }
16 |
17 | trait Function2[-A, -B, +C] {
18 | def apply(a: A, b: B): C
19 | }
20 |
21 | // and so on...
22 | ```
23 |
24 | For the most part this is all stuff we know. The only pieces of syntax we haven't seen are the `+` and `-` annotations on the type parameters. These are called *variance annotations*. Let's look at this for a moment.
25 |
26 | ## Invariance, Covariance and Contravariance
27 |
28 |
29 | #### Variance is Hard {-}
30 |
31 | Variance is one of the trickier aspects of Scala's type system. Although it is useful to be aware of its existence, we rarely have to use it in application code.
32 |
33 |
34 | If we have some type `Foo[A]`, and `A` is a subtype of `B`, is `Foo[A]` a subtype of `Foo[B]`? The answer depends on the *variance* of the type `Foo`. The variance of a generic type determines how its supertype/subtype relationships change with respect with its type parameters:
35 |
36 | A type `Foo[T]` is **invariant** in terms of `T`, meaning that the types `Foo[A]` and `Foo[B]` are unrelated regardless of the relationship between `A` and `B`. This is the default variance of any generic type in Scala.
37 |
38 | A type `Foo[+T]` is **covariant** in terms of `T`, meaning that `Foo[A]` is a supertype of `Foo[B]` if `A` is a supertype of `B`. Most Scala collection classes are covariant in terms of their contents. We'll see these next chapter.
39 |
40 | A type `Foo[-T]` is **contravariant** in terms of `T`, meaning that `Foo[A]` is a *subtype* of `Foo[B]` if `A` is a *supertype* of `B`. The only example of contravariance that I am aware of is function arguments.
41 |
42 | Functions are contravariant in terms of their arguments and covariant in terms of their return type. This seems counterintuitive but it makes sense if we look at it from the point of view of function arguments. Consider some code that expects a `Function1[A, B]`:
43 |
44 | ```tut:book:silent
45 | case class Box[A](value: A) {
46 | /** Apply `func` to `value`, returning a `Box` of the result. */
47 | def map[B](func: Function1[A, B]): Box[B] =
48 | Box(func(value))
49 | }
50 | ```
51 |
52 | To understand variance, consider what functions can we safely pass to this `map` method:
53 |
54 |
55 | - A function from `A` to `B` is clearly ok.
56 |
57 | - A function from `A` to a subtype of `B` is ok because it's result type will have all the properties of `B` that we might depend on. This indicates that functions are covariant in their result type.
58 |
59 | - A function expecting a supertype of `A` is also ok, because the `A` we have in the Box will have all the properties that the function expects.
60 |
61 | - A function expecting a subtype of `A` is not ok, because our value may in reality be a different subtype of `A`.
62 |
63 | ## Methods on Functions
64 |
65 | As functions are instances of a `Function` class we might ask what methods we get from the class apart from the `apply` method we define. The answer is not much that we'll use very often, but there are few that are useful to know about:
66 |
67 | - for functions of a single parameters we can compose them together to apply one function to the result of another
68 |
69 | ```tut:book
70 | val f = ((x: Int) => x + 1).compose((x: Int) => x * 2)
71 |
72 | f(2)
73 |
74 | val f = ((x: Int) => x + 1).andThen((x: Int) => x * 2)
75 |
76 | f(2)
77 | ```
78 |
79 | - for functions with two or more parameters we can convert them to functions that accepts a tuple of arguments, or a "curried" function which takes a single argument and returns a function.
80 |
81 | ```tut:book
82 | val f = ((x: Int, y: Int) => x + y).curried
83 |
84 | f(1)
85 |
86 | f(1)(2)
87 |
88 | val f = ((x: Int, y: Int) => x + y).tupled
89 |
90 | f( (1, 2) )
91 | ```
92 |
93 | ## Exercises
94 |
95 | #### Covariance and Contravariance
96 |
97 | ```tut:invisible
98 | object catExample {
99 | trait Animal
100 | trait Cat extends Animal { val color: String; val food: String }
101 | object Cat { def apply(aColor: String, aFood: String) = new Cat { val color = aColor; val food = aFood } }
102 | trait Siamese extends Cat
103 |
104 | trait Sound
105 | trait CatSound extends Sound
106 | trait Purr extends Sound
107 | }
108 | import catExample._
109 | ```
110 |
111 | Using the notation `A <: B` to indicate `A` is a subtype of `B` and assuming:
112 |
113 | - `Siamese <: Cat <: Animal`; and
114 | - `Purr <: CatSound <: Sound`
115 |
116 | if I have a method
117 |
118 | ```tut:book:silent
119 | def groom(groomer: Cat => CatSound): CatSound = {
120 | val oswald = Cat("Black", "Cat food")
121 | groomer(oswald)
122 | }
123 | ```
124 |
125 | which of the following can I pass to `groom`?
126 |
127 | - A function of type `Animal => Purr`
128 | - A function of type `Siamese => Purr`
129 | - A function of type `Animal => Sound`
130 |
131 |
132 | The only function that will work is the the function of type `Animal => Purr`. The `Siamese => Purr` function will not work because the Oswald is a not a Siamese cat. The `Animal => Sound` function will not work because we require the return type is a `CatSound`.
133 |
134 |
--------------------------------------------------------------------------------
/src/pages/sequencing/index.md:
--------------------------------------------------------------------------------
1 | # Sequencing Computations
2 |
3 | In this section we're going to look at two more language features, *generics* and *functions*, and see some abstractions we can build using these features: *functors*, and *monads*.
4 |
5 | Our starting point is code that we developed in the previous section. We developed `IntList`, a list of integers, and wrote code like the following:
6 |
7 | ```tut:book:silent
8 | object wrapper {
9 | sealed trait IntList {
10 | def length: Int =
11 | this match {
12 | case End => 0
13 | case Pair(hd, tl) => 1 + tl.length
14 | }
15 | def double: IntList =
16 | this match {
17 | case End => End
18 | case Pair(hd, tl) => Pair(hd * 2, tl.double)
19 | }
20 | def product: Int =
21 | this match {
22 | case End => 1
23 | case Pair(hd, tl) => hd * tl.product
24 | }
25 | def sum: Int =
26 | this match {
27 | case End => 0
28 | case Pair(hd, tl) => hd + tl.sum
29 | }
30 | }
31 | case object End extends IntList
32 | final case class Pair(head: Int, tail: IntList) extends IntList
33 | }; import wrapper._
34 | ```
35 |
36 | There are two problems with this code. The first is that our list is restricted to storing `Int`s. The second problem is that here is a lot of repetition. The code has the same general structure, which is unsurprising given we're using our structural recursion pattern, and it would be nice to reduce the amount of duplication.
37 |
38 | We will address both problems in this section. For the former we will use generics to *abstract over types*, so we can create data that works with user specified types. For the latter we will use functions to *abstract over methods*, so we can reduce duplication in our code.
39 |
40 | As we work with these techniques we'll see some general patterns emerge. We'll name and investigate these patterns in more detail at the end of this section.
41 |
--------------------------------------------------------------------------------
/src/pages/sequencing/intlist.scala:
--------------------------------------------------------------------------------
1 | sealed trait IntList {
2 | def fold(end: Int, f: (Int, Int) => Int): Int =
3 | this match {
4 | case End => end
5 | case Pair(hd, tl) => f(hd, tl.fold(end, f))
6 | }
7 | def length: Int =
8 | fold(0, (_, tl) => 1 + tl)
9 | def product: Int =
10 | fold(1, (hd, tl) => hd * tl)
11 | def sum: Int =
12 | fold(0, (hd, tl) => hd + tl)
13 | }
14 | case object End extends IntList
15 | final case class Pair(head: Int, tail: IntList) extends IntList
16 |
17 | val example = Pair(1, Pair(2, Pair(3, End)))
18 |
19 | assert(example.length == 3)
20 | assert(example.tail.length == 2)
21 | assert(End.length == 0)
22 |
23 | assert(example.product == 6)
24 | assert(example.tail.product == 6)
25 | assert(End.product == 1)
26 |
27 | assert(example.sum == 6)
28 | assert(example.tail.sum == 5)
29 | assert(End.sum == 0)
30 |
31 | object GenericFold {
32 | sealed trait IntList {
33 | def fold[A](end: A, f: (Int, A) => A): A =
34 | this match {
35 | case End => end
36 | case Pair(hd, tl) => f(hd, tl.fold(end, f))
37 | }
38 | def length: Int =
39 | fold[Int](0, (_, tl) => 1 + tl)
40 | def product: Int =
41 | fold[Int](1, (hd, tl) => hd * tl)
42 | def sum: Int =
43 | fold[Int](0, (hd, tl) => hd + tl)
44 | def double: IntList =
45 | fold[IntList](End, (hd, tl) => Pair(hd * 2, tl))
46 | }
47 | case object End extends IntList
48 | final case class Pair(head: Int, tail: IntList) extends IntList
49 | }
50 |
--------------------------------------------------------------------------------
/src/pages/sequencing/linkedlist.scala:
--------------------------------------------------------------------------------
1 | sealed trait LinkedList[A] {
2 | def length: Int =
3 | this match {
4 | case Pair(hd, tl) => 1 + tl.length
5 | case End() => 0
6 | }
7 | def contains(item: A): Boolean =
8 | this match {
9 | case Pair(hd, tl) =>
10 | if(hd == item)
11 | true
12 | else
13 | tl.contains(item)
14 | case End() => false
15 | }
16 | def apply(index: Int): A =
17 | this match {
18 | case Pair(hd, tl) =>
19 | if(index == 0)
20 | hd
21 | else
22 | tl(index - 1)
23 | case End() =>
24 | throw new Exception("Attempted to get element from an Empty list")
25 | }
26 | }
27 | final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
28 | final case class End[A]() extends LinkedList[A]
29 |
30 | val example = Pair(1, Pair(2, Pair(3, End())))
31 | assert(example.length == 3)
32 | assert(example.tail.length == 2)
33 | assert(End().length == 0)
34 |
35 | assert(example.contains(3) == true)
36 | assert(example.contains(4) == false)
37 | assert(example.contains(5) == false)
38 | assert(End().contains(0) == false)
39 |
40 | assert(example(0) == 1)
41 | assert(example(1) == 2)
42 | assert(example(2) == 3)
43 | assert(try {
44 | example(3)
45 | false
46 | } catch {
47 | case e: Exception => true
48 | })
49 |
50 | object SafeIndex {
51 | sealed trait Result[A]
52 | case class Success[A](result: A) extends Result[A]
53 | case class Failure[A](reason: String) extends Result[A]
54 |
55 | sealed trait LinkedList[A] {
56 | def apply(index: Int): Result[A] =
57 | this match {
58 | case Pair(hd, tl) =>
59 | if(index == 0)
60 | Success(hd)
61 | else
62 | tl(index - 1)
63 | case End() =>
64 | Failure("Index out of bounds")
65 | }
66 | }
67 | final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
68 | final case class End[A]() extends LinkedList[A]
69 |
70 | val example = Pair(1, Pair(2, Pair(3, End())))
71 | assert(example(0) == Success(1))
72 | assert(example(1) == Success(2))
73 | assert(example(2) == Success(3))
74 | assert(example(3) == Failure("Index out of bounds"))
75 | }
76 |
77 | object Fold {
78 | sealed trait LinkedList[A] {
79 | def fold[B](end: B, f: (A, B) => B): B =
80 | this match {
81 | case End() => end
82 | case Pair(hd, tl) => f(hd, tl.fold(end, f))
83 | }
84 | }
85 | final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
86 | final case class End[A]() extends LinkedList[A]
87 | }
88 |
--------------------------------------------------------------------------------
/src/pages/sequencing/maybe.scala:
--------------------------------------------------------------------------------
1 | object Invariant {
2 | sealed trait Maybe[A] {
3 | def flatMap[B](fn: A => Maybe[B]): Maybe[B] =
4 | this match {
5 | case Full(v) => fn(v)
6 | case Empty() => Empty[B]()
7 | }
8 | def map[B](fn: A => B): Maybe[B] =
9 | this match {
10 | case Full(v) => Full(fn(v))
11 | case Empty() => Empty[B]()
12 | }
13 | }
14 | final case class Full[A](value: A) extends Maybe[A]
15 | final case class Empty[A]() extends Maybe[A]
16 | }
17 |
18 | object Covariant {
19 | sealed trait Maybe[+A] {
20 | def flatMap[B](fn: A => Maybe[B]): Maybe[B] =
21 | this match {
22 | case Full(v) => fn(v)
23 | case Empty => Empty
24 | }
25 | def map[B](fn: A => B): Maybe[B] =
26 | this match {
27 | case Full(v) => Full(fn(v))
28 | case Empty => Empty
29 | }
30 | }
31 | final case class Full[A](value: A) extends Maybe[A]
32 | case object Empty extends Maybe[Nothing]
33 | }
34 |
--------------------------------------------------------------------------------
/src/pages/sequencing/sum.scala:
--------------------------------------------------------------------------------
1 | object Invariant {
2 | sealed trait Sum[A, B] {
3 | def fold[C](error: A => C, success: B => C): C =
4 | this match {
5 | case Failure(v) => error(v)
6 | case Success(v) => success(v)
7 | }
8 | def map[C](f: B => C): Sum[A, C] =
9 | this match {
10 | case Failure(v) => Failure(v)
11 | case Success(v) => Success(f(v))
12 | }
13 | def flatMap[C](f: B => Sum[A, C]) =
14 | this match {
15 | case Failure(v) => Failure(v)
16 | case Success(v) => f(v)
17 | }
18 | }
19 | final case class Failure[A, B](value: A) extends Sum[A, B]
20 | final case class Success[A, B](value: B) extends Sum[A, B]
21 |
22 | }
23 |
24 | object Covariant {
25 | sealed trait Sum[+A, +B] {
26 | def fold[C](error: A => C, success: B => C): C =
27 | this match {
28 | case Failure(v) => error(v)
29 | case Success(v) => success(v)
30 | }
31 | def map[C](f: B => C): Sum[A, C] =
32 | this match {
33 | case Failure(v) => Failure(v)
34 | case Success(v) => Success(f(v))
35 | }
36 | def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] =
37 | this match {
38 | case Failure(v) => Failure(v)
39 | case Success(v) => f(v)
40 | }
41 | }
42 | final case class Failure[A](value: A) extends Sum[A, Nothing]
43 | final case class Success[B](value: B) extends Sum[Nothing, B]
44 | }
45 |
--------------------------------------------------------------------------------
/src/pages/sequencing/tree.md:
--------------------------------------------------------------------------------
1 | sealed trait Tree[A] {
2 | def fold[B](node: (B, B) => B, leaf: A => B): B
3 | }
4 | final case class Node[A](left: Tree[A], right: Tree[A]) extends Tree[A] {
5 | def fold[B](node: (B, B) => B, leaf: A => B): B =
6 | node(left.fold(node, leaf), right.fold(node, leaf))
7 | }
8 | final case class Leaf[A](value: A) extends Tree[A] {
9 | def fold[B](node: (B, B) => B, leaf: A => B): B =
10 | leaf(value)
11 | }
12 |
13 | val tree: Tree[String] =
14 | Node(Node(Leaf("To"), Leaf("iterate")),
15 | Node(Node(Leaf("is"), Leaf("human,")),
16 | Node(Leaf("to"), Node(Leaf("recurse"), Leaf("divine")))))
17 |
18 | tree.fold[String]((a, b) => a + " " + b, str => str)
19 |
--------------------------------------------------------------------------------
/src/pages/solutions.md:
--------------------------------------------------------------------------------
1 | # Solutions to Exercises {#solutions}
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/pages/start-of-appendix.md:
--------------------------------------------------------------------------------
1 | \appendix
2 |
--------------------------------------------------------------------------------
/src/pages/thanks.md:
--------------------------------------------------------------------------------
1 | ## Thanks #{-}
2 |
3 | Many thanks to Richard Dallway and Jonathan Ferguson,
4 | who took on the herculean task of proof reading our early drafts
5 | and helped develop the rendering pipeline that produces the finished book.
6 |
7 | Thanks also to Danielle Ashley, who updated all of the code samples
8 | to use [Tut][link-tut] and increased our code quality 100% overnight!
9 |
10 | Thanks also to
11 | Amir Aryanpour,
12 | Audrey Welsh,
13 | Daniel Watford,
14 | Jason Scott,
15 | Joe Halliwell,
16 | Jon Pearce,
17 | Konstantine Gadyrka,
18 | N. Sriram,
19 | Rebecca Grenier,
20 | and Raffael Dzikowski,
21 | who sent us corrections and suggestions
22 | while the book was in early access.
23 | Knowing that our work was being used
24 | made the long haul of writing worthwhile.
25 |
--------------------------------------------------------------------------------
/src/pages/traits/calculation.scala:
--------------------------------------------------------------------------------
1 | sealed trait Calculation
2 | final case class Success(result: Int) extends Calculation
3 | final case class Failure(reason: String) extends Calculation
4 |
5 | object Calculator {
6 | def +(calc: Calculation, operand: Int): Calculation =
7 | calc match {
8 | case Success(result) => Success(result + operand)
9 | case Failure(reason) => Failure(reason)
10 | }
11 | def -(calc: Calculation, operand: Int): Calculation =
12 | calc match {
13 | case Success(result) => Success(result - operand)
14 | case Failure(reason) => Failure(reason)
15 | }
16 | def /(calc: Calculation, operand: Int): Calculation =
17 | calc match {
18 | case Success(result) =>
19 | operand match {
20 | case 0 => Failure("Division by zero")
21 | case _ => Success(result / operand)
22 | }
23 | case Failure(reason) => Failure(reason)
24 | }
25 | }
26 |
27 | assert(Calculator./(Success(4), 2) == Success(2))
28 | assert(Calculator.+(Success(1), 1) == Success(2))
29 | assert(Calculator.-(Success(1), 1) == Success(0))
30 | assert(Calculator.+(Failure("Badness"), 1) == Failure("Badness"))
31 | assert(Calculator./(Success(4), 0) == Failure("Division by zero"))
32 | assert(Calculator./(Failure("Badness"), 0) == Failure("Badness"))
33 |
--------------------------------------------------------------------------------
/src/pages/traits/calculator.scala:
--------------------------------------------------------------------------------
1 | sealed trait Expression
2 | final case class Addition(left: Expression, right: Expression) extends Expression
3 | final case class Subtraction(left: Expression, right: Expression) extends Expression
4 | final case class Number(value: Int) extends Expression
5 |
6 | sealed trait Expression {
7 | def eval: Double =
8 | this match {
9 | case Addition(l, r) => l.eval + r.eval
10 | case Subtraction(l, r) => l.eval - r.eval
11 | case Number(v) => v
12 | }
13 | }
14 | final case class Addition(left: Expression, right: Expression) extends Expression
15 | final case class Subtraction(left: Expression, right: Expression) extends Expression
16 | final case class Number(value: Int) extends Expression
17 |
18 |
19 | sealed trait Expression
20 | final case class Addition(left: Expression, right: Expression) extends Expression
21 | final case class Subtraction(left: Expression, right: Expression) extends Expression
22 | final case class Division(left: Expression, right: Expression) extends Expression
23 | final case class SquareRoot(value: Expression) extends Expression
24 | final case class Number(value: Int) extends Expression
25 |
26 | sealed trait Calculation
27 | final case class Success(result: Double) extends Calculation
28 | final case class Failure(reason: String) extends Calculation
29 |
30 | sealed trait Expression {
31 | def eval: Calculation =
32 | this match {
33 | case Addition(l, r) =>
34 | l.eval match {
35 | case Failure(reason) => Failure(reason)
36 | case Success(r1) =>
37 | r.eval match {
38 | case Failure(reason) => Failure(reason)
39 | case Success(r2) => Success(r1 + r2)
40 | }
41 | }
42 | case Subtraction(l, r) =>
43 | l.eval match {
44 | case Failure(reason) => Failure(reason)
45 | case Success(r1) =>
46 | r.eval match {
47 | case Failure(reason) => Failure(reason)
48 | case Success(r2) => Success(r1 - r2)
49 | }
50 | }
51 | case Division(l, r) =>
52 | l.eval match {
53 | case Failure(reason) => Failure(reason)
54 | case Success(r1) =>
55 | r.eval match {
56 | case Failure(reason) => Failure(reason)
57 | case Success(r2) =>
58 | if(r2 == 0)
59 | Failure("Division by zero")
60 | else
61 | Success(r1 / r2)
62 | }
63 | }
64 | case SquareRoot(v) =>
65 | v.eval match {
66 | case Success(r) =>
67 | if(r < 0)
68 | Failure("Square root of negative number")
69 | else
70 | Success(Math.sqrt(r))
71 | case Failure(reason) => Failure(reason)
72 | }
73 | case Number(v) => Success(v)
74 | }
75 | }
76 | final case class Addition(left: Expression, right: Expression) extends Expression
77 | final case class Subtraction(left: Expression, right: Expression) extends Expression
78 | final case class Division(left: Expression, right: Expression) extends Expression
79 | final case class SquareRoot(value: Expression) extends Expression
80 | final case class Number(value: Int) extends Expression
81 |
82 | assert(Addition(SquareRoot(Number(4)), Number(2)).eval == Success(4.0))
83 | assert(Addition(SquareRoot(Number(-1)), Number(2)).eval ==
84 | Failure("Square root of negative number"))
85 | assert(Division(Number(4), Number(0)).eval == Failure("Division by zero"))
86 |
--------------------------------------------------------------------------------
/src/pages/traits/conclusions.md:
--------------------------------------------------------------------------------
1 | ## Conclusions
2 |
3 | In this chapter we have made an extremely important change in our focus, away from language features and towards the programming patterns they support. This continues for the rest of the book.
4 |
5 | We have explored two extremely important patterns: *algebraic data types* and *structural recursion*. These patterns allow us to go from a mental model of data, to the representation and processing of that data in Scala in an almost entirely mechanical way. Not only in the structure of our code formulaic, and thus easy to comprehend, but the compiler can catch common errors for us which makes development and maintenance easier. These two tools are among the most commonly used in idiomatic functional code, and it is hard to over-emphasize their importance.
6 |
7 | In the exercises we developed a few common data structures, but we were limited to storing a fixed type of data, and our code contained a lot of repetition. In the next section we will look at how we can abstract over types and methods, and introduce some important concepts of sequencing operations.
8 |
--------------------------------------------------------------------------------
/src/pages/traits/feline.scala:
--------------------------------------------------------------------------------
1 | sealed trait Food
2 | case object Antelope extends Food
3 | case object TigerFood extends Food
4 | case object Licorice extends Food
5 | final case class CatFood(food: String) extends Food
6 |
7 | sealed trait Feline {
8 | def dinner: Food =
9 | this match {
10 | case Lion() => Antelope
11 | case Tiger() => TigerFood
12 | case Panther() => Licorice
13 | case Cat(favouriteFood) => CatFood(favouriteFood)
14 | }
15 | }
16 | final case class Lion() extends Feline {
17 | }
18 | final case class Tiger() extends Feline {
19 | }
20 | final case class Panther() extends Feline {
21 | }
22 | final case class Cat(favouriteFood: String) extends Feline {
23 | }
24 |
25 | object Diner {
26 | def dinner(feline: Feline): Food =
27 | feline match {
28 | case Lion() => Antelope
29 | case Tiger() => TigerFood
30 | case Panther() => Licorice
31 | case Cat(food) => CatFood(food)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/pages/traits/index.md:
--------------------------------------------------------------------------------
1 | # Modelling Data with Traits
2 |
3 | We looked in depth at classes in the previous chapter. Classes provide us with a way to abstract over objects that have similar properties, allowing us to write code that works with any object in a class.
4 |
5 | In this chapter we explore *abstraction over classes*, allowing us to write code that works with objects of different classes. We achieve this with a mechanism called *traits*.
6 |
7 | This chapter also marks a change in our focus. In previous chapters we have addressed the technical aspects of constructing Scala code. In this chapter we will initially focus on the technical aspects of traits. Our focus will then change to using Scala as a *medium to express our thoughts*.
8 |
9 | We will see how we can mechanically transform a description of data, called an *algebraic datatype*, into code. Using *structural recursion* we can mechanically write code that transforms an algebraic datatype.
10 |
--------------------------------------------------------------------------------
/src/pages/traits/intlist.scala:
--------------------------------------------------------------------------------
1 | sealed trait IntList {
2 | def length: Int =
3 | this match {
4 | case End => 0
5 | case Pair(hd, tl) => 1 + tl.length
6 | }
7 | def double: IntList =
8 | this match {
9 | case End => End
10 | case Pair(hd, tl) => Pair(hd * 2, tl.double)
11 | }
12 | def product: Int =
13 | this match {
14 | case End => 1
15 | case Pair(hd, tl) => hd * tl.product
16 | }
17 | def sum: Int =
18 | this match {
19 | case End => 0
20 | case Pair(hd, tl) => hd + tl.sum
21 | }
22 | }
23 | case object End extends IntList
24 | final case class Pair(head: Int, tail: IntList) extends IntList
25 |
26 | val example = Pair(1, Pair(2, Pair(3, End)))
27 |
28 | assert(example.length == 3)
29 | assert(example.tail.length == 2)
30 | assert(End.length == 0)
31 |
32 | assert(example.product == 6)
33 | assert(example.tail.product == 6)
34 | assert(End.product == 1)
35 |
36 | assert(example.sum == 6)
37 | assert(example.tail.sum == 5)
38 | assert(End.sum == 0)
39 |
40 | assert(example.double == Pair(2, Pair(4, Pair(6, End))))
41 | assert(example.tail.double == Pair(4, Pair(6, End)))
42 | assert(End.double == End)
43 |
--------------------------------------------------------------------------------
/src/pages/traits/json.scala:
--------------------------------------------------------------------------------
1 | sealed trait Json {
2 | def print: String = {
3 | def quote(s: String): String =
4 | '"'.toString ++ s ++ '"'.toString
5 | def seqToJson(seq: SeqCell): String =
6 | seq match {
7 | case SeqCell(h, t @ SeqCell(_, _)) =>
8 | s"${h.print}, ${seqToJson(t)}"
9 | case SeqCell(h, SeqEnd) => h.print
10 | }
11 |
12 | def objectToJson(obj: ObjectCell): String =
13 | obj match {
14 | case ObjectCell(k, v, t @ ObjectCell(_, _, _)) =>
15 | s"${quote(k)}: ${v.print}, ${objectToJson(t)}"
16 | case ObjectCell(k, v, ObjectEnd) =>
17 | s"${quote(k)}: ${v.print}"
18 | }
19 |
20 | this match {
21 | case JsNumber(v) => v.toString
22 | case JsString(v) => quote(v)
23 | case JsBoolean(v) => v.toString
24 | case JsNull => "null"
25 | case s @ SeqCell(_, _) => "[" ++ seqToJson(s) ++ "]"
26 | case SeqEnd => "[]"
27 | case o @ ObjectCell(_, _, _) => "{" ++ objectToJson(o) ++ "}"
28 | case ObjectEnd => "{}"
29 | }
30 | }
31 | }
32 | final case class JsNumber(value: Double) extends Json
33 | final case class JsString(value: String) extends Json
34 | final case class JsBoolean(value: Boolean) extends Json
35 | case object JsNull extends Json
36 | sealed trait JsSequence extends Json
37 | final case class SeqCell(head: Json, tail: JsSequence) extends JsSequence
38 | case object SeqEnd extends JsSequence
39 | sealed trait JsObject extends Json
40 | final case class ObjectCell(key: String, value: Json, tail: JsObject) extends JsObject
41 | case object ObjectEnd extends JsObject
42 |
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.sketch/Data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/traits/linked-list.sketch/Data
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.sketch/QuickLook/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/traits/linked-list.sketch/QuickLook/Preview.png
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.sketch/QuickLook/Thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underscoreio/essential-scala/32a54fbdc3a773a4b5cdfdc64e36057459493edf/src/pages/traits/linked-list.sketch/QuickLook/Thumbnail.png
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.sketch/metadata:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | app
6 | com.bohemiancoding.sketch
7 | build
8 | 5355
9 | commit
10 | b7d299b0a34651d1a0e066786b75aa36168d5809
11 | fonts
12 |
13 | OpenSans
14 | OpenSans
15 | OpenSans
16 | OpenSans
17 | OpenSans
18 | OpenSans
19 | OpenSans
20 | OpenSans
21 | OpenSans
22 | OpenSans
23 | OpenSans
24 | OpenSans
25 | OpenSans
26 |
27 | length
28 | 35485
29 | version
30 | 18
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.sketch/version:
--------------------------------------------------------------------------------
1 | 18
--------------------------------------------------------------------------------
/src/pages/traits/linked-list.svg:
--------------------------------------------------------------------------------
1 |
2 |
54 |
--------------------------------------------------------------------------------
/src/pages/traits/modelling-data-with-traits.md:
--------------------------------------------------------------------------------
1 | ## Modelling Data with Traits
2 |
3 | In this section we're going to shift our focus from language features to programming patterns. We're going to look at modelling data and learn a process for expressing in Scala any data model defined in terms of *logical ors and ands*. Using the terminology of object-oriented programming, we will express *is-a* and *has-a* relationships. In the terminology of functional programming we are learning about *sum* and *product types*, which are together called *algebraic data types*.
4 |
5 | Our goal in this section is to see how to translate a data model into Scala code. In the next section we'll see patterns for code that uses algebraic data types.
6 |
7 | ### The Product Type Pattern
8 |
9 | Our first pattern is to model data that contains other data. We might describe this as "`A` *has a* `B` *and* `C`". For example, a `Cat` has a colour and a favourite food; a `Visitor` has an id and a creation date; and so on.
10 |
11 | The way we write this is to use a case class. We've already done this many times in exercises; now we're formalising the pattern.
12 |
13 |
14 | #### Product Type Pattern {-}
15 |
16 | If `A` has a `b` (with type `B`) and a `c` (with type `C`) write
17 |
18 | ```tut:invisible
19 | type A = Any
20 | type B = Any
21 | type C = Any
22 | type D = Any
23 | ```
24 |
25 | ```tut:book:silent
26 | case class A(b: B, c: C)
27 | ```
28 |
29 | or
30 |
31 | ```tut:book:silent
32 | trait A {
33 | def b: B
34 | def c: C
35 | }
36 | ```
37 |
38 |
39 | ## The Sum Type Pattern
40 |
41 | Our next pattern is to model data that is two or more distinct cases. We might describe this as "`A` *is a* `B` *or* `C`". For example, a `Feline` is a `Cat`, `Lion`, or `Tiger`; a `Visitor` is an `Anonymous` or `User`; and so on.
42 |
43 | We write this using the sealed trait / final case class pattern.
44 |
45 |
46 | #### Sum Type Pattern {-}
47 |
48 | If `A` is a `B` or `C` write
49 |
50 | ```tut:book:silent
51 | sealed trait A
52 | final case class B() extends A
53 | final case class C() extends A
54 | ```
55 |
56 |
57 | ### Algebraic Data Types
58 |
59 | An algebraic data type is any data that uses the above two patterns. In the functional programming literature, data using the "has-a and" pattern is known as a *product type*, and the "is-a or" pattern is a *sum type*.
60 |
61 | ### The Missing Patterns
62 |
63 | We have looked at relationships along two dimensions: is-a/has-a, and and/or. We can draw up a little table and see we only have patterns for two of the four table cells.
64 |
65 | +-----------+--------------+----------+
66 | | | And | Or |
67 | +===========+==============+==========+
68 | | **Is-a** | | Sum type |
69 | +-----------+--------------+----------+
70 | | **Has-a** | Product type | |
71 | +-----------+--------------+----------+
72 |
73 |
74 |
75 | What about the missing two patterns?
76 |
77 | The "is-a and" pattern means that `A` is a `B` and `C`. This pattern is in some ways the inverse of the sum type pattern, and we can implement it as
78 |
79 | ```tut:book:silent
80 | trait B
81 | trait C
82 | trait A extends B with C
83 | ```
84 |
85 | In Scala a trait can extend as many traits as we like using the `with` keyword like `A extends B with C with D` and so on. We aren't going to use this pattern in this course. If we want to represent that some data conforms to a number of different interfaces we will often be better off using a *type class*, which we will explore later. There are, however, several legitimate uses of this pattern:
86 |
87 | - for modularity, using what's known as the [cake pattern](http://jonasboner.com/real-world-scala-dependency-injection-di/); and
88 | - sharing implementation across several classes where it doesn't make sense to make default implementations in the main trait.
89 |
90 | The "has-a or" patterns means that `A` has a `B` or `C`. There are two ways we can implement this. We can say that `A` has a `d` of type `D`, where `D` is a `B` or `C`. We can mechanically apply our two patterns to implement this:
91 |
92 | ```tut:book:silent
93 | trait A {
94 | def d: D
95 | }
96 | sealed trait D
97 | final case class B() extends D
98 | final case class C() extends D
99 | ```
100 |
101 | Alternatively we could implement this as `A` is a `D` or `E`, and `D` has a `B` and `E` has a `C`. Again this translates directly into code
102 |
103 | ```tut:book:silent
104 | sealed trait A
105 | final case class D(b: B) extends A
106 | final case class E(c: C) extends A
107 | ```
108 |
109 | ### Take Home Points
110 |
111 | We have seen that we can mechanically translate data using the "has-a and" and "is-a or" patterns (or, more succinctly, the product and sum types) into Scala code. This type of data is known as an algebraic data type. Understanding these patterns is very important for writing idiomatic Scala code.
112 |
113 | ### Exercises
114 |
115 | #### Stop on a Dime
116 |
117 | A traffic light is red, green, or yellow. Translate this description into Scala code.
118 |
119 |
120 | This is a direct application of the sum type pattern.
121 |
122 | ```tut:book:silent
123 | sealed trait TrafficLight
124 | case object Red extends TrafficLight
125 | case object Green extends TrafficLight
126 | case object Yellow extends TrafficLight
127 | ```
128 |
129 | As there are no fields or methods on the three cases, and thus there is no need to create more than one instance of them, I used case objects instead of case classes.
130 |
131 |
132 | #### Calculator
133 |
134 | A calculation may succeed (with an `Int` result) or fail (with a `String` message). Implement this.
135 |
136 |
137 | ```tut:book:silent
138 | sealed trait Calculation
139 | final case class Success(result: Int) extends Calculation
140 | final case class Failure(reason: String) extends Calculation
141 | ```
142 |
143 |
144 | #### Water, Water, Everywhere
145 |
146 | Bottled water has a size (an `Int`), a source (which is a well, spring, or tap), and a `Boolean` carbonated. Implement this in Scala.
147 |
148 |
149 | Crank the handle on the product and sum type patterns.
150 |
151 | ```tut:book:silent
152 | sealed trait Source
153 | case object Well extends Source
154 | case object Spring extends Source
155 | case object Tap extends Source
156 | final case class BottledWater(size: Int, source: Source, carbonated: Boolean)
157 | ```
158 |
159 |
--------------------------------------------------------------------------------
/src/pages/traits/structural-recursion.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Structural Recursion
4 | ---
5 |
6 | In the previous section we saw that traits allow us to define data representing a logical or. We also saw that traits define an interface that extending classes must implement.
7 |
8 | If we are using the logical or pattern, it indicates we want to define different data types that have something in common---the interface defined by the trait---but also something different. After all, if there wasn't a difference between the data types we wouldn't bother defining them.
9 |
10 | Concretely, consider the `Visitor` type from the previous section. We have two different cases: `User` for logged in users, and `Anonymous`. Now imagine we want to send an email to visitors. We can send an email to users, but not to anonymous visitors. At the moment we don't know how to implement this.
11 |
12 | How can we do different things for different data types that belong to a common trait? In this section we'll see two ways to do this: *polymorphism* and *pattern matching*.
13 |
14 | ## Polymorphism
15 |
16 | ## Pattern Matching
17 |
--------------------------------------------------------------------------------
/src/pages/traits/traits-as-mixins.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: "This and That: Traits as Mixins"
4 | ---
5 |
6 | In the previous section we looked at categorising objects using a single set of traits. In this section we will look at objects that can simultaneously be categorised using several type hierarchies. In functional programming these are known as [product types][link-product-types]. In object oriented programming there is a related concept: [multiple inheritance][link-multiple-inheritance].
7 |
8 | ## Traits as Mixins
9 |
10 | We achieve composition in Scala by separating each component into its own trait and **mixing the traits we want to use** together to form classes.
11 |
12 | The syntax is to write `extends` for the first trait and `with` for each following trait: `A extends B with C with D`. For example, imagine the following model of staff and students at a university:
13 |
14 | ```tut:invisible
15 | trait Course
16 | ```
17 |
18 | ```tut:book:silent
19 | trait Person {
20 | def firstName: String
21 | def lastName: String
22 | }
23 |
24 | trait Student extends Person {
25 | def studentNumber: String
26 | def coursesTaken: Seq[Course] // a list of courses
27 | }
28 |
29 | trait Staff extends Person {
30 | def staffNumber: String
31 | def coursesTaught: Seq[Course] // a list of courses
32 | }
33 |
34 | trait TeachingAssistant extends Staff with Student
35 | ```
36 |
37 | In this example, `TeachingAssistent` ends up with all of the methods from `Person`, `Staff`, and `Student`.
38 |
39 | ## Is-a vs Has-a
40 |
41 | A trait or class is a subtype of every trait it extends. This means that if `A extends B`, `A` **is a** `B` and may be used wherever a `B` is expected. A `TeachingAssistant` is a `Staff` member, a `Student`, and a `Person` and can be treated as any of these types.
42 |
43 | Don't confuse an is-a relationship with a **has a** relationship. A book has a publisher but is not a publisher itself, so we would not mixin a `Publisher` trait to a `Book`.
44 |
45 | ## Overriding and Super Calls
46 |
47 | Traits and classes can mofidy the fields and methods they inherit from supertypes. We can use the `override` keyword to redefine an existing field or method, and use the `super` keyword to refer to the original definition that we are overriding. For example:
48 |
49 | ```tut:book:silent
50 | trait Person {
51 | def firstName: String
52 | def lastName: String
53 | def name = s"$firstName $lastName"
54 | }
55 |
56 | trait Veteran extends Person {
57 | def rank: String
58 | override def name = s"$rank ${super.name}"
59 | }
60 | ```
61 |
62 | ## Trait Linearization
63 |
64 | Overriding can lead to interesting interactions if we have many traits that each override a particular method. Consider the following code---what happens when we call `foo` on an instance of `Example`?
65 |
66 | ```tut:book:silent
67 | trait Fooable { def foo: String }
68 |
69 | trait A extends Fooable { override def foo: String = "A" }
70 | trait B extends Fooable { override def foo: String = "B" }
71 | trait C extends Fooable { override def foo: String = "C" }
72 |
73 | case class Example() extends A with B with C
74 | ```
75 |
76 | The simple way to tell is to ask the REPL:
77 |
78 | ```
79 | scala> Example().foo
80 | res1: String = C
81 | ```
82 |
83 | Ambiguity between traits is resolved using **linearization**. They are effectively stacked on top of one another and any method call is resolved by searchin up the stack until a matching definition is found. In the example the search order imposed by linearization is `Example -> C -> B -> A -> AnyRef -> Any` (extending `AnyRef` is implicit when you don't explicitly `extend` anything else).
84 |
85 | Linearization enables us to come up with all sorts of byzantine designs where traits add pieces of behaviour to a few common methods. The simple rule of designing with linearization is: don't. **If you depend on the order in which traits are stacked, you are doing something wrong**---it is a sure way to introduce bugs into your code.
86 |
87 | ## Take home points
88 |
89 | We can model **multiple inheritance** in Scala by **mixing traits** with one another using the `with` keyword.
90 |
91 | Traits solve the method resolution problems of multiple inheritance by defining a **linearization order** that dictates the order of overriding. The linearization order is tied to the order of traits in the header of a class.
92 |
93 | **If you find yourself relying on the linearization order in your type heirarchy, stop!** That way madness lies.
94 |
95 | Multiple inheritance is one way (but not the only way) of modelling a **this and that** relationship between types. In functional programming, this is called a **product type**.
96 |
97 | We can also model product types using *generics*---we'll see these later.
98 |
99 | ## Exercises
100 |
101 | ### Perplexing publications
102 |
103 | Let's create a simple model for publisher data. Code a set of traits and classes according to the following description:
104 |
105 | - A *publication* is a *book* or a *periodical*.
106 |
107 | - A *book* has an *author* while a *periodical* has an *editor*.
108 |
109 | - *Periodicals* have many *issues*, each of which has a *volume* and an *issue number*.
110 |
111 | - A *manuscript* is a document of a certain *length* written by an *author*.
112 |
113 | - A *book* is a *manuscript*, but an *issue* of a periodical contains a sequence of *manuscripts*.
114 |
115 | Tip: a sequence of type `A` has type `Seq[A]`.
116 |
117 |
118 | This is as much a problem of parsing the problem description as it is writing code. However, looking at the final Scala code it is easy to verify that all of the initial requirements hold:
119 |
120 | ```tut:invisible
121 | trait Issue
122 | ```
123 |
124 | ```tut:book:silent
125 | trait Publication {
126 | def title: String
127 | }
128 |
129 | trait Manuscript {
130 | def title: String
131 | def length: Int
132 | def author: String
133 | }
134 |
135 | case class Book(
136 | val title: String,
137 | val author: String,
138 | val length: Int
139 | ) extends Publication with Manuscript
140 |
141 | case class Periodical(
142 | val title: String,
143 | val editor: String,
144 | val issues: Seq[Issue]
145 | ) extends Publication
146 |
147 | case class Issue(
148 | volume: Int,
149 | issue: Int,
150 | manuscripts: Seq[Manuscript]
151 | )
152 | ```
153 |
154 |
--------------------------------------------------------------------------------
/src/pages/traits/tree.scala:
--------------------------------------------------------------------------------
1 | sealed trait Tree {
2 | def sum: Int
3 | def double: Tree
4 | }
5 | final case class Node(l: Tree, r: Tree) extends Tree {
6 | def sum: Int =
7 | l.sum + r.sum
8 |
9 | def double: Tree =
10 | Node(l.double, r.double)
11 | }
12 | final case class Leaf(elt: Int) extends Tree {
13 | def sum: Int =
14 | elt
15 |
16 | def double: Tree =
17 | Leaf(elt * 2)
18 | }
19 |
20 | object TreeOps {
21 | def sum(tree: Tree): Int =
22 | tree match {
23 | case Leaf(elt) => elt
24 | case Node(l, r) => sum(l) + sum(r)
25 | }
26 |
27 | def double(tree: Tree): Tree =
28 | tree match {
29 | case Leaf(elt) => Leaf(elt * 2)
30 | case Node(l, r) => Node(double(l), double(r))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/template/cover-notes.html:
--------------------------------------------------------------------------------
1 |
Copies of this, and related topics, can be found at http://underscore.io/training. Team discounts, when available, may also be found at that address. Contact the author regarding this text at: hello@underscore.io.
2 |
3 |
Our courses, workshops, and other products can help you and your team create better software and have more fun. For more information, as well as the latest Underscore titles, please visit http://underscore.io/training.
4 |
5 |
6 |
7 |
Disclaimer: Every precaution was taken in the preparation of this book. However, the author and Underscore Consulting LLP assume no responsibility for errors or omissions, or for damages that may result from the use of information (including program listings) contained herein.
8 |
--------------------------------------------------------------------------------
/src/template/cover-notes.tex:
--------------------------------------------------------------------------------
1 | \begin{center}
2 |
3 | Copies of this, and related topics, can be found at \href{http://underscore.io/training}{http://underscore.io/training}.
4 |
5 | Team discounts, when available, may also be found at that address.
6 |
7 | Contact the author regarding this text at: \href{mailto:hello@underscore.io}{hello@underscore.io}.
8 |
9 | \vspace{3em}
10 |
11 | Our courses, workshops, and other products can help you and your team create better software and have more fun. For more information, as well as the latest Underscore titles, please visit \href{http://underscore.io/training}{http://underscore.io/training}.
12 |
13 | \vspace{3em}
14 |
15 | \textit{\textbf{Disclaimer:} Every precaution was taken in the preparation of this book. However, \textbf{the author and Underscore Consulting LLP assume no responsibility for errors or omissions, or for damages} that may result from the use of information (including program listings) contained herein.}
16 |
17 | \end{center}
18 |
--------------------------------------------------------------------------------