├── .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 | Creative Commons Licence
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 |
83 | ```tut:book 84 | val animals = Seq("cat", "dog", "penguin") 85 | ``` 86 |
87 | 88 | Append the element `"tyrannosaurus"` to `animals` and prepend the element `"mouse"`. 89 | 90 |
91 | ```tut:book 92 | "mouse" +: animals :+ "tyrannosaurus" 93 | ``` 94 |
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 |
58 | ```scala 59 | def addOne(in: List[Int]): List[Int] = 60 | in match { 61 | case Nil => Nil 62 | case (x :: xs) => (x + 1) :: addOne(xs) 63 | } 64 | ``` 65 |
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 |
147 | ```scala 148 | def derivative(f: Double => Double): Double => Double = { 149 | val h = 0.01 150 | (x: Double) => 151 | (f(x + h) - f(x - h)) / (2 * h) 152 | } 153 | ``` 154 |
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 |
159 | ```scala 160 | def makeDerivative(h: Double) = { 161 | val derivative = (f: Double => Double) => 162 | (x: Double) => 163 | (f(x + h) - f(x - h)) / (2 * h) 164 | derivative 165 | } 166 | ``` 167 |
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 |
172 | ```scala 173 | def solve(f: Double => Double, x: Double, tolerance: Double) = { 174 | def iterate(bracket: Double, lastGuess: Double): Double = { 175 | val guess = makeDerivative(bracket)(f)(x) 176 | if(math.abs(guess - lastGuess) < tolerance) 177 | lastGuess 178 | else 179 | iterate(bracket / 2, guess) 180 | } 181 | 182 | iterate(0.05, makeDerivative(0.1)(f)(x)) 183 | } 184 | ``` 185 |
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 |
52 | ```tut:book:silent 53 | object JsUtil { 54 | def toJson[A](value: A)(implicit writer: JsWriter[A]) = 55 | writer write value 56 | } 57 | ``` 58 |
59 | 60 | Now, let's revisit our data types from the web site visitors example in the [Sealed traits](/traits/sealed-traits.html) section: 61 | 62 | ```tut:book:silent 63 | import java.util.Date 64 | 65 | sealed trait Visitor { 66 | def id: String 67 | def createdAt: Date 68 | def age: Long = new Date().getTime() - createdAt.getTime() 69 | } 70 | 71 | final case class Anonymous( 72 | id: String, 73 | createdAt: Date = new Date() 74 | ) extends Visitor 75 | 76 | final case class User( 77 | id: String, 78 | email: String, 79 | createdAt: Date = new Date() 80 | ) extends Visitor 81 | ``` 82 | 83 | Write `JsWriter` instances for `Anonymous` and `User`. 84 | 85 |
86 | ```tut:book:silent 87 | implicit object AnonymousWriter extends JsWriter[Anonymous] { 88 | def write(value: Anonymous) = JsObject(Map( 89 | "id" -> JsString(value.id), 90 | "createdAt" -> JsString(value.createdAt.toString) 91 | )) 92 | } 93 | 94 | implicit object UserWriter extends JsWriter[User] { 95 | def write(value: User) = JsObject(Map( 96 | "id" -> JsString(value.id), 97 | "email" -> JsString(value.email), 98 | "createdAt" -> JsString(value.createdAt.toString) 99 | )) 100 | } 101 | ``` 102 |
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 |
122 | ```tut:book:silent 123 | visitors.map(visitor => JsUtil.toJson(visitor)) 124 | ``` 125 |
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 | 3 | linked-list 4 | Created with Sketch (http://www.bohemiancoding.com/sketch) 5 | 6 | 7 | 8 | 9 | 10 | a: Pair 11 | 12 | 13 | head 14 | 15 | 16 | tail 17 | 18 | 19 | head 20 | 21 | 22 | tail 23 | 24 | 25 | head 26 | 27 | 28 | tail 29 | 30 | 31 | 32 | 33 | b: Pair 34 | 35 | 36 | 37 | 38 | c: Pair 39 | 40 | 41 | d: End 42 | 43 | 44 | 1 45 | 46 | 47 | 2 48 | 49 | 50 | 3 51 | 52 | 53 | 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 | --------------------------------------------------------------------------------