├── .github ├── pr-labeler.yml ├── release-drafter.yml └── workflows │ ├── ci.yml │ ├── docs.yml │ ├── release-drafter.yml │ └── release.yml ├── .gitignore ├── .ruby-version ├── .scalafmt.conf ├── AUTHORS.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE.md ├── NOTICE.md ├── README.md ├── build.sbt ├── docs ├── AUTHORS.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md └── NOTICE.md ├── project ├── build.properties └── plugins.sbt └── src ├── main ├── resources │ ├── public │ │ └── scala_tutorial │ │ │ ├── animals.svg │ │ │ ├── music_sheet.png │ │ │ └── scala_type_hierarchy.png │ └── scala-tutorial.svg └── scala │ └── scalatutorial │ ├── ScalaTutorial.scala │ ├── sections │ ├── ClassesVsCaseClasses.scala │ ├── DefinitionsAndEvaluation.scala │ ├── FunctionalLoops.scala │ ├── HigherOrderFunctions.scala │ ├── ImperativeProgramming.scala │ ├── LazyEvaluation.scala │ ├── LexicalScopes.scala │ ├── ObjectOrientedProgramming.scala │ ├── PolymorphicTypes.scala │ ├── ScalaTutorialSection.scala │ ├── StandardLibrary.scala │ ├── StructuringInformation.scala │ ├── SyntacticConveniences.scala │ ├── TailRecursion.scala │ ├── TermsAndTypes.scala │ └── TypeClasses.scala │ └── utils │ ├── BankAccount.scala │ ├── IntSet.scala │ ├── Note.scala │ ├── Rational.scala │ ├── animals.scala │ └── sorting.scala └── test └── scala └── scalatutorial └── sections ├── ClassesVsCaseClassesSpec.scala ├── DefinitionsAndEvaluationSpec.scala ├── FunctionalLoopsSpec.scala ├── HigherOrderFunctionsSpec.scala ├── ImperativeProgrammingSpec.scala ├── LazyEvaluationSpec.scala ├── LexicalScopesSpec.scala ├── ObjectOrientedProgrammingSpec.scala ├── PolymorphicTypesSpec.scala ├── StandardLibrarySpec.scala ├── StructuringInformationSpec.scala ├── SyntacticConveniencesSpec.scala ├── TailRecursionSpec.scala ├── TermsAndTypesSpec.scala └── TypeClassesSpec.scala /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | enhancement: ['enhancement/*', 'feature/*'] 6 | documentation: ['docs/*', 'doc/*'] 7 | breaking-change: ['breaking/*', 'break/*'] 8 | bug: ['bug/*', 'fix/*'] 9 | tests: ['test/*', 'tests/*'] 10 | dependency-update: ['dep/*', 'dependency/*', 'dependency-update/*'] 11 | scala-steward: ['update/*'] 12 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | name-template: 'v$NEXT_PATCH_VERSION' 6 | tag-template: 'v$NEXT_PATCH_VERSION' 7 | exclude-labels: 8 | - 'auto-update' 9 | - 'auto-documentation' 10 | - 'auto-changelog' 11 | categories: 12 | - title: '⚠️ Breaking changes' 13 | label: 'breaking-change' 14 | - title: '🚀 Features' 15 | label: 'enhancement' 16 | - title: '📘 Documentation' 17 | label: 'documentation' 18 | - title: '🐛 Bug Fixes' 19 | label: 'bug' 20 | - title: '📈 Dependency updates' 21 | labels: 22 | - 'dependency-update' 23 | - 'scala-steward' 24 | template: | 25 | ## What's changed 26 | 27 | $CHANGES 28 | 29 | ## Contributors to this release 30 | 31 | $CONTRIBUTORS 32 | 33 | autolabeler: 34 | - label: "enhancement" 35 | branch: 36 | - '/enhancement\/.+/' 37 | - '/feature\/.+/' 38 | - label: "documentation" 39 | files: 40 | - "*.md" 41 | branch: 42 | - '/docs\/.+/' 43 | - '/doc\/.+/' 44 | - label: "breaking-change" 45 | branch: 46 | - '/breaking\/.+/' 47 | - '/break\/.+/' 48 | - label: "bug" 49 | branch: 50 | - '/bug\/.+/' 51 | - '/fix\/.+/' 52 | - label: "tests" 53 | branch: 54 | - '/test\/.+/' 55 | - '/tests\/.+/' 56 | - label: "dependency-update" 57 | branch: 58 | - '/dep\/.+/' 59 | - '/dependency\/.+/' 60 | - '/dependency-update\/.+/' 61 | - label: "scala-steward" 62 | branch: 63 | - '/update\/.+/' 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | name: Formatters & Tests 6 | 7 | on: 8 | push: 9 | branches: [main] 10 | pull_request: 11 | 12 | jobs: 13 | test: 14 | if: "!contains(github.event.head_commit.message, 'skip ci')" 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout project (pull-request) 18 | if: github.event_name == 'pull_request' 19 | uses: actions/checkout@v2.3.2 20 | with: 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | ref: ${{ github.event.pull_request.head.ref }} 23 | - name: Checkout project (main) 24 | if: github.event_name == 'push' 25 | uses: actions/checkout@v2 26 | - name: Setup Scala 27 | uses: olafurpg/setup-scala@v11 28 | with: 29 | java-version: adopt@1.11 30 | - name: Setup Ruby 31 | uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: .ruby-version 34 | - name: Setup yq 35 | run: sudo snap install yq 36 | - name: Run pre-conditions 37 | run: test -f .github/actions.yml && eval "$(yq e '.pre.ci // "true"' .github/actions.yml)" || true 38 | - name: Run scalafmt on Scala Steward PRs 39 | if: github.event.pull_request.user.login == '47erbot' && contains(github.event.pull_request.body, 'Scala Steward') 40 | run: sbt "scalafixEnable; fix" || sbt "scalafmtAll; scalafmtSbt" || true 41 | - name: Push changes 42 | uses: stefanzweifel/git-auto-commit-action@v4.5.1 43 | with: 44 | commit_message: Run formatter/linter 45 | - name: Run checks 46 | run: sbt ci-test 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.ADMIN_GITHUB_TOKEN }} 49 | - name: Run post-conditions 50 | run: test -f .github/actions.yml && eval "$(yq e '.post.ci // "true"' .github/actions.yml)" || true 51 | - name: Automerge Scala Steward PRs 52 | if: success() && github.event_name == 'pull_request' && contains(github.event.pull_request.body, 'Scala Steward') 53 | uses: ridedott/merge-me-action@v1.1.36 54 | with: 55 | GITHUB_LOGIN: 47erbot 56 | GITHUB_TOKEN: ${{ secrets.ADMIN_GITHUB_TOKEN }} 57 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | name: Update documentation 6 | 7 | on: 8 | release: 9 | types: [published] 10 | repository_dispatch: 11 | types: [docs] 12 | 13 | jobs: 14 | documentation: 15 | if: "!contains(github.event.head_commit.message, 'skip ci')" 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout project 19 | uses: actions/checkout@v2 20 | with: 21 | token: ${{ secrets.ADMIN_GITHUB_TOKEN }} 22 | ref: main 23 | - name: Fetch tags 24 | run: git fetch --tags 25 | - name: Setup Scala 26 | uses: olafurpg/setup-scala@v11 27 | with: 28 | java-version: adopt@1.11 29 | - name: Setup Ruby 30 | uses: ruby/setup-ruby@v1 31 | with: 32 | ruby-version: .ruby-version 33 | - name: Setup github-changelog-generator 34 | run: gem install github_changelog_generator -v 1.15.0 35 | - name: Setup yq 36 | run: sudo snap install yq 37 | - name: Run pre-conditions 38 | run: test -f .github/actions.yml && eval "$(yq e '.pre.docs // "true"' .github/actions.yml)" || true 39 | - name: Generate documentation 40 | run: sbt ci-docs 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | DOWNLOAD_INFO_FROM_GITHUB: true 44 | - name: Run post-conditions 45 | run: test -f .github/actions.yml && eval "$(yq e '.post.docs // "true"' .github/actions.yml)" || true 46 | - name: Push changes 47 | uses: stefanzweifel/git-auto-commit-action@v4.1.3 48 | with: 49 | commit_message: 'Update documentation, and other files [skip ci]' 50 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Drafts/updates the next repository release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v5 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.ADMIN_GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | name: Release 6 | 7 | on: 8 | release: 9 | types: [published] 10 | push: 11 | branches: main 12 | 13 | jobs: 14 | release: 15 | if: "!contains(github.event.head_commit.message, 'skip ci')" 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout project 19 | uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | - name: Fetch tags 23 | run: git fetch --tags 24 | - name: Setup Scala 25 | uses: olafurpg/setup-scala@v11 26 | with: 27 | java-version: adopt@1.11 28 | - name: Setup Ruby 29 | uses: ruby/setup-ruby@v1 30 | with: 31 | ruby-version: .ruby-version 32 | - name: Setup GPG 33 | uses: olafurpg/setup-gpg@v3 34 | - name: Setup yq 35 | run: sudo snap install yq 36 | - name: Run pre-conditions 37 | run: test -f .github/actions.yml && eval "$(yq e '.pre.release // "true"' .github/actions.yml)" || true 38 | - name: Release new version 39 | run: sbt ci-publish 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 43 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 44 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 45 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 46 | - name: Run post-conditions 47 | run: test -f .github/actions.yml && eval "$(yq e '.post.release // "true"' .github/actions.yml)" || true 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't edit this file! 2 | # It is automatically updated after every release of https://github.com/47degrees/.github 3 | # If you want to suggest a change, please open a PR or issue in that repository 4 | 5 | ### Intellij ### 6 | 7 | .idea 8 | out/ 9 | 10 | ### Java ### 11 | 12 | *.class 13 | *.log 14 | 15 | ### macOS ### 16 | 17 | .DS_Store 18 | 19 | ### SBT ### 20 | 21 | dist/* 22 | target/ 23 | lib_managed/ 24 | src_managed/ 25 | project/boot/ 26 | project/plugins/project/ 27 | .history 28 | .cache 29 | .lib/ 30 | .bsp 31 | 32 | ### Scala ### 33 | 34 | *.metals 35 | .bloop/ 36 | .metals/ 37 | metals.sbt 38 | .scala-build/ 39 | 40 | ### Vim ### 41 | 42 | # Swap 43 | [._]*.s[a-v][a-z] 44 | [._]*.sw[a-p] 45 | [._]s[a-rt-v][a-z] 46 | [._]ss[a-gi-z] 47 | [._]sw[a-p] 48 | 49 | # Session 50 | Session.vim 51 | Sessionx.vim 52 | 53 | # Temporary 54 | .netrwhist 55 | 56 | # Project local build artefacts 57 | .output 58 | 59 | # Auto-generated tag files 60 | tags 61 | 62 | # Persistent undo 63 | [._]*.un~ 64 | 65 | # Coc configuration directory 66 | .vim 67 | 68 | ### VisualStudioCode ### 69 | 70 | .vscode/ 71 | .vscode/* 72 | !.vscode/settings.json 73 | !.vscode/tasks.json 74 | !.vscode/launch.json 75 | !.vscode/extensions.json 76 | 77 | # Direnv 78 | 79 | .direnv 80 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version=3.7.3 2 | style = defaultWithAlign 3 | maxColumn = 100 4 | runner.dialect = scala213 5 | 6 | continuationIndent.callSite = 2 7 | 8 | newlines { 9 | sometimesBeforeColonInMethodReturnType = false 10 | } 11 | 12 | align { 13 | arrowEnumeratorGenerator = false 14 | ifWhileOpenParen = false 15 | openParenCallSite = false 16 | openParenDefnSite = false 17 | } 18 | 19 | docstrings.style = Asterisk 20 | 21 | rewrite { 22 | rules = [SortImports, RedundantBraces] 23 | redundantBraces.maxLines = 1 24 | } 25 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Authors 6 | 7 | ## Maintainers 8 | 9 | The maintainers of the project are: 10 | 11 | - [![47erbot](https://avatars1.githubusercontent.com/u/24799081?v=4&s=20) **47erbot**](https://github.com/47erbot) 12 | - [![AdrianRaFo](https://avatars0.githubusercontent.com/u/15971742?v=4&s=20) **Adrian Ramirez Fornell (AdrianRaFo)**](https://github.com/AdrianRaFo) 13 | - [![alejandrohdezma](https://avatars0.githubusercontent.com/u/9027541?v=4&s=20) **Alejandro Hernández (alejandrohdezma)**](https://github.com/alejandrohdezma) 14 | - [![angoglez](https://avatars0.githubusercontent.com/u/10107285?v=4&s=20) **Ana Gómez González (angoglez)**](https://github.com/angoglez) 15 | - [![BenFradet](https://avatars2.githubusercontent.com/u/1737211?v=4&s=20) **Ben Fradet (BenFradet)**](https://github.com/BenFradet) 16 | - [![dominv](https://avatars1.githubusercontent.com/u/3943031?v=4&s=20) **Domingo Valera (dominv)**](https://github.com/dominv) 17 | - [![kiroco12](https://avatars1.githubusercontent.com/u/48894338?v=4&s=20) **Enrique Nieto (kiroco12)**](https://github.com/kiroco12) 18 | - [![fedefernandez](https://avatars0.githubusercontent.com/u/720923?v=4&s=20) **Fede Fernández (fedefernandez)**](https://github.com/fedefernandez) 19 | - [![FRosner](https://avatars2.githubusercontent.com/u/3427394?v=4&s=20) **Frank Rosner (FRosner)**](https://github.com/FRosner) 20 | - [![jdesiloniz](https://avatars2.githubusercontent.com/u/2835739?v=4&s=20) **Javier de Silóniz Sandino (jdesiloniz)**](https://github.com/jdesiloniz) 21 | - [![juanpedromoreno](https://avatars2.githubusercontent.com/u/4879373?v=4&s=20) **Juan Pedro Moreno (juanpedromoreno)**](https://github.com/juanpedromoreno) 22 | - [![calvellido](https://avatars0.githubusercontent.com/u/7753447?v=4&s=20) **Juan Valencia (calvellido)**](https://github.com/calvellido) 23 | - [![julienrf](https://avatars2.githubusercontent.com/u/332812?v=4&s=20) **Julien Richard-Foy (julienrf)**](https://github.com/julienrf) 24 | - [![MaureenElsberry](https://avatars0.githubusercontent.com/u/17556002?v=4&s=20) **Maureen Elsberry (MaureenElsberry)**](https://github.com/MaureenElsberry) 25 | 26 | ## Contributors 27 | 28 | These are the people that have contributed to the _exercises-scalatutorial_ project: 29 | 30 | - [![julienrf](https://avatars2.githubusercontent.com/u/332812?v=4&s=20) **julienrf**](https://github.com/julienrf) 31 | - [![47erbot](https://avatars1.githubusercontent.com/u/24799081?v=4&s=20) **47erbot**](https://github.com/47erbot) 32 | - [![juanpedromoreno](https://avatars2.githubusercontent.com/u/4879373?v=4&s=20) **juanpedromoreno**](https://github.com/juanpedromoreno) 33 | - [![jdesiloniz](https://avatars2.githubusercontent.com/u/2835739?v=4&s=20) **jdesiloniz**](https://github.com/jdesiloniz) 34 | - [![kiroco12](https://avatars1.githubusercontent.com/u/48894338?v=4&s=20) **kiroco12**](https://github.com/kiroco12) 35 | - [![FRosner](https://avatars2.githubusercontent.com/u/3427394?v=4&s=20) **FRosner**](https://github.com/FRosner) 36 | - [![MaureenElsberry](https://avatars0.githubusercontent.com/u/17556002?v=4&s=20) **MaureenElsberry**](https://github.com/MaureenElsberry) 37 | - [![sauntimo](https://avatars3.githubusercontent.com/u/2720466?v=4&s=20) **sauntimo**](https://github.com/sauntimo) 38 | - [![xiaowei-zhang](https://avatars0.githubusercontent.com/u/19760649?v=4&s=20) **xiaowei-zhang**](https://github.com/xiaowei-zhang) 39 | - [![dominv](https://avatars1.githubusercontent.com/u/3943031?v=4&s=20) **dominv**](https://github.com/dominv) 40 | - [![AdrianRaFo](https://avatars0.githubusercontent.com/u/15971742?v=4&s=20) **AdrianRaFo**](https://github.com/AdrianRaFo) 41 | - [![CaileenD](https://avatars3.githubusercontent.com/u/9513625?v=4&s=20) **CaileenD**](https://github.com/CaileenD) 42 | - [![calvellido](https://avatars0.githubusercontent.com/u/7753447?v=4&s=20) **calvellido**](https://github.com/calvellido) 43 | - [![angoglez](https://avatars0.githubusercontent.com/u/10107285?v=4&s=20) **angoglez**](https://github.com/angoglez) 44 | - [![alexmdac](https://avatars3.githubusercontent.com/u/41476?v=4&s=20) **alexmdac**](https://github.com/alexmdac) 45 | - [![timaschew](https://avatars1.githubusercontent.com/u/110870?v=4&s=20) **timaschew**](https://github.com/timaschew) 46 | - [![arjun1438](https://avatars1.githubusercontent.com/u/15716626?v=4&s=20) **arjun1438**](https://github.com/arjun1438) 47 | - [![BenFradet](https://avatars2.githubusercontent.com/u/1737211?v=4&s=20) **BenFradet**](https://github.com/BenFradet) 48 | - [![btoueg](https://avatars3.githubusercontent.com/u/498190?v=4&s=20) **btoueg**](https://github.com/btoueg) 49 | - [![dleve123](https://avatars2.githubusercontent.com/u/1561546?v=4&s=20) **dleve123**](https://github.com/dleve123) 50 | - [![dskrvk](https://avatars1.githubusercontent.com/u/2267624?v=4&s=20) **dskrvk**](https://github.com/dskrvk) 51 | - [![Dx724](https://avatars1.githubusercontent.com/u/12465997?v=4&s=20) **Dx724**](https://github.com/Dx724) 52 | - [![TwelveNights](https://avatars2.githubusercontent.com/u/9423051?v=4&s=20) **TwelveNights**](https://github.com/TwelveNights) 53 | - [![fractalliter](https://avatars1.githubusercontent.com/u/25972962?v=4&s=20) **fractalliter**](https://github.com/fractalliter) 54 | - [![ecamellini](https://avatars3.githubusercontent.com/u/6418684?v=4&s=20) **ecamellini**](https://github.com/ecamellini) 55 | - [![fedefernandez](https://avatars0.githubusercontent.com/u/720923?v=4&s=20) **fedefernandez**](https://github.com/fedefernandez) 56 | - [![Gerko32](https://avatars2.githubusercontent.com/u/1730295?v=4&s=20) **Gerko32**](https://github.com/Gerko32) 57 | - [![jgeofil](https://avatars2.githubusercontent.com/u/12494787?v=4&s=20) **jgeofil**](https://github.com/jgeofil) 58 | - [![JuanmaBM](https://avatars0.githubusercontent.com/u/7102242?v=4&s=20) **JuanmaBM**](https://github.com/JuanmaBM) 59 | - [![exp0nge](https://avatars0.githubusercontent.com/u/11747245?v=4&s=20) **exp0nge**](https://github.com/exp0nge) 60 | - [![Michad](https://avatars3.githubusercontent.com/u/5895099?v=4&s=20) **Michad**](https://github.com/Michad) 61 | - [![iselind](https://avatars1.githubusercontent.com/u/21103057?v=4&s=20) **iselind**](https://github.com/iselind) 62 | - [![Tschis](https://avatars1.githubusercontent.com/u/20662669?v=4&s=20) **Tschis**](https://github.com/Tschis) 63 | - [![rvilla87](https://avatars2.githubusercontent.com/u/26299708?v=4&s=20) **rvilla87**](https://github.com/rvilla87) 64 | - [![SCKelemen](https://avatars1.githubusercontent.com/u/4325375?v=4&s=20) **SCKelemen**](https://github.com/SCKelemen) 65 | - [![aamirfayaz](https://avatars0.githubusercontent.com/u/16462830?v=4&s=20) **aamirfayaz**](https://github.com/aamirfayaz) 66 | - [![alejandrohdezma](https://avatars0.githubusercontent.com/u/9027541?v=4&s=20) **alejandrohdezma**](https://github.com/alejandrohdezma) 67 | - [![amitsingh-10](https://avatars1.githubusercontent.com/u/51735639?v=4&s=20) **amitsingh-10**](https://github.com/amitsingh-10) 68 | - [![artnan](https://avatars2.githubusercontent.com/u/28643106?v=4&s=20) **artnan**](https://github.com/artnan) 69 | - [![mlahoz](https://avatars0.githubusercontent.com/u/2168902?v=4&s=20) **mlahoz**](https://github.com/mlahoz) 70 | - [![prokofyev](https://avatars0.githubusercontent.com/u/4557968?v=4&s=20) **prokofyev**](https://github.com/prokofyev) -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v0.6.6](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.6) (2020-08-10) 4 | 5 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.6.4...v0.6.6) 6 | 7 | 📈 **Dependency updates** 8 | 9 | - Update definitions, exercise-compiler, ... to 0.6.4 [\#154](https://github.com/scala-exercises/exercises-scalatutorial/pull/154) ([47erbot](https://github.com/47erbot)) 10 | - Update sbt-mdoc to 2.2.4 [\#153](https://github.com/scala-exercises/exercises-scalatutorial/pull/153) ([47erbot](https://github.com/47erbot)) 11 | - Update scalacheck-1-14 to 3.2.1.0 [\#152](https://github.com/scala-exercises/exercises-scalatutorial/pull/152) ([47erbot](https://github.com/47erbot)) 12 | - Update scalatest to 3.2.1 [\#151](https://github.com/scala-exercises/exercises-scalatutorial/pull/151) ([47erbot](https://github.com/47erbot)) 13 | - Update sbt-scalafmt to 2.4.2 [\#148](https://github.com/scala-exercises/exercises-scalatutorial/pull/148) ([47erbot](https://github.com/47erbot)) 14 | - Update scalafmt-core to 2.6.4 [\#147](https://github.com/scala-exercises/exercises-scalatutorial/pull/147) ([47erbot](https://github.com/47erbot)) 15 | - Update scalafmt-core to 2.6.3 [\#146](https://github.com/scala-exercises/exercises-scalatutorial/pull/146) ([scala-steward](https://github.com/scala-steward)) 16 | - Update scalafmt-core to 2.6.2 [\#144](https://github.com/scala-exercises/exercises-scalatutorial/pull/144) ([scala-steward](https://github.com/scala-steward)) 17 | 18 | **Merged pull requests:** 19 | 20 | - Remove double parenthesis in tuple exercise [\#149](https://github.com/scala-exercises/exercises-scalatutorial/pull/149) ([Gerko32](https://github.com/Gerko32)) 21 | 22 | ## [v0.6.4](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.4) (2020-06-30) 23 | 24 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.6.3...v0.6.4) 25 | 26 | ## [v0.6.3](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.3) (2020-06-29) 27 | 28 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.6.2...v0.6.3) 29 | 30 | 📈 **Dependency updates** 31 | 32 | - Update sbt to 1.3.13 [\#139](https://github.com/scala-exercises/exercises-scalatutorial/pull/139) ([scala-steward](https://github.com/scala-steward)) 33 | - Update sbt-mdoc to 2.2.3 [\#137](https://github.com/scala-exercises/exercises-scalatutorial/pull/137) ([scala-steward](https://github.com/scala-steward)) 34 | - Update scalacheck-1-14 to 3.2.0.0 [\#134](https://github.com/scala-exercises/exercises-scalatutorial/pull/134) ([scala-steward](https://github.com/scala-steward)) 35 | - Update scalatest to 3.2.0 [\#133](https://github.com/scala-exercises/exercises-scalatutorial/pull/133) ([scala-steward](https://github.com/scala-steward)) 36 | - Update sbt-mdoc to 2.2.2 [\#131](https://github.com/scala-exercises/exercises-scalatutorial/pull/131) ([scala-steward](https://github.com/scala-steward)) 37 | - Update sbt to 1.3.11 [\#127](https://github.com/scala-exercises/exercises-scalatutorial/pull/127) ([scala-steward](https://github.com/scala-steward)) 38 | - Update scalatest to 3.1.2 [\#126](https://github.com/scala-exercises/exercises-scalatutorial/pull/126) ([scala-steward](https://github.com/scala-steward)) 39 | 40 | **Merged pull requests:** 41 | 42 | - Updates build [\#140](https://github.com/scala-exercises/exercises-scalatutorial/pull/140) ([juanpedromoreno](https://github.com/juanpedromoreno)) 43 | - Update scalafmt-core to 2.6.1 [\#135](https://github.com/scala-exercises/exercises-scalatutorial/pull/135) ([BenFradet](https://github.com/BenFradet)) 44 | - Updated SyntacticConveniences.scala [\#129](https://github.com/scala-exercises/exercises-scalatutorial/pull/129) ([aamirfayaz](https://github.com/aamirfayaz)) 45 | - Prepare repository for next release and SBT build improvements [\#128](https://github.com/scala-exercises/exercises-scalatutorial/pull/128) ([juanpedromoreno](https://github.com/juanpedromoreno)) 46 | 47 | ## [v0.6.2](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.2) (2020-04-27) 48 | 49 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.6.1...v0.6.2) 50 | 51 | ## [v0.6.1](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.1) (2020-04-27) 52 | 53 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.6.0...v0.6.1) 54 | 55 | **Merged pull requests:** 56 | 57 | - Compile when Publish [\#121](https://github.com/scala-exercises/exercises-scalatutorial/pull/121) ([juanpedromoreno](https://github.com/juanpedromoreno)) 58 | 59 | ## [v0.6.0](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.6.0) (2020-04-24) 60 | 61 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/v0.4.0...v0.6.0) 62 | 63 | **Closed issues:** 64 | 65 | - Scala Tutorial: the last exercise of Lazy Evaluation, builder.result\(\) shouldBe "xyzz" [\#38](https://github.com/scala-exercises/exercises-scalatutorial/issues/38) 66 | - Rename `aux` folder in `exercises-scalatutorial` content library, as its incompatible in Windows machines [\#28](https://github.com/scala-exercises/exercises-scalatutorial/issues/28) 67 | - Syntax isn't introduced [\#23](https://github.com/scala-exercises/exercises-scalatutorial/issues/23) 68 | - I can't press run button [\#15](https://github.com/scala-exercises/exercises-scalatutorial/issues/15) 69 | 70 | **Merged pull requests:** 71 | 72 | - Updates Project [\#116](https://github.com/scala-exercises/exercises-scalatutorial/pull/116) ([juanpedromoreno](https://github.com/juanpedromoreno)) 73 | - "arbitrary" -\> "arbitrarily" [\#114](https://github.com/scala-exercises/exercises-scalatutorial/pull/114) ([alexmdac](https://github.com/alexmdac)) 74 | - Update sbt to 1.3.10 [\#113](https://github.com/scala-exercises/exercises-scalatutorial/pull/113) ([scala-steward](https://github.com/scala-steward)) 75 | - Define nthPrime\(\) before called by secondPrime\(\) [\#110](https://github.com/scala-exercises/exercises-scalatutorial/pull/110) ([xiaowei-zhang](https://github.com/xiaowei-zhang)) 76 | - Concatenate int and string \(since Scala 2.13.0\) [\#109](https://github.com/scala-exercises/exercises-scalatutorial/pull/109) ([xiaowei-zhang](https://github.com/xiaowei-zhang)) 77 | - Convert int to string [\#108](https://github.com/scala-exercises/exercises-scalatutorial/pull/108) ([xiaowei-zhang](https://github.com/xiaowei-zhang)) 78 | - Fix recursive type missing error [\#106](https://github.com/scala-exercises/exercises-scalatutorial/pull/106) ([xiaowei-zhang](https://github.com/xiaowei-zhang)) 79 | - Added missing words for contravariant bounding [\#104](https://github.com/scala-exercises/exercises-scalatutorial/pull/104) ([Dx724](https://github.com/Dx724)) 80 | - Update sbt to 1.3.9 [\#103](https://github.com/scala-exercises/exercises-scalatutorial/pull/103) ([scala-steward](https://github.com/scala-steward)) 81 | - Update scalacheck-shapeless\_1.14 to 1.2.5 [\#100](https://github.com/scala-exercises/exercises-scalatutorial/pull/100) ([scala-steward](https://github.com/scala-steward)) 82 | - Update scalatest to 3.1.1 [\#97](https://github.com/scala-exercises/exercises-scalatutorial/pull/97) ([scala-steward](https://github.com/scala-steward)) 83 | - Mergify: configuration update [\#96](https://github.com/scala-exercises/exercises-scalatutorial/pull/96) ([angoglez](https://github.com/angoglez)) 84 | - Update scalacheck-shapeless\_1.14 to 1.2.4 [\#92](https://github.com/scala-exercises/exercises-scalatutorial/pull/92) ([scala-steward](https://github.com/scala-steward)) 85 | - Update sbt to 1.3.8 [\#91](https://github.com/scala-exercises/exercises-scalatutorial/pull/91) ([scala-steward](https://github.com/scala-steward)) 86 | - Update to Scala 2.13.1 [\#89](https://github.com/scala-exercises/exercises-scalatutorial/pull/89) ([kiroco12](https://github.com/kiroco12)) 87 | - Update scalacheck to 1.14.3 [\#85](https://github.com/scala-exercises/exercises-scalatutorial/pull/85) ([scala-steward](https://github.com/scala-steward)) 88 | - Updated ScalaTest 3.1.0 [\#83](https://github.com/scala-exercises/exercises-scalatutorial/pull/83) ([kiroco12](https://github.com/kiroco12)) 89 | - Update to Scala 2.12.10 [\#80](https://github.com/scala-exercises/exercises-scalatutorial/pull/80) ([kiroco12](https://github.com/kiroco12)) 90 | - Generalize the syntax of function types [\#79](https://github.com/scala-exercises/exercises-scalatutorial/pull/79) ([julienrf](https://github.com/julienrf)) 91 | - edit typo for redefined [\#78](https://github.com/scala-exercises/exercises-scalatutorial/pull/78) ([fractalliter](https://github.com/fractalliter)) 92 | - Use openjdk8 for Travis builds [\#74](https://github.com/scala-exercises/exercises-scalatutorial/pull/74) ([calvellido](https://github.com/calvellido)) 93 | - Rectified `to is not a member of Boolean` error [\#69](https://github.com/scala-exercises/exercises-scalatutorial/pull/69) ([amitsingh-10](https://github.com/amitsingh-10)) 94 | - grammar fix [\#66](https://github.com/scala-exercises/exercises-scalatutorial/pull/66) ([sauntimo](https://github.com/sauntimo)) 95 | - grammar fix [\#65](https://github.com/scala-exercises/exercises-scalatutorial/pull/65) ([sauntimo](https://github.com/sauntimo)) 96 | - Added explanation of shadowing, grammar fix [\#64](https://github.com/scala-exercises/exercises-scalatutorial/pull/64) ([sauntimo](https://github.com/sauntimo)) 97 | - Suggested more guidance in last section of Terms and Types [\#63](https://github.com/scala-exercises/exercises-scalatutorial/pull/63) ([sauntimo](https://github.com/sauntimo)) 98 | - Make the equality executable in the scala shell [\#62](https://github.com/scala-exercises/exercises-scalatutorial/pull/62) ([iselind](https://github.com/iselind)) 99 | - Fixing typos and making a sentence clearer [\#61](https://github.com/scala-exercises/exercises-scalatutorial/pull/61) ([CaileenD](https://github.com/CaileenD)) 100 | - Adding a missing word [\#60](https://github.com/scala-exercises/exercises-scalatutorial/pull/60) ([CaileenD](https://github.com/CaileenD)) 101 | - Fix typo in PolymorphicTypes.scala [\#57](https://github.com/scala-exercises/exercises-scalatutorial/pull/57) ([artnan](https://github.com/artnan)) 102 | - Add minor explanation about List are homogeneous [\#56](https://github.com/scala-exercises/exercises-scalatutorial/pull/56) ([AdrianRaFo](https://github.com/AdrianRaFo)) 103 | - Grammar fix [\#52](https://github.com/scala-exercises/exercises-scalatutorial/pull/52) ([SCKelemen](https://github.com/SCKelemen)) 104 | - Clarify documentation of square root example [\#51](https://github.com/scala-exercises/exercises-scalatutorial/pull/51) ([dleve123](https://github.com/dleve123)) 105 | - Fix insertionSort function in TypeClasses.scala [\#44](https://github.com/scala-exercises/exercises-scalatutorial/pull/44) ([rvilla87](https://github.com/rvilla87)) 106 | - typo: double "is" [\#42](https://github.com/scala-exercises/exercises-scalatutorial/pull/42) ([exp0nge](https://github.com/exp0nge)) 107 | - Typo [\#41](https://github.com/scala-exercises/exercises-scalatutorial/pull/41) ([jgeofil](https://github.com/jgeofil)) 108 | - return type of triangle Area should be Double [\#36](https://github.com/scala-exercises/exercises-scalatutorial/pull/36) ([arjun1438](https://github.com/arjun1438)) 109 | - Renaming the aux directory to avoid issues with Windows developers [\#29](https://github.com/scala-exercises/exercises-scalatutorial/pull/29) ([jdesiloniz](https://github.com/jdesiloniz)) 110 | - Fixing flaky test [\#27](https://github.com/scala-exercises/exercises-scalatutorial/pull/27) ([jdesiloniz](https://github.com/jdesiloniz)) 111 | - Changing abs\(\) to accept Double instead of Int [\#26](https://github.com/scala-exercises/exercises-scalatutorial/pull/26) ([Tschis](https://github.com/Tschis)) 112 | - Replace Int with T in generic insertionSort definition [\#25](https://github.com/scala-exercises/exercises-scalatutorial/pull/25) ([dskrvk](https://github.com/dskrvk)) 113 | - Fix typo [\#22](https://github.com/scala-exercises/exercises-scalatutorial/pull/22) ([Michad](https://github.com/Michad)) 114 | - Update TailRecursion.scala [\#21](https://github.com/scala-exercises/exercises-scalatutorial/pull/21) ([timaschew](https://github.com/timaschew)) 115 | - Fix typo in StandardLibrary.scala [\#20](https://github.com/scala-exercises/exercises-scalatutorial/pull/20) ([TwelveNights](https://github.com/TwelveNights)) 116 | - Updating sbt org policies [\#19](https://github.com/scala-exercises/exercises-scalatutorial/pull/19) ([dominv](https://github.com/dominv)) 117 | - Updating libraries versions [\#18](https://github.com/scala-exercises/exercises-scalatutorial/pull/18) ([dominv](https://github.com/dominv)) 118 | - Update DefinitionsAndEvaluations.scala [\#16](https://github.com/scala-exercises/exercises-scalatutorial/pull/16) ([ghostnostic](https://github.com/ghostnostic)) 119 | - Fixing typo in Standard Library [\#14](https://github.com/scala-exercises/exercises-scalatutorial/pull/14) ([ecamellini](https://github.com/ecamellini)) 120 | - Fixing typo in Pattern Matching block example [\#13](https://github.com/scala-exercises/exercises-scalatutorial/pull/13) ([mlahoz](https://github.com/mlahoz)) 121 | - Update TypeClasses.scala [\#12](https://github.com/scala-exercises/exercises-scalatutorial/pull/12) ([prokofyev](https://github.com/prokofyev)) 122 | - \#619 ScalaExercises 2º Part [\#11](https://github.com/scala-exercises/exercises-scalatutorial/pull/11) ([AdrianRaFo](https://github.com/AdrianRaFo)) 123 | 124 | ## [v0.4.0](https://github.com/scala-exercises/exercises-scalatutorial/tree/v0.4.0) (2017-03-28) 125 | 126 | [Full Changelog](https://github.com/scala-exercises/exercises-scalatutorial/compare/363a88c258b3bd3e63bf1acefb7a43c57edf1b9a...v0.4.0) 127 | 128 | **Closed issues:** 129 | 130 | - Exercises with nothing to fill in / impossible to complete [\#5](https://github.com/scala-exercises/exercises-scalatutorial/issues/5) 131 | - Adds org sbt settings and integrates with Travis [\#2](https://github.com/scala-exercises/exercises-scalatutorial/issues/2) 132 | 133 | **Merged pull requests:** 134 | 135 | - Integrates sbt-org-policies plugin [\#9](https://github.com/scala-exercises/exercises-scalatutorial/pull/9) ([juanpedromoreno](https://github.com/juanpedromoreno)) 136 | - Update TypeClasses.scala [\#7](https://github.com/scala-exercises/exercises-scalatutorial/pull/7) ([JuanmaBM](https://github.com/JuanmaBM)) 137 | - Typo [\#6](https://github.com/scala-exercises/exercises-scalatutorial/pull/6) ([btoueg](https://github.com/btoueg)) 138 | - Update Library Description [\#4](https://github.com/scala-exercises/exercises-scalatutorial/pull/4) ([juanpedromoreno](https://github.com/juanpedromoreno)) 139 | - Library Settings and CI Configuration [\#3](https://github.com/scala-exercises/exercises-scalatutorial/pull/3) ([juanpedromoreno](https://github.com/juanpedromoreno)) 140 | - Add a Scala tutorial [\#1](https://github.com/scala-exercises/exercises-scalatutorial/pull/1) ([julienrf](https://github.com/julienrf)) 141 | 142 | 143 | 144 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 145 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Code of Conduct 6 | 7 | We are committed to providing a friendly, safe and welcoming 8 | environment for all, regardless of level of experience, gender, gender 9 | identity and expression, sexual orientation, disability, personal 10 | appearance, body size, race, ethnicity, age, religion, nationality, or 11 | other such characteristics. 12 | 13 | Everyone is expected to follow the 14 | [Scala Code of Conduct](https://www.scala-lang.org/conduct/) when 15 | discussing the project on the available communication channels. If you 16 | are being harassed, please contact us immediately so that we can 17 | support you. 18 | 19 | ## Moderation 20 | 21 | For any questions, concerns, or moderation requests please contact a 22 | [member of the project](AUTHORS.md#maintainers). -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Contributing 6 | 7 | Discussion around _exercises-scalatutorial_ happens in the [GitHub issues](https://github.com/scala-exercises/exercises-scalatutorial/issues) and [pull requests](https://github.com/scala-exercises/exercises-scalatutorial/pulls). 8 | 9 | Feel free to open an issue if you notice a bug, have an idea for a feature, or have a question about 10 | the code. Pull requests are also welcome. 11 | 12 | People are expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md) when discussing _exercises-scalatutorial_ on the Github page or other venues. 13 | 14 | If you are being harassed, please contact one of [us](AUTHORS.md#maintainers) immediately so that we can support you. In case you cannot get in touch with us please write an email to [47 Degrees Open Source](mailto:hello@47deg.com). 15 | 16 | ## How can I help? 17 | 18 | _exercises-scalatutorial_ follows a standard [fork and pull](https://help.github.com/articles/using-pull-requests/) model for contributions via GitHub pull requests. 19 | 20 | The process is simple: 21 | 22 | 1. Find something you want to work on 23 | 2. Let us know you are working on it via GitHub issues/pull requests 24 | 3. Implement your contribution 25 | 4. Write tests 26 | 5. Update the documentation 27 | 6. Submit pull request 28 | 29 | You will be automatically included in the [AUTHORS.md](AUTHORS.md#contributors) file as contributor in the next release. 30 | 31 | If you encounter any confusion or frustration during the contribution process, please create a GitHub issue and we'll do our best to improve the process. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (C) 2016-2020 47 Degrees Open Source 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | exercises-scalatutorial 6 | 7 | Copyright (c) 2016-2020 47 Degrees Open Source. All rights reserved. 8 | 9 | Licensed under Apache-2.0. See [LICENSE](LICENSE.md) for terms. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scala Exercises - Functional Programming Principles library 2 | ------------------------ 3 | 4 | This repository hosts a library for the first course of the [Scala MOOC](https://www.coursera.org/specializations/scala) ("Functional Programming Principles in Scala"). 5 | 6 | ## Run Locally 7 | 8 | - Clone this repository, compile and publish the project: 9 | 10 | ~~~ sh 11 | git clone git@github.com:scala-exercises/exercises-scalatutorial.git 12 | cd exercises-scalatutorial/ 13 | sbt compile publishLocal # it is important to first run the `compile` command alone 14 | ~~~ 15 | 16 | - Clone the `evaluator` and run it: 17 | 18 | ~~~ sh 19 | git clone git@github.com:scala-exercises/evaluator.git 20 | cd evaluator/ 21 | sbt "project evaluator-server" run 22 | ~~~ 23 | 24 | - Clone the `scala-tutorial` branch of our `scala-exercises` fork: 25 | 26 | ~~~ sh 27 | git clone -b scala-tutorial git@github.com:scalacenter/scala-exercises.git 28 | ~~~ 29 | 30 | - Follow the database setup instructions given 31 | [here](https://github.com/scala-exercises/scala-exercises#configure-the-database) 32 | 33 | - Add the following line the `server/conf/application.dev.conf`: 34 | 35 | ~~~ 36 | evaluator.secretKey="secretKey" 37 | ~~~ 38 | 39 | - Run the server: 40 | 41 | ~~~ sh 42 | sbt -mem 1500 run 43 | ~~~ 44 | 45 | ## About Scala exercises 46 | 47 | "Scala Exercises" brings exercises for the Stdlib, Cats, Shapeless and many other great libraries for Scala to your browser. Offering hundreds of solvable exercises organized into several categories covering the basics of the Scala language and it's most important libraries. 48 | 49 | Scala Exercises is available at [scala-exercises.org](https://scala-exercises.org). 50 | 51 | ## Contributing 52 | 53 | Contributions welcome! Please join our [Gitter channel](https://gitter.im/scala-exercises/scala-exercises) 54 | to get involved, or visit our [GitHub site](https://github.com/scala-exercises). 55 | 56 | ## License 57 | 58 | Copyright (C) 2015-2016 47 Degrees, LLC. 59 | Reactive, scalable software solutions. 60 | http://47deg.com 61 | hello@47deg.com 62 | 63 | Licensed under the Apache License, Version 2.0 (the "License"); 64 | you may not use this file except in compliance with the License. 65 | You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software 70 | distributed under the License is distributed on an "AS IS" BASIS, 71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 72 | See the License for the specific language governing permissions and 73 | limitations under the License. -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import com.jsuereth.sbtpgp.PgpKeys.publishSigned 2 | 3 | ThisBuild / organization := "org.scala-exercises" 4 | ThisBuild / githubOrganization := "47degrees" 5 | ThisBuild / scalaVersion := "2.13.10" 6 | 7 | // Required to prevent errors for eviction from binary incompatible dependency 8 | // resolutions. 9 | // See also: https://github.com/scala-exercises/exercises-cats/pull/267 10 | ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % "always" 11 | 12 | // This is required by the exercises compiler: 13 | publishLocal := (publishLocal dependsOn compile).value 14 | publishSigned := (publishSigned dependsOn compile).value 15 | 16 | addCommandAlias("ci-test", "scalafmtCheckAll; scalafmtSbtCheck; test") 17 | addCommandAlias("ci-docs", "github; documentation/mdoc; headerCreateAll") 18 | addCommandAlias("ci-publish", "github; ci-release") 19 | 20 | lazy val exercises = (project in file(".")) 21 | .settings(name := "exercises-scalatutorial") 22 | .settings( 23 | libraryDependencies ++= Seq( 24 | "org.scala-exercises" %% "exercise-compiler" % "0.7.1", 25 | "org.scala-exercises" %% "definitions" % "0.7.1", 26 | "com.chuusai" %% "shapeless" % "2.3.10", 27 | "org.scalatest" %% "scalatest" % "3.2.15", 28 | "org.scalacheck" %% "scalacheck" % "1.17.0", 29 | "org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0", 30 | "com.github.alexarchambault" %% "scalacheck-shapeless_1.15" % "1.3.0" 31 | ) 32 | ) 33 | .enablePlugins(ExerciseCompilerPlugin) 34 | 35 | lazy val documentation = project 36 | .settings(mdocOut := file(".")) 37 | .settings(publish / skip := true) 38 | .enablePlugins(MdocPlugin) 39 | -------------------------------------------------------------------------------- /docs/AUTHORS.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Authors 6 | 7 | ## Maintainers 8 | 9 | The maintainers of the project are: 10 | 11 | @COLLABORATORS@ 12 | 13 | ## Contributors 14 | 15 | These are the people that have contributed to the _@NAME@_ project: 16 | 17 | @CONTRIBUTORS@ -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Code of Conduct 6 | 7 | We are committed to providing a friendly, safe and welcoming 8 | environment for all, regardless of level of experience, gender, gender 9 | identity and expression, sexual orientation, disability, personal 10 | appearance, body size, race, ethnicity, age, religion, nationality, or 11 | other such characteristics. 12 | 13 | Everyone is expected to follow the 14 | [Scala Code of Conduct](https://www.scala-lang.org/conduct/) when 15 | discussing the project on the available communication channels. If you 16 | are being harassed, please contact us immediately so that we can 17 | support you. 18 | 19 | ## Moderation 20 | 21 | For any questions, concerns, or moderation requests please contact a 22 | [member of the project](AUTHORS.md#maintainers). -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | # Contributing 6 | 7 | Discussion around _@NAME@_ happens in the [GitHub issues](https://github.com/@REPO@/issues) and [pull requests](https://github.com/@REPO@/pulls). 8 | 9 | Feel free to open an issue if you notice a bug, have an idea for a feature, or have a question about 10 | the code. Pull requests are also welcome. 11 | 12 | People are expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md) when discussing _@NAME@_ on the Github page or other venues. 13 | 14 | If you are being harassed, please contact one of [us](AUTHORS.md#maintainers) immediately so that we can support you. In case you cannot get in touch with us please write an email to [@ORG_NAME@](mailto:@ORG_EMAIL@). 15 | 16 | ## How can I help? 17 | 18 | _@NAME@_ follows a standard [fork and pull](https://help.github.com/articles/using-pull-requests/) model for contributions via GitHub pull requests. 19 | 20 | The process is simple: 21 | 22 | 1. Find something you want to work on 23 | 2. Let us know you are working on it via GitHub issues/pull requests 24 | 3. Implement your contribution 25 | 4. Write tests 26 | 5. Update the documentation 27 | 6. Submit pull request 28 | 29 | You will be automatically included in the [AUTHORS.md](AUTHORS.md#contributors) file as contributor in the next release. 30 | 31 | If you encounter any confusion or frustration during the contribution process, please create a GitHub issue and we'll do our best to improve the process. -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (C) @YEAR_RANGE@ @COPYRIGHT_OWNER@ 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /docs/NOTICE.md: -------------------------------------------------------------------------------- 1 | [comment]: <> (Don't edit this file!) 2 | [comment]: <> (It is automatically updated after every release of https://github.com/47degrees/.github) 3 | [comment]: <> (If you want to suggest a change, please open a PR or issue in that repository) 4 | 5 | @NAME@ 6 | 7 | Copyright (c) @YEAR_RANGE@ @ORG_NAME@. All rights reserved. 8 | 9 | Licensed under @LICENSE@. See [LICENSE](LICENSE.md) for terms. -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.8.2 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.7.1") 2 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11") 3 | addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7") 4 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") 5 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") 6 | addSbtPlugin("com.alejandrohdezma" % "sbt-github" % "0.11.8") 7 | addSbtPlugin("com.alejandrohdezma" % "sbt-github-header" % "0.11.8") 8 | addSbtPlugin("com.alejandrohdezma" % "sbt-github-mdoc" % "0.11.8") 9 | addSbtPlugin("com.alejandrohdezma" % "sbt-remove-test-from-pom" % "0.1.0") 10 | -------------------------------------------------------------------------------- /src/main/resources/public/scala_tutorial/music_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-exercises/exercises-scalatutorial/f904f6e91d6b954b38dfe3b2b86c7c8b3305d112/src/main/resources/public/scala_tutorial/music_sheet.png -------------------------------------------------------------------------------- /src/main/resources/public/scala_tutorial/scala_type_hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-exercises/exercises-scalatutorial/f904f6e91d6b954b38dfe3b2b86c7c8b3305d112/src/main/resources/public/scala_tutorial/scala_type_hierarchy.png -------------------------------------------------------------------------------- /src/main/resources/scala-tutorial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | std_lib 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/ScalaTutorial.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial 18 | 19 | import org.scalaexercises.definitions.Library 20 | 21 | import sections._ 22 | 23 | /** 24 | * Quickly learn Scala through an interactive tutorial based on the first two courses of the Scala 25 | * MOOCs. 26 | * 27 | * @param name 28 | * scala_tutorial 29 | */ 30 | object ScalaTutorial extends Library { 31 | val owner = "scala-exercises" 32 | val repository = "exercises-scalatutorial" 33 | override val color = Some("#f26527") 34 | val logoPath = "scala-tutorial" 35 | 36 | val sections = List( 37 | TermsAndTypes, 38 | DefinitionsAndEvaluation, 39 | FunctionalLoops, 40 | LexicalScopes, 41 | TailRecursion, 42 | StructuringInformation, 43 | HigherOrderFunctions, 44 | StandardLibrary, 45 | SyntacticConveniences, 46 | ObjectOrientedProgramming, 47 | ImperativeProgramming, 48 | ClassesVsCaseClasses, 49 | PolymorphicTypes, 50 | LazyEvaluation, 51 | TypeClasses 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/ClassesVsCaseClasses.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import scalatutorial.utils.{BankAccount, Note} 20 | 21 | /** 22 | * @param name 23 | * classes_vs_case_classes 24 | */ 25 | object ClassesVsCaseClasses extends ScalaTutorialSection { 26 | 27 | /** 28 | * In the previous sections we have seen how case classes could be used to achieve information 29 | * aggregation, and also how classes could be used to achieve data abstraction or to define 30 | * stateful objects. 31 | * 32 | * What are the relationship between classes and case classes? How do they differ? 33 | * 34 | * =Creation and Manipulation= 35 | * 36 | * Remember the class definition of `BankAccount`: 37 | * 38 | * {{{ 39 | * class BankAccount { 40 | * 41 | * private var balance = 0 42 | * 43 | * def deposit(amount: Int): Unit = { 44 | * if (amount > 0) balance = balance + amount 45 | * } 46 | * 47 | * def withdraw(amount: Int): Int = 48 | * if (0 < amount && amount <= balance) { 49 | * balance = balance - amount 50 | * balance 51 | * } else throw new Error("insufficient funds") 52 | * } 53 | * }}} 54 | * 55 | * And the case class definition of `Note`: 56 | * 57 | * {{{ 58 | * case class Note(name: String, duration: String, octave: Int) 59 | * }}} 60 | * 61 | * Let’s create some instances of `BankAccount` and `Note` and manipulate them: 62 | */ 63 | def creationAndManipulation(res0: String): Unit = { 64 | val aliceAccount = new BankAccount 65 | val c3 = Note("C", "Quarter", 3) 66 | 67 | c3.name shouldBe res0 68 | } 69 | 70 | /** 71 | * We see that creating a class instance requires the keyword `new`, whereas this is not required 72 | * for case classes. 73 | * 74 | * Also, we see that the case class constructor parameters are promoted to members, whereas this 75 | * is not true for regular classes: the parameters will remain private. 76 | * 77 | * {{{ 78 | * class MyClass(x: Int) { def doubledX = x * 2 } 79 | * val myInstance = new MyClass(5) 80 | * myInstance.doubledX // returns 10 81 | * myInstance.x // error: value x is not a member of MyClass 82 | * }}} 83 | * 84 | * =Equality= 85 | */ 86 | def equality(res0: Boolean, res1: Boolean): Unit = { 87 | val aliceAccount = new BankAccount 88 | val bobAccount = new BankAccount 89 | 90 | aliceAccount == bobAccount shouldBe res0 91 | 92 | val c3 = Note("C", "Quarter", 3) 93 | val cThree = Note("C", "Quarter", 3) 94 | 95 | c3 == cThree shouldBe res1 96 | } 97 | 98 | /** 99 | * In the above example, the same definitions of bank accounts lead to different values, whereas 100 | * the same definitions of notes lead to equal values. 101 | * 102 | * As we have seen in the previous sections, stateful classes introduce a notion of ''identity'' 103 | * that does not exist in case classes. Indeed, the value of `BankAccount` can change over time 104 | * whereas the value of a `Note` is immutable. 105 | * 106 | * In Scala, by default, comparing objects will compare their identity, but in the case of case 107 | * class instances, the equality is redefined to compare the values of the aggregated information. 108 | * 109 | * =Pattern Matching= 110 | * 111 | * We saw how pattern matching can be used to extract information from a case class instance: 112 | * 113 | * {{{ 114 | * c3 match { 115 | * case Note(name, duration, octave) => s"The duration of c3 is $duration" 116 | * } 117 | * }}} 118 | * 119 | * By default, pattern matching does not work with regular classes. 120 | * 121 | * =Extensibility= 122 | * 123 | * A class can extend another class, whereas a case class can not extend another case class 124 | * (because it would not be possible to correctly implement their equality). 125 | * 126 | * =Case Classes Encoding= 127 | * 128 | * We saw the main differences between classes and case classes. 129 | * 130 | * It turns out that case classes are just a special case of classes, whose purpose is to 131 | * aggregate several values into a single value. 132 | * 133 | * The Scala language provides explicit support for this use case because it is very common in 134 | * practice. 135 | * 136 | * So, when we define a case class, the Scala compiler defines a class enhanced with some more 137 | * methods and a companion object. 138 | * 139 | * For instance, the following case class definition: 140 | * 141 | * {{{ 142 | * case class Note(name: String, duration: String, octave: Int) 143 | * }}} 144 | * 145 | * Expands to the following class definition: 146 | * 147 | * {{{ 148 | * class Note(_name: String, _duration: String, _octave: Int) extends Serializable { 149 | * 150 | * // Constructor parameters are promoted to members 151 | * val name = _name 152 | * val duration = _duration 153 | * val octave = _octave 154 | * 155 | * // Equality redefinition 156 | * override def equals(other: Any): Boolean = other match { 157 | * case that: Note => 158 | * (that canEqual this) && 159 | * name == that.name && 160 | * duration == that.duration && 161 | * octave == that.octave 162 | * case _ => false 163 | * } 164 | * 165 | * def canEqual(other: Any): Boolean = other.isInstanceOf[Note] 166 | * 167 | * // Java hashCode redefinition according to equality 168 | * override def hashCode(): Int = { 169 | * val state = Seq(name, duration, octave) 170 | * state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) 171 | * } 172 | * 173 | * // toString redefinition to return the value of an instance instead of its memory addres 174 | * override def toString = s"Note($name,$duration,$octave)" 175 | * 176 | * // Create a copy of a case class, with potentially modified field values 177 | * def copy(name: String = name, duration: String = duration, octave: Int = octave): Note = 178 | * new Note(name, duration, octave) 179 | * 180 | * } 181 | * 182 | * object Note { 183 | * 184 | * // Constructor that allows the omission of the `new` keyword 185 | * def apply(name: String, duration: String, octave: Int): Note = 186 | * new Note(name, duration, octave) 187 | * 188 | * // Extractor for pattern matching 189 | * def unapply(note: Note): Option[(String, String, Int)] = 190 | * if (note eq null) None 191 | * else Some((note.name, note.duration, note.octave)) 192 | * 193 | * } 194 | * }}} 195 | */ 196 | def encoding(res0: String, res1: Boolean, res2: String): Unit = { 197 | val c3 = Note("C", "Quarter", 3) 198 | c3.toString shouldBe res0 199 | val d = Note("D", "Quarter", 3) 200 | c3.equals(d) shouldBe res1 201 | val c4 = c3.copy(octave = 4) 202 | c4.toString shouldBe res2 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/DefinitionsAndEvaluation.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * definitions_and_evaluation 22 | */ 23 | object DefinitionsAndEvaluation extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Naming Things= 27 | * 28 | * Consider the following program that computes the area of a disc whose radius is `10`: 29 | * 30 | * {{{ 31 | * 3.14159 * 10 * 10 32 | * }}} 33 | * 34 | * To make complex expressions more readable we can give meaningful names to intermediate 35 | * expressions: 36 | * 37 | * {{{ 38 | * val radius = 10 39 | * val pi = 3.14159 40 | * 41 | * pi * radius * radius 42 | * }}} 43 | * 44 | * Besides making the last expression more readable it also allows us to not repeat the actual 45 | * value of the radius. 46 | * 47 | * =Evaluation= 48 | * 49 | * A name is evaluated by replacing it with the right hand side of its definition 50 | * 51 | * ==Example== 52 | * 53 | * Here are the evaluation steps of the above expression: 54 | * 55 | * {{{ 56 | * pi * radius * radius 57 | * 3.14159 * radius * radius 58 | * 3.14159 * 10 * radius 59 | * 31.4159 * radius 60 | * 31.4159 * 10 61 | * 314.159 62 | * }}} 63 | * 64 | * =Methods= 65 | * 66 | * Definitions can have parameters. For instance: 67 | */ 68 | def usingSquare(res0: Double): Unit = { 69 | def square(x: Double) = x * x 70 | 71 | square(3.0) shouldBe res0 72 | } 73 | 74 | /** 75 | * Let’s define a method that computes the area of a disc, given its radius: 76 | */ 77 | def areaExercise(res0: Double): Unit = { 78 | def square(x: Double) = x * x 79 | 80 | def area(radius: Double): Double = 3.14159 * square(radius) 81 | 82 | area(10) shouldBe res0 83 | } 84 | 85 | /** 86 | * =Multiple Parameters= 87 | * 88 | * Separate several parameters with commas: 89 | * 90 | * {{{ 91 | * def sumOfSquares(x: Double, y: Double) = square(x) + square(y) 92 | * }}} 93 | * 94 | * =Parameters and Return Types= 95 | * 96 | * Function parameters come with their type, which is given after a colon 97 | * 98 | * {{{ 99 | * def power(x: Double, y: Int): Double = ... 100 | * }}} 101 | * 102 | * If a return type is given, it follows the parameter list. 103 | * 104 | * =Val vs Def= 105 | * 106 | * The right hand side of a `def` definition is evaluated on each use. 107 | * 108 | * The right hand side of a `val` definition is evaluated at the point of the definition itself. 109 | * Afterwards, the name refers to the value. 110 | * 111 | * {{{ 112 | * val x = 2 113 | * val y = square(x) 114 | * }}} 115 | * 116 | * For instance, `y` above refers to `4`, not `square(2)`. 117 | * 118 | * =Evaluation of Function Applications= 119 | * 120 | * Applications of parametrized functions are evaluated in a similar way as operators: 121 | * 122 | * 1. Evaluate all function arguments, from left to right 123 | * 1. Replace the function application by the function's right-hand side, and, at the same time 124 | * 1. Replace the formal parameters of the function by the actual arguments. 125 | * 126 | * ==Example== 127 | * 128 | * {{{ 129 | * sumOfSquares(3, 2+2) 130 | * sumOfSquares(3, 4) 131 | * square(3) + square(4) 132 | * 3 * 3 + square(4) 133 | * 9 + square(4) 134 | * 9 + 4 * 4 135 | * 9 + 16 136 | * 25 137 | * }}} 138 | * 139 | * =The substitution model= 140 | * 141 | * This scheme of expression evaluation is called the ''substitution model''. 142 | * 143 | * The idea underlying this model is that all evaluation does is ''reduce an expression to a 144 | * value''. 145 | * 146 | * It can be applied to all expressions, as long as they have no side effects. 147 | * 148 | * The substitution model is formalized in the λ-calculus, which gives a foundation for functional 149 | * programming. 150 | * 151 | * =Termination= 152 | * 153 | * Does every expression reduce to a value (in a finite number of steps)? 154 | * 155 | * No. Here is a counter-example: 156 | * 157 | * {{{ 158 | * def loop: Int = loop 159 | * 160 | * loop 161 | * }}} 162 | * 163 | * =Value Definitions and Termination= 164 | * 165 | * The difference between `val` and `def` becomes apparent when the right hand side does not 166 | * terminate. Given 167 | * 168 | * {{{ 169 | * def loop: Int = loop 170 | * }}} 171 | * 172 | * A definition 173 | * 174 | * {{{ 175 | * def x = loop 176 | * }}} 177 | * 178 | * is OK, but a value 179 | * 180 | * {{{ 181 | * val x = loop 182 | * }}} 183 | * 184 | * will lead to an infinite loop. 185 | * 186 | * =Changing the evaluation strategy= 187 | * 188 | * The interpreter reduces function arguments to values before rewriting the function application. 189 | * 190 | * One could alternatively apply the function to unreduced arguments. 191 | * 192 | * For instance: 193 | * 194 | * {{{ 195 | * sumOfSquares(3, 2+2) 196 | * square(3) + square(2+2) 197 | * 3 * 3 + square(2+2) 198 | * 9 + square(2+2) 199 | * 9 + (2+2) * (2+2) 200 | * 9 + 4 * (2+2) 201 | * 9 + 4 * 4 202 | * 25 203 | * }}} 204 | * 205 | * =Call-by-name and call-by-value= 206 | * 207 | * The first evaluation strategy is known as ''call-by-value'', the second is known as 208 | * ''call-by-name''. 209 | * 210 | * Both strategies reduce to the same final values as long as 211 | * 212 | * - the reduced expression consists of pure functions, and 213 | * - both evaluations terminate. 214 | * 215 | * Call-by-value has the advantage that it evaluates every function argument only once. 216 | * 217 | * Call-by-name has the advantage that a function argument is not evaluated if the corresponding 218 | * parameter is unused in the evaluation of the function body. 219 | * 220 | * Scala normally uses call-by-value. 221 | * 222 | * =Exercise= 223 | * 224 | * Complete the following definition of the `triangleArea` function, which takes a triangle base 225 | * and height as parameters and returns its area: 226 | */ 227 | def triangleAreaExercise(res0: Double, res1: Double): Unit = { 228 | def triangleArea(base: Double, height: Double): Double = 229 | base * height / res0 230 | 231 | triangleArea(3, 4) shouldBe 6.0 232 | triangleArea(5, 6) shouldBe res1 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/FunctionalLoops.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * functional_loops 22 | */ 23 | object FunctionalLoops extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Conditional Expressions= 27 | * 28 | * To express choosing between two alternatives, Scala has a conditional expression `if-else`. 29 | * 30 | * It looks like a `if-else` in Java, but is used for expressions, not statements. 31 | * 32 | * Example: 33 | * 34 | * {{{ 35 | * def abs(x: Double) = if (x >= 0) x else -x 36 | * }}} 37 | * 38 | * `x >= 0` is a ''predicate'', of type `Boolean`. 39 | * 40 | * =Boolean Expressions= 41 | * 42 | * Boolean expressions `b` can be composed of 43 | * 44 | * {{{ 45 | * true false // Constants 46 | * !b // Negation 47 | * b && b // Conjunction 48 | * b || b // Disjunction 49 | * }}} 50 | * 51 | * and of the usual comparison operations: 52 | * {{{ 53 | * e <= e, e >= e, e < e, e > e, e == e, e != e 54 | * }}} 55 | * 56 | * =Rewrite rules for Booleans= 57 | * 58 | * Here are reduction rules for Boolean expressions (`e` is an arbitrary expression): 59 | * 60 | * {{{ 61 | * !true --> false 62 | * !false --> true 63 | * true && e --> e 64 | * false && e --> false 65 | * true || e --> true 66 | * false || e --> e 67 | * }}} 68 | * 69 | * Note that `&&` and `||` do not always need their right operand to be evaluated. 70 | * 71 | * We say these expressions use “short-circuit evaluation”. 72 | * 73 | * =Computing the Square Root of a Value= 74 | * 75 | * We will define in this section a method 76 | * 77 | * {{{ 78 | * /** Calculates the square root of parameter x */ 79 | * def sqrt(x: Double): Double = ... 80 | * }}} 81 | * 82 | * The classical way to achieve this is by successive approximations using Newton's method. 83 | * 84 | * =Method= 85 | * 86 | * To compute `sqrt(x)`: 87 | * 88 | * - Start with an initial ''estimate'' `y` (let's pick `y = 1`). 89 | * - Repeatedly improve the estimate by taking the mean of `y` and `x/y`. 90 | * 91 | * Example: Evaluation of the square root of 2 (x = 2): 92 | * 93 | * {{{ 94 | * Estimation Quotient Mean 95 | * 1 2 / 1 = 2 1.5 96 | * 1.5 2 / 1.5 = 1.333 1.4167 97 | * 1.4167 2 / 1.4167 = 1.4118 1.4142 98 | * 1.4142 ... ... 99 | * }}} 100 | * 101 | * =Implementation in Scala= 102 | * 103 | * First, we define a method which computes one iteration step: 104 | * 105 | * {{{ 106 | * def sqrtIter(guess: Double, x: Double): Double = 107 | * if (isGoodEnough(guess, x)) guess 108 | * else sqrtIter(improve(guess, x), x) 109 | * }}} 110 | * 111 | * Note that `sqrtIter` is ''recursive'', its right-hand side calls itself. 112 | * 113 | * Recursive methods need an explicit return type in Scala. 114 | * 115 | * For non-recursive methods, the return type is optional. 116 | * 117 | * Second, we define a method `improve` to improve an estimate and a test to check for 118 | * termination: 119 | * 120 | * {{{ 121 | * def improve(guess: Double, x: Double) = 122 | * (guess + x / guess) / 2 123 | * 124 | * def isGoodEnough(guess: Double, x: Double) = 125 | * abs(guess * guess - x) < 0.001 126 | * }}} 127 | * 128 | * Third, we define the `sqrt` function: 129 | * 130 | * {{{ 131 | * def sqrt(x: Double) = sqrtIter(1.0, x) 132 | * }}} 133 | * 134 | * =Summary= 135 | * 136 | * You have seen simple elements of functional programing in Scala. 137 | * 138 | * - arithmetic and boolean expressions 139 | * - conditional expressions if-else 140 | * - functions with recursion 141 | * 142 | * You have learned the difference between the call-by-name and call-by-value evaluation 143 | * strategies. 144 | * 145 | * You have learned a way to reason about program execution: reduce expressions using the 146 | * substitution model. 147 | * 148 | * =Exercise= 149 | * 150 | * Complete the following method definition that computes the factorial of a number: 151 | */ 152 | def factorialExercise(res0: Int, res1: Int): Unit = { 153 | def factorial(n: Int): Int = 154 | if (n == res0) res1 155 | else factorial(n - 1) * n 156 | 157 | factorial(3) shouldBe 6 158 | factorial(4) shouldBe 24 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/HigherOrderFunctions.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * higher_order_functions 22 | */ 23 | object HigherOrderFunctions extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Higher-Order Functions= 27 | * 28 | * Functional languages treat functions as ''first-class values''. 29 | * 30 | * This means that, like any other value, a function can be passed as a parameter and returned as 31 | * a result. 32 | * 33 | * This provides a flexible way to compose programs. 34 | * 35 | * Functions that take other functions as parameters or that return functions as results are 36 | * called ''higher order functions''. 37 | * 38 | * =Motivation= 39 | * 40 | * Consider the following programs. 41 | * 42 | * Take the sum of the integers between `a` and `b`: 43 | * 44 | * {{{ 45 | * def sumInts(a: Int, b: Int): Int = 46 | * if (a > b) 0 else a + sumInts(a + 1, b) 47 | * }}} 48 | * 49 | * Take the sum of the cubes of all the integers between `a` and `b`: 50 | * 51 | * {{{ 52 | * def cube(x: Int): Int = x * x * x 53 | * 54 | * def sumCubes(a: Int, b: Int): Int = 55 | * if (a > b) 0 else cube(a) + sumCubes(a + 1, b) 56 | * }}} 57 | * 58 | * Take the sum of the factorials of all the integers between `a` and `b`: 59 | * 60 | * {{{ 61 | * def sumFactorials(a: Int, b: Int): Int = 62 | * if (a > b) 0 else factorial(a) + sumFactorials(a + 1, b) 63 | * }}} 64 | * 65 | * Note how similar these methods are. Can we factor out the common pattern? 66 | * 67 | * =Summing with Higher-Order Functions= 68 | * 69 | * Let's define: 70 | * 71 | * {{{ 72 | * def sum(f: Int => Int, a: Int, b: Int): Int = 73 | * if (a > b) 0 74 | * else f(a) + sum(f, a + 1, b) 75 | * }}} 76 | * 77 | * We can then write: 78 | * 79 | * {{{ 80 | * def id(x: Int): Int = x 81 | * def sumInts(a: Int, b: Int) = sum(id, a, b) 82 | * def sumCubes(a: Int, b: Int) = sum(cube, a, b) 83 | * def sumFactorials(a: Int, b: Int) = sum(factorial, a, b) 84 | * }}} 85 | * 86 | * =Function Types= 87 | * 88 | * The type `A => B` is the type of a ''function'' that takes an argument of type `A` and returns 89 | * a result of type `B`. 90 | * 91 | * So, `Int => Int` is the type of functions that map integers to integers. 92 | * 93 | * Similarly, `(A1, A2) => B` is the type of functions that take two arguments (of types `A1` and 94 | * `A2`, respectively) and return a result of type `B`. 95 | * 96 | * More generally, `(A1, ..., An) => B` is the type of functions that take `n` arguments (of types 97 | * `A1` to `An`) and return a result of type `B`. 98 | * 99 | * =Anonymous Functions= 100 | * 101 | * Passing functions as parameters leads to the creation of many small functions. 102 | * 103 | * Sometimes it is tedious to have to define (and name) these functions using `def`. 104 | * 105 | * Compare to strings: We do not need to define a string using `val`. Instead of: 106 | * 107 | * {{{ 108 | * val str = "abc"; println(str) 109 | * }}} 110 | * 111 | * We can directly write: 112 | * 113 | * {{{ 114 | * println("abc") 115 | * }}} 116 | * 117 | * because strings exist as ''literals''. Analogously we would like function literals, which let 118 | * us write a function without giving it a name. 119 | * 120 | * These are called ''anonymous functions''. 121 | * 122 | * ==Anonymous Function Syntax== 123 | * 124 | * Example of a function that raises its argument to a cube: 125 | * 126 | * {{{ 127 | * (x: Int) => x * x * x 128 | * }}} 129 | * 130 | * Here, `(x: Int)` is the ''parameter'' of the function, and `x * x * x` is its ''body''. 131 | * 132 | * The type of the parameter can be omitted if it can be inferred by the compiler from the 133 | * context. 134 | * 135 | * If there are several parameters, they are separated by commas: 136 | * 137 | * {{{ 138 | * (x: Int, y: Int) => x + y 139 | * }}} 140 | * 141 | * ==Anonymous Functions are Syntactic Sugar== 142 | * 143 | * An anonymous function `(x1: T1, …, xn: Tn) => e` can always be expressed using `def` as 144 | * follows: 145 | * 146 | * {{{ 147 | * { def f(x1: T1, …, xn: Tn) = e ; f } 148 | * }}} 149 | * 150 | * where `f` is an arbitrary, fresh name (that's not yet used in the program). 151 | * 152 | * One can therefore say that anonymous functions are ''syntactic sugar''. 153 | * 154 | * ==Summation with Anonymous Functions== 155 | * 156 | * Using anonymous functions, we can write sums in a shorter way: 157 | * 158 | * {{{ 159 | * def sumInts(a: Int, b: Int) = sum(x => x, a, b) 160 | * def sumCubes(a: Int, b: Int) = sum(x => x * x * x, a, b) 161 | * }}} 162 | * 163 | * =Exercise= 164 | * 165 | * The `sum` function uses linear recursion. Complete the following tail-recursive version: 166 | */ 167 | def tailRecSum(res0: Int, res1: Int): Unit = { 168 | def sum(f: Int => Int, a: Int, b: Int): Int = { 169 | def loop(x: Int, acc: Int): Int = 170 | if (x > b) acc 171 | else loop(x + 1, acc + f(x)) 172 | loop(a, res0) 173 | } 174 | sum(x => x, 1, res1) shouldBe 55 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/ImperativeProgramming.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import scalatutorial.utils.BankAccount 20 | 21 | /** 22 | * @param name 23 | * imperative_programming 24 | */ 25 | object ImperativeProgramming extends ScalaTutorialSection { 26 | 27 | /** 28 | * Until now, our programs have been side-effect free. 29 | * 30 | * Therefore, the concept of ''time'' wasn't important. 31 | * 32 | * For all programs that terminate, any sequence of actions would have given the same results. 33 | * 34 | * This was also reflected in the substitution model of computation. 35 | * 36 | * =Reminder: Substitution Model= 37 | * 38 | * Programs can be evaluated by ''rewriting'': 39 | * - a name is evaluated by replacing it with the right-hand side of its definition, 40 | * - function application is evaluated by replacing it with the function’s right-hand side, and, 41 | * at the same time, by replacing the formal parameters by the actual arguments. 42 | * 43 | * Say you have the following two functions `iterate` and `square`: 44 | * 45 | * {{{ 46 | * def iterate(n: Int, f: Int => Int, x: Int): Int = 47 | * if (n == 0) x else iterate(n-1, f, f(x)) 48 | * def square(x: Int) = x * x 49 | * }}} 50 | * 51 | * Then the call `iterate(1, square, 3)` gets rewritten as follows: 52 | * 53 | * {{{ 54 | * iterate(1, square, 3) 55 | * if (1 == 0) 3 else iterate(1-1, square, square(3)) 56 | * iterate(0, square, square(3)) 57 | * iterate(0, square, 3 * 3) 58 | * iterate(0, square, 9) 59 | * if (0 == 0) 9 else iterate(0-1, square, square(9)) 60 | * 9 61 | * }}} 62 | * 63 | * Rewriting can be done anywhere in a term, and all rewritings which terminate lead to the same 64 | * solution. 65 | * 66 | * This is an important result of the λ-calculus, the theory behind functional programming. 67 | * 68 | * For instance, these two rewriting will eventually lead to the same result: 69 | * 70 | * {{{ 71 | * if (1 == 0) 3 else iterate(1 - 1, square, square(3)) 72 | * iterate(0, square, square(3)) 73 | * 74 | * // OR 75 | * if (1 == 0) 3 else iterate(1 - 1, square, square(3)) 76 | * if (1 == 0) 3 else iterate(1 - 1, square, 3 * 3) 77 | * }}} 78 | * 79 | * =Stateful Objects= 80 | * 81 | * One normally describes the world as a set of objects, some of which have state that ''changes'' 82 | * over the course of time. 83 | * 84 | * An object ''has a state'' if its behavior is influenced by its history. 85 | * 86 | * Example: a bank account has a state, because the answer to the question “can I withdraw 100 CHF 87 | * ?” may vary over the course of the lifetime of the account. 88 | * 89 | * =Implementation of State= 90 | * 91 | * Every form of mutable state is constructed from variables. 92 | * 93 | * A variable definition is written like a value definition, but with the keyword `var` in place 94 | * of `val`: 95 | * 96 | * {{{ 97 | * var x: String = "abc" 98 | * var count = 111 99 | * }}} 100 | * 101 | * Just like a value definition, a variable definition associates a value with a name. 102 | * 103 | * However, in the case of variable definitions, this association can be changed later through an 104 | * ''assignment'': 105 | * 106 | * {{{ 107 | * x = "hi" 108 | * count = count + 1 109 | * }}} 110 | * 111 | * =State in Objects= 112 | * 113 | * In practice, objects with state are usually represented by objects that have some variable 114 | * members. 115 | * 116 | * Here is a class modeling a bank account: 117 | * 118 | * {{{ 119 | * class BankAccount { 120 | * private var balance = 0 121 | * def deposit(amount: Int): Int = { 122 | * if (amount > 0) balance = balance + amount 123 | * balance 124 | * } 125 | * def withdraw(amount: Int): Int = 126 | * if (0 < amount && amount <= balance) { 127 | * balance = balance - amount 128 | * balance 129 | * } else throw new Error("insufficient funds") 130 | * } 131 | * }}} 132 | * 133 | * The class `BankAccount` defines a variable `balance` that contains the current balance of the 134 | * account. 135 | * 136 | * The methods `deposit` and `withdraw` change the value of the `balance` through assignments. 137 | * 138 | * Note that `balance` is `private` in the `BankAccount` class, it therefore cannot be accessed 139 | * from outside the class. 140 | * 141 | * To create bank accounts, we use the usual notation for object creation: 142 | * 143 | * {{{ 144 | * val account = new BankAccount 145 | * }}} 146 | * 147 | * =Working with Mutable Objects= 148 | * 149 | * Here is a program that manipulates bank accounts. 150 | * 151 | * {{{ 152 | * val account = new BankAccount // account: BankAccount = BankAccount 153 | * account deposit 50 // 154 | * account withdraw 20 // res1: Int = 30 155 | * account withdraw 20 // res2: Int = 10 156 | * account withdraw 15 // java.lang.Error: insufficient funds 157 | * }}} 158 | * 159 | * Applying the same operation to an account twice in a row produces different results. Clearly, 160 | * accounts are stateful objects. 161 | * 162 | * =Identity and Change= 163 | * 164 | * Assignment poses the new problem of deciding whether two expressions are "the same" 165 | * 166 | * When one excludes assignments and one writes: 167 | * 168 | * {{{ 169 | * val x = E; val y = E 170 | * }}} 171 | * 172 | * where `E` is an arbitrary expression, then it is reasonable to assume that `x` and `y` are the 173 | * same. That is to say that we could have also written: 174 | * 175 | * {{{ 176 | * val x = E; val y = x 177 | * }}} 178 | * 179 | * (This property is usually called ''referential transparency'') 180 | * 181 | * But once we allow the assignment, the two formulations are different. For example: 182 | * 183 | * {{{ 184 | * val x = new BankAccount 185 | * val y = new BankAccount 186 | * }}} 187 | * 188 | * Are `x` and `y` the same? 189 | * 190 | * =Operational Equivalence= 191 | * 192 | * To respond to the last question, we must specify what is meant by “the same”. 193 | * 194 | * The precise meaning of “being the same” is defined by the property of ''operational 195 | * equivalence''. 196 | * 197 | * In a somewhat informal way, this property is stated as follows: 198 | * 199 | * - Suppose we have two definitions `x` and `y`. 200 | * - `x` and `y` are operationally equivalent if ''no possible test'' can distinguish between 201 | * them. 202 | * 203 | * =Testing for Operational Equivalence= 204 | * 205 | * To test if `x` and `y` are the same, we must 206 | * 207 | * - Execute the definitions followed by an arbitrary sequence `S` of operations that involves 208 | * `x` and `y`, observing the possible outcomes. 209 | * 210 | * {{{ 211 | * val x = new BankAccount 212 | * val y = new BankAccount 213 | * f(x, y) 214 | * }}} 215 | * 216 | * - Then, execute the definitions with another sequence `S'` obtained by renaming all 217 | * occurrences of `y` by `x` in `S`: 218 | * 219 | * {{{ 220 | * val x = new BankAccount 221 | * val y = new BankAccount 222 | * f(x, x) 223 | * }}} 224 | * 225 | * - If the results are different, then the expressions `x` and `y` are certainly different. 226 | * - On the other hand, if all possible pairs of sequences `(S, S')` produce the same result, 227 | * then `x` and `y` are the same. 228 | * 229 | * Based on this definition, let's see if the expressions 230 | * 231 | * {{{ 232 | * val x = new BankAccount 233 | * val y = new BankAccount 234 | * }}} 235 | * 236 | * Let's follow the definitions by a test sequence: 237 | * 238 | * {{{ 239 | * val x = new BankAccount 240 | * val y = new BankAccount 241 | * x deposit 30 242 | * y withdraw 20 // java.lang.Error: insufficient funds 243 | * }}} 244 | * 245 | * Now rename all occurrences of `y` with `x` in this sequence. We obtain: 246 | */ 247 | def observationalEquivalence(res0: Int): Unit = { 248 | val x = new BankAccount 249 | val y = new BankAccount 250 | x deposit 30 251 | x withdraw 20 shouldBe res0 252 | } 253 | 254 | /** 255 | * The final results are different. We conclude that `x` and `y` are not the same. 256 | * 257 | * =Establishing Operational Equivalence= 258 | * 259 | * On the other hand, if we define 260 | * 261 | * {{{ 262 | * val x = new BankAccount 263 | * val y = x 264 | * }}} 265 | * 266 | * then no sequence of operations can distinguish between `x` and `y`, so `x` and `y` are the same 267 | * in this case. 268 | * 269 | * =Assignment and Substitution Model= 270 | * 271 | * The preceding examples show that our model of computation by substitution cannot be used. 272 | * 273 | * Indeed, according to this model, one can always replace the name of a value by the expression 274 | * that defines it. For example, in 275 | * 276 | * {{{ 277 | * val x = new BankAccount 278 | * val y = x 279 | * }}} 280 | * 281 | * the `x` in the definition of `y` could be replaced by `new BankAccount`. 282 | * 283 | * But we have seen that this change leads to a different program! 284 | * 285 | * The substitution model ceases to be valid when we add the assignment. 286 | * 287 | * It is possible to adapt the substitution model by introducing a ''store'', but this becomes 288 | * considerably more complicated. 289 | * 290 | * =Imperative Loops= 291 | * 292 | * In the first sections, we saw how to write loops using recursion. 293 | * 294 | * ==While-Loops== 295 | * 296 | * We can also write loops with the `while` keyword: 297 | * 298 | * {{{ 299 | * def power (x: Double, exp: Int): Double = { 300 | * var r = 1.0 301 | * var i = exp 302 | * while (i > 0) { r = r * x; i = i - 1 } 303 | * r 304 | * } 305 | * }}} 306 | * 307 | * As long as the condition of a ''while'' statement is `true`, its body is evaluated. 308 | * 309 | * ==For-Loops== 310 | * 311 | * In Scala there is a kind of `for` loop: 312 | * 313 | * {{{ 314 | * for (i <- 1 until 3) { System.out.print(i.toString + " ") } 315 | * }}} 316 | * 317 | * This displays `1 2`. 318 | * 319 | * For-loops translate similarly to for-expressions, but using the `foreach` combinator instead of 320 | * `map` and `flatMap`. 321 | * 322 | * `foreach` is defined on collections with elements of type `A` as follows: 323 | * 324 | * {{{ 325 | * def foreach(f: A => Unit): Unit = 326 | * // apply `f` to each element of the collection 327 | * }}} 328 | * 329 | * Example: 330 | * 331 | * {{{ 332 | * for (i <- 1 until 3; j <- "abc") println(s"$i $j") 333 | * }}} 334 | * 335 | * translates to: 336 | * 337 | * {{{ 338 | * (1 until 3) foreach (i => "abc" foreach (j => println(s"$i $j"))) 339 | * }}} 340 | * 341 | * =Exercise= 342 | * 343 | * Complete the following imperative implementation of `factorial`: 344 | */ 345 | def factorialExercise(res0: Int, res1: Int, res2: Int): Unit = { 346 | def factorial(n: Int): Int = { 347 | var result = res0 348 | var i = res1 349 | while (i <= n) { 350 | result = result * i 351 | i = i + res2 352 | } 353 | result 354 | } 355 | 356 | factorial(2) shouldBe 2 357 | factorial(3) shouldBe 6 358 | factorial(4) shouldBe 24 359 | factorial(5) shouldBe 120 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/LazyEvaluation.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * lazy_evaluation 22 | */ 23 | object LazyEvaluation extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Motivation= 27 | * 28 | * Consider the following program that finds the second prime number between 1000 and 10000: 29 | * 30 | * {{{ 31 | * ((1000 to 10000) filter isPrime)(1) 32 | * }}} 33 | * 34 | * This is ''much'' shorter than the recursive alternative: 35 | * 36 | * {{{ 37 | * def nthPrime(from: Int, to: Int, n: Int): Int = 38 | * if (from >= to) throw new Error("no prime") 39 | * else if (isPrime(from)) 40 | * if (n == 1) from else nthPrime(from + 1, to, n - 1) 41 | * else nthPrime(from + 1, to, n) 42 | * 43 | * def secondPrime(from: Int, to: Int) = nthPrime(from, to, 2) 44 | * }}} 45 | * 46 | * But from a standpoint of performance, the first version is pretty bad; it constructs ''all'' 47 | * prime numbers between `1000` and `10000` in a list, but only ever looks at the first two 48 | * elements of that list. 49 | * 50 | * Reducing the upper bound would speed things up, but risks that we miss the second prime number 51 | * all together. 52 | * 53 | * =Delayed Evaluation= 54 | * 55 | * However, we can make the short-code efficient by using a trick: 56 | * 57 | * - Avoid computing the tail of a sequence until it is needed for the evaluation result (which 58 | * might be never) 59 | * 60 | * This idea is implemented in a new class, the `LazyList`. 61 | * 62 | * LazyLists are similar to lists, but their elements are evaluated only ''on demand''. 63 | * 64 | * =Defining LazyLists= 65 | * 66 | * LazyLists are defined from a constructor `LazyList.cons`. 67 | * 68 | * For instance, 69 | * 70 | * {{{ 71 | * val xs = LazyList.cons(1, LazyList.cons(2, LazyList.empty)) 72 | * }}} 73 | * 74 | * =LazyList Ranges= 75 | * 76 | * Let's try to write a function that returns a `LazyList` representing a range of numbers between 77 | * `lo` and `hi`: 78 | * 79 | * {{{ 80 | * def llRange(lo: Int, hi: Int): LazyList[Int] = 81 | * if (lo >= hi) LazyList.empty 82 | * else LazyList.cons(lo, llRange(lo + 1, hi)) 83 | * }}} 84 | * 85 | * Compare to the same function that produces a list: 86 | * 87 | * {{{ 88 | * def listRange(lo: Int, hi: Int): List[Int] = 89 | * if (lo >= hi) Nil 90 | * else lo :: listRange(lo + 1, hi) 91 | * }}} 92 | * 93 | * The functions have almost identical structure yet they evaluate quite differently. 94 | * 95 | * - `listRange(start, end)` will produce a list with `end - start` elements and return it. 96 | * - `llRange(start, end)` returns a single object of type `LazyList` with `start` as head 97 | * element. 98 | * - The other elements are only computed when they are needed, where “needed” means that 99 | * someone calls `tail` on the stream. 100 | * 101 | * =Methods on LazyLists= 102 | * 103 | * `LazyList` supports almost all methods of `List`. 104 | * 105 | * For instance, to find the second prime number between 1000 and 10000: 106 | * 107 | * {{{ 108 | * (llRange(1000, 10000) filter isPrime)(1) 109 | * }}} 110 | * 111 | * The one major exception is `::`. 112 | * 113 | * `x :: xs` always produces a list, never a lazy list. 114 | * 115 | * There is however an alternative operator `#::` which produces a lazy list. 116 | * 117 | * {{{ 118 | * x #:: xs == LazyList.cons(x, xs) 119 | * }}} 120 | * 121 | * `#::` can be used in expressions as well as patterns. 122 | * 123 | * =Implementation of LazyLists= 124 | * 125 | * The implementation of lazy lists is quite close to the one of lists. 126 | * 127 | * Here's the class `LazyList`: 128 | * 129 | * {{{ 130 | * final class LazyList[+A] ... extends ... { 131 | * override def isEmpty: Boolean = ... 132 | * override def head: A = ... 133 | * override def tail: LazyList[A] = ... 134 | * ... 135 | * } 136 | * }}} 137 | * 138 | * As for lists, all other methods can be defined in terms of these three. 139 | * 140 | * Concrete implementations of streams are defined in the `LazyList.State` companion object. 141 | * Here's a first draft: 142 | * 143 | * {{{ 144 | * private object State { 145 | * object Empty extends State[Nothing] { 146 | * def head: Nothing = throw new NoSuchElementException("head of empty lazy list") 147 | * def tail: LazyList[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list") 148 | * } 149 | * 150 | * final class Cons[A](val head: A, val tail: LazyList[A]) extends State[A] 151 | * } 152 | * }}} 153 | * 154 | * The only important difference between the implementations of `List` and `LazyList` concern 155 | * `tail`, the second parameter of `LazyList.cons`. 156 | * 157 | * For lazy lists, this is a by-name parameter: the type of `tail` starts with `=>`. In such a 158 | * case, this parameter is evaluated by following the rules of the call-by-name model. 159 | * 160 | * That's why the second argument to `LazyList.cons` is not evaluated at the point of call. 161 | * 162 | * Instead, it will be evaluated each time someone calls `tail` on a `LazyList` object. 163 | * 164 | * In Scala 2.13, LazyList (previously Stream) became fully lazy from head to tail. To make it 165 | * possible, methods (`filter`, `flatMap`...) are implemented in a way where the head is not being 166 | * evaluated if is not explicitly indicated. 167 | * 168 | * For instance, here's `filter`: 169 | * 170 | * {{{ 171 | * object LazyList extends SeqFactory[LazyList] { 172 | * ... 173 | * private def filterImpl[A](ll: LazyList[A], p: A => Boolean, isFlipped: Boolean): LazyList[A] = { 174 | * // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD 175 | * var restRef = ll // val restRef = new ObjectRef(ll) 176 | * newLL { 177 | * var elem: A = null.asInstanceOf[A] 178 | * var found = false 179 | * var rest = restRef // var rest = restRef.elem 180 | * while (!found && !rest.isEmpty) { 181 | * elem = rest.head 182 | * found = p(elem) != isFlipped 183 | * rest = rest.tail 184 | * restRef = rest // restRef.elem = rest 185 | * } 186 | * if (found) sCons(elem, filterImpl(rest, p, isFlipped)) else State.Empty 187 | * } 188 | * } 189 | * }}} 190 | * 191 | * =Exercise= 192 | * 193 | * Consider the following modification of `llRange`. When you write `llRange(1, 194 | * 10).take(3).toList` what is the value of `rec`? 195 | * 196 | * Be careful, head is evaluating too! 197 | */ 198 | def llRangeExercise(res0: Int): Unit = { 199 | var rec = 0 200 | def llRange(lo: Int, hi: Int): LazyList[Int] = { 201 | rec = rec + 1 202 | if (lo >= hi) LazyList.empty 203 | else LazyList.cons(lo, llRange(lo + 1, hi)) 204 | } 205 | llRange(1, 10).take(3).toList 206 | rec shouldBe res0 207 | } 208 | 209 | /** 210 | * =Lazy Evaluation= 211 | * 212 | * The proposed `LazyList` implementation suffers from a serious potential performance problem: If 213 | * `tail` is called several times, the corresponding stream will be recomputed each time. 214 | * 215 | * This problem can be avoided by storing the result of the first evaluation of `tail` and 216 | * re-using the stored result instead of recomputing `tail`. 217 | * 218 | * This optimization is sound, since in a purely functional language an expression produces the 219 | * same result each time it is evaluated. 220 | * 221 | * We call this scheme ''lazy evaluation'' (as opposed to ''by-name evaluation'' in the case where 222 | * everything is recomputed, and ''strict evaluation'' for normal parameters and `val` 223 | * definitions.) 224 | * 225 | * ==Lazy Evaluation in Scala== 226 | * 227 | * Haskell is a functional programming language that uses lazy evaluation by default. 228 | * 229 | * Scala uses strict evaluation by default, but allows lazy evaluation of value definitions with 230 | * the `lazy val` form: 231 | * 232 | * {{{ 233 | * lazy val x = expr 234 | * }}} 235 | * 236 | * ==Exercise== 237 | */ 238 | def lazyVal(res0: String): Unit = { 239 | val builder = new StringBuilder 240 | 241 | val x = { builder += 'x'; 1 } 242 | lazy val y = { builder += 'y'; 2 } 243 | def z = { builder += 'z'; 3 } 244 | 245 | z + y + x + z + y + x 246 | 247 | builder.result() shouldBe res0 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/LexicalScopes.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * lexical_scopes 22 | */ 23 | object LexicalScopes extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Nested functions= 27 | * 28 | * It's good functional programming style to split up a task into many small functions. 29 | * 30 | * But the names of functions like `sqrtIter`, `improve`, and `isGoodEnough` (defined in the 31 | * previous section) matter only for the ''implementation'' of `sqrt`, not for its ''usage''. 32 | * 33 | * Normally we would not like users to access these functions directly. 34 | * 35 | * We can achieve this and at the same time avoid “name-space pollution” by putting the auxiliary 36 | * functions inside `sqrt`. 37 | * 38 | * =The `sqrt` Function, Take 2= 39 | * 40 | * {{{ 41 | * def sqrt(x: Double) = { 42 | * def sqrtIter(guess: Double, x: Double): Double = 43 | * if (isGoodEnough(guess, x)) guess 44 | * else sqrtIter(improve(guess, x), x) 45 | * 46 | * def improve(guess: Double, x: Double) = 47 | * (guess + x / guess) / 2 48 | * 49 | * def isGoodEnough(guess: Double, x: Double) = 50 | * abs(square(guess) - x) < 0.001 51 | * 52 | * sqrtIter(1.0, x) 53 | * } 54 | * }}} 55 | * 56 | * =Blocks in Scala= 57 | * 58 | * - A block is delimited by braces `{ ... }`. 59 | * 60 | * {{{ 61 | * { 62 | * val x = f(3) 63 | * x * x 64 | * } 65 | * }}} 66 | * 67 | * - It contains a sequence of definitions or expressions. 68 | * - The last element of a block is an expression that defines its value. 69 | * - This return expression can be preceded by auxiliary definitions. 70 | * - Blocks are themselves expressions; a block may appear everywhere an expression can. 71 | * 72 | * =Blocks and Visibility= 73 | * 74 | * - The definitions inside a block are only visible from within the block. 75 | * - The definitions inside a block ''shadow'' definitions of the same names outside the block. 76 | * 77 | * ==Exercise: Scope Rules== 78 | * 79 | * What is the value of `result` in the following program? 80 | */ 81 | def scopeRules(res0: Int): Unit = { 82 | val x = 0 83 | def f(y: Int) = y + 1 84 | val result = { 85 | val x = f(3) 86 | x * x 87 | } + x 88 | result shouldBe res0 89 | } 90 | 91 | /** 92 | * =Lexical Scoping= 93 | * 94 | * Definitions of outer blocks are visible inside a block unless they are shadowed. Shadowed 95 | * definitions are ones which are redefined in a lower scope. 96 | * 97 | * Therefore, we can simplify `sqrt` by eliminating redundant occurrences of the `x` parameter, 98 | * which means the same thing everywhere: 99 | * 100 | * =The `sqrt` Function, Take 3= 101 | * 102 | * {{{ 103 | * def sqrt(x: Double) = { 104 | * def sqrtIter(guess: Double): Double = 105 | * if (isGoodEnough(guess)) guess 106 | * else sqrtIter(improve(guess)) 107 | * 108 | * def improve(guess: Double) = 109 | * (guess + x / guess) / 2 110 | * 111 | * def isGoodEnough(guess: Double) = 112 | * abs(square(guess) - x) < 0.001 113 | * 114 | * sqrtIter(1.0) 115 | * } 116 | * }}} 117 | * 118 | * =Semicolons= 119 | * 120 | * In Scala, semicolons at the end of lines are in most cases optional. 121 | * 122 | * You could write: 123 | * 124 | * {{{ 125 | * val x = 1; 126 | * }}} 127 | * 128 | * but most people would omit the semicolon. 129 | * 130 | * On the other hand, if there is more than one statement on a line, they need to be separated by 131 | * semicolons: 132 | * 133 | * {{{ 134 | * val y = x + 1; y * y 135 | * }}} 136 | * 137 | * =Semicolons and infix operators= 138 | * 139 | * One issue with Scala's semicolon convention is how to write expressions that span several 140 | * lines. For instance: 141 | * 142 | * {{{ 143 | * someLongExpression 144 | * + someOtherExpression 145 | * }}} 146 | * 147 | * would be interpreted as ''two'' expressions: 148 | * 149 | * {{{ 150 | * someLongExpression; 151 | * + someOtherExpression 152 | * }}} 153 | * 154 | * There are two ways to overcome this problem. 155 | * 156 | * You could write the multi-line expression in parentheses, because semicolons are never inserted 157 | * inside `(…)`: 158 | * 159 | * {{{ 160 | * (someLongExpression 161 | * + someOtherExpression) 162 | * }}} 163 | * 164 | * Or you could write the operator on the first line, because this tells the Scala compiler that 165 | * the expression is not yet finished: 166 | * 167 | * {{{ 168 | * someLongExpression + 169 | * someOtherExpression 170 | * }}} 171 | * 172 | * =Top-Level Definitions= 173 | * 174 | * In real Scala programs, `def` and `val` definitions must be written within a top-level ''object 175 | * definition'', in a .scala file: 176 | * 177 | * {{{ 178 | * object MyExecutableProgram { 179 | * val myVal = … 180 | * def myMethod = … 181 | * } 182 | * }}} 183 | * 184 | * The above code defines an ''object'' named `MyExecutableProgram`. You can refer to its 185 | * ''members'' using the usual dot notation: 186 | * 187 | * {{{ 188 | * MyExecutableProgram.myMethod 189 | * }}} 190 | * 191 | * The definition of `MyExecutableProgram` is ''top-level'' because it is not nested within 192 | * another definition. 193 | * 194 | * =Packages and Imports= 195 | * 196 | * Top-level definitions can be organized in ''packages''. To place a class or object inside a 197 | * package, use a package clause at the top of your source file: 198 | * 199 | * {{{ 200 | * // file foo/Bar.scala 201 | * package foo 202 | * object Bar { … } 203 | * }}} 204 | * 205 | * {{{ 206 | * // file foo/Baz.scala 207 | * package foo 208 | * object Baz { … } 209 | * }}} 210 | * 211 | * Definitions located in a package are visible from other definitions located in the same 212 | * package: 213 | * 214 | * {{{ 215 | * // file foo/Baz.scala 216 | * package foo 217 | * object Baz { 218 | * // Bar is visible because it is in the `foo` package too 219 | * Bar.someMethod 220 | * } 221 | * }}} 222 | * 223 | * On the other hand, definitions located in other packages are not directly visible: you must use 224 | * ''fully qualified names'' to refer to them: 225 | * 226 | * {{{ 227 | * // file quux/Quux.scala 228 | * package quux 229 | * object Quux { 230 | * foo.Bar.someMethod 231 | * } 232 | * }}} 233 | * 234 | * Finally, you can import names to avoid repeating their fully qualified form: 235 | * 236 | * {{{ 237 | * // file quux/Quux.scala 238 | * package quux 239 | * import foo.Bar 240 | * object Quux { 241 | * // Bar refers to the imported `foo.Bar` 242 | * Bar.someMethod 243 | * } 244 | * }}} 245 | * 246 | * =Automatic Imports= 247 | * 248 | * Some entities are automatically imported in any Scala program. 249 | * 250 | * These are: 251 | * 252 | * - All members of package `scala` 253 | * - All members of package `java.lang` 254 | * - All members of the singleton object `scala.Predef`. 255 | * 256 | * Here are the fully qualified names of some types and functions which you have seen so far: 257 | * 258 | * {{{ 259 | * Int scala.Int 260 | * Boolean scala.Boolean 261 | * Object java.lang.Object 262 | * String java.lang.String 263 | * }}} 264 | * 265 | * =Writing Executable Programs= 266 | * 267 | * So far our examples of code were executed from your Web browser, but it is also possible to 268 | * create standalone applications in Scala. 269 | * 270 | * Each such application contains an object with a `main` method. 271 | * 272 | * For instance, here is the "Hello World!" program in Scala: 273 | * 274 | * {{{ 275 | * object Hello { 276 | * def main(args: Array[String]) = println("hello world!") 277 | * } 278 | * }}} 279 | * 280 | * Once this program is compiled, you can start it from the command line with 281 | * 282 | * {{{ 283 | * $ scala Hello 284 | * }}} 285 | * 286 | * =Exercise= 287 | */ 288 | def objectScopes(res0: Int): Unit = { 289 | object Foo { 290 | val x = 1 291 | } 292 | object Bar { 293 | val x = 2 294 | } 295 | object Baz { 296 | import Bar.x 297 | val y = x + Foo.x 298 | } 299 | 300 | Baz.y shouldBe res0 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/ScalaTutorialSection.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalaexercises.definitions.Section 20 | import org.scalatest.flatspec.AnyFlatSpec 21 | import org.scalatest.matchers.should.Matchers 22 | 23 | trait ScalaTutorialSection extends AnyFlatSpec with Matchers with Section 24 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/StandardLibrary.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * standard_library 22 | */ 23 | object StandardLibrary extends ScalaTutorialSection { 24 | 25 | /** 26 | * =List= 27 | * 28 | * The list is a fundamental data structure in functional programming. 29 | * 30 | * A list having `x1`, …, `xn` as elements is written `List(x1, …, xn)`: 31 | * 32 | * {{{ 33 | * val fruit = List("apples", "oranges", "pears") 34 | * val nums = List(1, 2, 3, 4) 35 | * val diag3 = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) 36 | * val empty = List() 37 | * }}} 38 | * 39 | * - Lists are immutable --- the elements of a list cannot be changed, 40 | * - Lists are recursive (as you will see in the next subsection), 41 | * - Lists are ''homogeneous'': A list is intended to be composed of elements that all have the 42 | * same type. 43 | * 44 | * That's because when you create a `List` of elements with different types it will look for a 45 | * common ancestor. The common ancestor for all types is `Any` 46 | * 47 | * {{{ 48 | * val heterogeneousList: List[Any] = List(1, "1", '1') 49 | * }}} 50 | * 51 | * The type of a list with elements of type `T` is written `List[T]`: 52 | * 53 | * {{{ 54 | * val fruit: List[String] = List("apples", "oranges", "pears") 55 | * val nums : List[Int] = List(1, 2, 3, 4) 56 | * val diag3: List[List[Int]] = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) 57 | * val empty: List[Nothing] = List() 58 | * }}} 59 | * 60 | * ==Constructors of Lists== 61 | * 62 | * Actually, all lists are constructed from: 63 | * 64 | * - the empty list `Nil`, and 65 | * - the construction operation `::` (pronounced ''cons''): `x :: xs` gives a new list with the 66 | * first element `x`, called the `head`, followed by the `tail` `xs`, which is itself a list 67 | * of elements. 68 | * 69 | * For example: 70 | * 71 | * {{{ 72 | * val fruit = "apples" :: ("oranges" :: ("pears" :: Nil)) 73 | * val nums = 1 :: (2 :: (3 :: (4 :: Nil))) 74 | * val empty = Nil 75 | * }}} 76 | * 77 | * ===Right Associativity=== 78 | * 79 | * Convention: Operators ending in “`:`” associate to the right. 80 | * 81 | * `A :: B :: C` is interpreted as `A :: (B :: C)`. 82 | * 83 | * We can thus omit the parentheses in the definition above. 84 | * 85 | * {{{ 86 | * val nums = 1 :: 2 :: 3 :: 4 :: Nil 87 | * }}} 88 | * 89 | * Operators ending in “`:`” are also different in the fact that they are seen as method calls of 90 | * the ''right-hand'' operand. 91 | * 92 | * So the expression above is equivalent to: 93 | * 94 | * {{{ 95 | * val nums = Nil.::(4).::(3).::(2).::(1) 96 | * }}} 97 | * 98 | * ==Manipulating Lists== 99 | * 100 | * It is possible to decompose lists with pattern matching: 101 | * 102 | * - `Nil`: the `Nil` constant, 103 | * - `p :: ps`: A pattern that matches a list with a `head` matching `p` and a `tail` matching 104 | * `ps`. 105 | * 106 | * {{{ 107 | * nums match { 108 | * // Lists of `Int` that starts with `1` and then `2` 109 | * case 1 :: 2 :: xs => … 110 | * // Lists of length 1 111 | * case x :: Nil => … 112 | * // Same as `x :: Nil` 113 | * case List(x) => … 114 | * // The empty list, same as `Nil` 115 | * case List() => 116 | * // A list that contains as only element another list that starts with `2` 117 | * case List(2 :: xs) => … 118 | * } 119 | * }}} 120 | * 121 | * ==Exercise: Sorting Lists== 122 | * 123 | * Suppose we want to sort a list of numbers in ascending order: 124 | * 125 | * - One way to sort the list `List(7, 3, 9, 2)` is to sort the tail `List(3, 9, 2)` to obtain 126 | * `List(2, 3, 9)`. 127 | * - The next step is to insert the head `7` in the right place to obtain the result `List(2, 3, 128 | * 7, 9)`. 129 | * 130 | * This idea describes ''Insertion Sort'': 131 | * 132 | * {{{ 133 | * def insertionSort(xs: List[Int]): List[Int] = xs match { 134 | * case List() => List() 135 | * case y :: ys => insert(y, insertionSort(ys)) 136 | * } 137 | * }}} 138 | * 139 | * Complete the definition insertion sort by filling in the blanks in the definition below: 140 | */ 141 | def insertionSort(res0: (Int, Int) => Boolean, res1: List[Int]): Unit = { 142 | val cond: (Int, Int) => Boolean = res0 143 | def insert(x: Int, xs: List[Int]): List[Int] = 144 | xs match { 145 | case List() => x :: res1 146 | case y :: ys => 147 | if (cond(x, y)) x :: y :: ys 148 | else y :: insert(x, ys) 149 | } 150 | insert(2, 1 :: 3 :: Nil) shouldBe (1 :: 2 :: 3 :: Nil) 151 | insert(1, 2 :: 3 :: Nil) shouldBe (1 :: 2 :: 3 :: Nil) 152 | insert(3, 1 :: 2 :: Nil) shouldBe (1 :: 2 :: 3 :: Nil) 153 | } 154 | 155 | /** 156 | * ==Common Operations on Lists== 157 | * 158 | * Transform the elements of a list using `map`: 159 | * 160 | * {{{ 161 | * List(1, 2, 3).map(x => x + 1) == List(2, 3, 4) 162 | * }}} 163 | * 164 | * Filter elements using `filter`: 165 | * 166 | * {{{ 167 | * List(1, 2, 3).filter(x => x % 2 == 0) == List(2) 168 | * }}} 169 | * 170 | * Transform each element of a list into a list and flatten the results into a single list using 171 | * `flatMap`: 172 | * 173 | * {{{ 174 | * val xs = 175 | * List(1, 2, 3).flatMap { x => 176 | * List(x, 2 * x, 3 * x) 177 | * } 178 | * xs == List(1, 2, 3, 2, 4, 6, 3, 6, 9) 179 | * }}} 180 | * 181 | * =Optional Values= 182 | * 183 | * We represent an optional value of type `A` with the type `Option[A]`. This is useful to 184 | * implement, for instance, partially defined functions: 185 | * 186 | * {{{ 187 | * // The `sqrt` function is not defined for negative values 188 | * def sqrt(x: Double): Option[Double] = … 189 | * }}} 190 | * 191 | * An `Option[A]` can either be `None` (if there is no value) or `Some[A]` (if there is a value): 192 | * 193 | * {{{ 194 | * def sqrt(x: Double): Option[Double] = 195 | * if (x < 0) None else Some(…) 196 | * }}} 197 | * 198 | * ==Manipulating Options== 199 | * 200 | * It is possible to decompose options with pattern matching: 201 | * 202 | * {{{ 203 | * def foo(x: Double): String = 204 | * sqrt(x) match { 205 | * case None => "no result" 206 | * case Some(y) => y.toString 207 | * } 208 | * }}} 209 | * 210 | * ==Common Operations on Options== 211 | * 212 | * Transform an optional value with `map`: 213 | */ 214 | def optionMap(res0: Option[Int], res1: Option[Int]): Unit = { 215 | Some(1).map(x => x + 1) shouldBe Some(2) 216 | None.map((x: Int) => x + 1) shouldBe None 217 | res0.map(x => x + 1) shouldBe res1 218 | } 219 | 220 | /** 221 | * Filter values with `filter`: 222 | */ 223 | def optionFilter(res0: Option[Int], res1: Option[Int]): Unit = { 224 | Some(1).filter(x => x % 2 == 0) shouldBe None 225 | Some(2).filter(x => x % 2 == 0) shouldBe Some(2) 226 | res0.filter(x => x % 2 == 0) shouldBe res1 227 | } 228 | 229 | /** 230 | * Use `flatMap` to transform a successful value into an optional value: 231 | */ 232 | def optionFlatMap(res0: Option[Int], res1: Option[Int]): Unit = { 233 | Some(1).flatMap(x => Some(x + 1)) shouldBe Some(2) 234 | None.flatMap((x: Int) => Some(x + 1)) shouldBe None 235 | res0.flatMap(x => Some(x + 1)) shouldBe res1 236 | } 237 | 238 | /** 239 | * =Error Handling= 240 | * 241 | * This subsection introduces types that are useful to handle failures. 242 | * 243 | * ==Try== 244 | * 245 | * `Try[A]` represents a computation that attempted to return an `A`. It can either be: 246 | * - a `Success[A]`, 247 | * - or a `Failure`. 248 | * 249 | * The key difference between `None` and `Failure`s is that the latter provide the reason for the 250 | * failure: 251 | * 252 | * {{{ 253 | * def sqrt(x: Double): Try[Double] = 254 | * if (x < 0) Failure(new IllegalArgumentException("x must be positive")) 255 | * else Success(…) 256 | * }}} 257 | * 258 | * ===Manipulating `Try[A]` values=== 259 | * 260 | * Like options and lists, `Try[A]` is an algebraic data type, so it can be decomposed using 261 | * pattern matching. 262 | * 263 | * `Try[A]` also have `map`, `filter` and `flatMap`. They behave the same as with `Option[A]`, 264 | * except that any exception that is thrown during their execution is converted into a `Failure`. 265 | * 266 | * ==Either== 267 | * 268 | * `Either` can also be useful to handle failures. Basically, the type `Either[A, B]` represents a 269 | * value that can either be of type `A` or of type `B`. It can be decomposed in two cases: `Left` 270 | * or `Right`. 271 | * 272 | * You can use one case to represent the failure and the other to represent the success. What 273 | * makes it different from `Try` is that you can choose a type other than `Throwable` to represent 274 | * the exception. Another difference is that exceptions that occur when transforming `Either` 275 | * values are not converted into failures. 276 | * 277 | * {{{ 278 | * def sqrt(x: Double): Either[String, Double] = 279 | * if (x < 0) Left("x must be positive") 280 | * else Right(…) 281 | * }}} 282 | * 283 | * ===Manipulating `Either[A, B]` Values=== 284 | * 285 | * Since Scala 2.12, `Either` has `map` and `flatMap`. These methods transform the `Right` case 286 | * only. We say that `Either` is “right biased”: 287 | * 288 | * {{{ 289 | * Right(1).map((x: Int) => x + 1) shouldBe Right(2) 290 | * Left("foo").map((x: Int) => x + 1) shouldBe Left("foo") 291 | * }}} 292 | * 293 | * `Either` also has a `filterOrElse` method that turns a `Right` value into a `Left` value if it 294 | * does not satisfy a given predicate: 295 | * 296 | * {{{ 297 | * Right(1).filterOrElse(x => x % 2 == 0, "Odd value") shouldBe Left("Odd value") 298 | * }}} 299 | * 300 | * Specify which “side” (`Left` or `Right`) you wanted to `map`: 301 | */ 302 | def either(res0: Either[String, Int], res1: Either[String, Int]): Unit = { 303 | def triple(x: Int): Int = 3 * x 304 | 305 | def tripleEither(x: Either[String, Int]): Either[String, Int] = 306 | x.map(triple) 307 | 308 | tripleEither(Right(1)) shouldBe res0 309 | tripleEither(Left("not a number")) shouldBe res1 310 | } 311 | 312 | } 313 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/StructuringInformation.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * structuring_information 22 | */ 23 | object StructuringInformation extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Introduction= 27 | * 28 | * Programs are systems that process information. Therefore, programming languages provide ways to 29 | * model the domain of a program. 30 | * 31 | * This section introduces the ways you can structure information in Scala. We will base our 32 | * examples on the following domain, a ''music sheet'': 33 | * 34 | * 35 | * 36 | * =Aggregating Information With Case Classes= 37 | * 38 | * First, let’s focus on ''notes''. Suppose that, in our program, we are interested in the 39 | * following properties of notes: their 40 | * [[https://en.wikipedia.org/wiki/Musical_note#12-tone_chromatic_scale name]] (A, B, C, etc.), 41 | * their [[https://en.wikipedia.org/wiki/Note_value duration]] (whole, half, quarter, etc.) and 42 | * their octave number. 43 | * 44 | * In summary, our note model ''aggregates'' several data (name, duration and octave). We express 45 | * this in Scala by using a ''case class'' definition: 46 | * 47 | * {{{ 48 | * case class Note( 49 | * name: String, 50 | * duration: String, 51 | * octave: Int 52 | * ) 53 | * }}} 54 | * 55 | * This definition introduces a new type, `Note`. You can create values of this type by calling 56 | * its ''constructor'': 57 | * 58 | * {{{ 59 | * val c3 = Note("C", "Quarter", 3) 60 | * }}} 61 | * 62 | * `c3` is a value that aggregates the arguments passed to the `Note` constructor. 63 | * 64 | * Then, you can retrieve the information carried by each ''member'' (`name`, `duration` and 65 | * `octave`) by using the dot notation: 66 | */ 67 | def caseClassProjection(res0: String, res1: Int): Unit = { 68 | case class Note(name: String, duration: String, octave: Int) 69 | val c3 = Note("C", "Quarter", 3) 70 | c3.name shouldBe "C" 71 | c3.duration shouldBe res0 72 | c3.octave shouldBe res1 73 | } 74 | 75 | /** 76 | * =Defining Alternatives With Sealed Traits= 77 | * 78 | * If we look at the introductory picture, we see that musical symbols can be either ''notes'' or 79 | * ''rests'' (but nothing else). 80 | * 81 | * So, we want to introduce the concept of ''symbol'', as something that can be embodied by a 82 | * fixed set of alternatives: a note or rest. We can express this in Scala using a ''sealed 83 | * trait'' definition: 84 | * 85 | * {{{ 86 | * sealed trait Symbol 87 | * case class Note(name: String, duration: String, octave: Int) extends Symbol 88 | * case class Rest(duration: String) extends Symbol 89 | * }}} 90 | * 91 | * A sealed trait definition introduces a new type (here, `Symbol`), but no constructor for it. 92 | * Constructors are defined by alternatives that ''extend'' the sealed trait: 93 | * 94 | * {{{ 95 | * val symbol1: Symbol = Note("C", "Quarter", 3) 96 | * val symbol2: Symbol = Rest("Whole") 97 | * }}} 98 | * 99 | * =Pattern Matching= 100 | * 101 | * Since the `Symbol` type has no members, we can not do anything useful when we manipulate one. 102 | * We need a way to distinguish between the different cases of symbols. ''Pattern matching'' 103 | * allows us to do so: 104 | * 105 | * {{{ 106 | * def symbolDuration(symbol: Symbol): String = 107 | * symbol match { 108 | * case Note(name, duration, octave) => duration 109 | * case Rest(duration) => duration 110 | * } 111 | * }}} 112 | * 113 | * The above `match` expression first checks if the given `Symbol` is a `Note`, and if it is the 114 | * case it extracts its fields (`name`, `duration` and `octave`) and evaluates the expression at 115 | * the right of the arrow. Otherwise, it checks if the given `Symbol` is a `Rest`, and if it is 116 | * the case it extracts its `duration` field, and evaluates the expression at the right of the 117 | * arrow. 118 | * 119 | * When we write `case Rest(duration) => …`, we say that `Rest(…)` is a ''constructor pattern'': 120 | * it matches all the values of type `Rest` that have been constructed with arguments matching the 121 | * pattern `duration`. 122 | * 123 | * The pattern `duration` is called a ''variable pattern''. It matches any value and ''binds'' its 124 | * name (here, `duration`) to this value. 125 | * 126 | * More generally, an expression of the form 127 | * 128 | * {{{ 129 | * e match { 130 | * case p1 => e1 131 | * case p2 => e2 132 | * … 133 | * case pn => en 134 | * } 135 | * }}} 136 | * 137 | * matches the value of the selector `e` with the patterns `p1`, …, `pn` in the order in which 138 | * they are written. 139 | * 140 | * The whole match expression is rewritten to the right-hand side of the first case where the 141 | * pattern matches the selector `e`. 142 | * 143 | * References to pattern variables are replaced by the corresponding parts in the selector. 144 | * 145 | * ==Exhaustivity== 146 | * 147 | * Having defined `Symbol` as a sealed trait gives us the guarantee that the possible case of 148 | * symbols is fixed. The compiler can leverage this knowledge to warn us if we write code that 149 | * does not handle ''all'' the cases: 150 | * {{{ 151 | * def unexhaustive(): Unit = { 152 | * sealed trait Symbol 153 | * case class Note(name: String, duration: String, octave: Int) extends Symbol 154 | * case class Rest(duration: String) extends Symbol 155 | * 156 | * def nonExhaustiveDuration(symbol: Symbol): String = 157 | * symbol match { 158 | * case Rest(duration) => duration 159 | * } 160 | * } 161 | * }}} 162 | * 163 | * If we try to run the above code to see how the compiler informs us that we don’t handle all the 164 | * cases in `nonExhaustiveDuration`. 165 | * 166 | * =Equals= 167 | * 168 | * It is worth noting that, since the purpose of case classes is to aggregate values, comparing 169 | * case class instances compares their values: 170 | */ 171 | def caseClassEquals(res0: Boolean, res1: Boolean): Unit = { 172 | case class Note(name: String, duration: String, octave: Int) 173 | val c3 = Note("C", "Quarter", 3) 174 | val otherC3 = Note("C", "Quarter", 3) 175 | val f3 = Note("F", "Quarter", 3) 176 | (c3 == otherC3) shouldBe res0 177 | (c3 == f3) shouldBe res1 178 | } 179 | 180 | /** 181 | * =Enumerations= 182 | * 183 | * Our above definition of the `Note` type allows users to create instances with invalid names and 184 | * durations: 185 | * 186 | * {{{ 187 | * val invalidNote = Note("not a name", "not a duration", 3) 188 | * }}} 189 | * 190 | * It is generally a bad idea to work with a model that makes it possible to reach invalid states. 191 | * In our case, we want to restrict the space of the possible note names and durations to a set of 192 | * fixed alternatives. 193 | * 194 | * In the case of note names, the alternatives are either `A`, `B`, `C`, `D`, `E`, `F` or `G`. We 195 | * can express the fact that note names are a fixed set of alternatives by using a sealed trait, 196 | * but in contrast to the previous example alternatives are not case classes because they 197 | * aggregate no information: 198 | * 199 | * {{{ 200 | * sealed trait NoteName 201 | * case object A extends NoteName 202 | * case object B extends NoteName 203 | * case object C extends NoteName 204 | * … 205 | * case object G extends NoteName 206 | * }}} 207 | * 208 | * =Algebraic Data Types= 209 | * 210 | * Data types defined with sealed trait and case classes are called ''algebraic data types''. An 211 | * algebraic data type definition can be thought of as a set of possible values. 212 | * 213 | * Algebraic data types are a powerful way to structure information. 214 | * 215 | * If a concept of your program’s domain can be formulated in terms of an ''is'' relationship, you 216 | * will express it with a sealed trait: 217 | * 218 | * “A symbol ''is'' either a note ''or'' a rest.” 219 | * 220 | * {{{ 221 | * sealed trait Symbol 222 | * case class Note(…) extends Symbol 223 | * case class Rest(…) extends Symbol 224 | * }}} 225 | * 226 | * On the other hand, if a concept of your program’s domain can be formulated in terms of an 227 | * ''has'' relationship, you will express it with a case class: 228 | * 229 | * “A note ''has'' a name, a duration ''and'' an octave number.” 230 | * 231 | * {{{ 232 | * case class Note(name: String, duration: String, octave: Int) extends Symbol 233 | * }}} 234 | * 235 | * =Exercise= 236 | * 237 | * Consider the following algebraic data type that models note durations. Complete the 238 | * implementation of the function `fractionOfWhole`, which takes a duration as a parameter and 239 | * returns the corresponding fraction of the `Whole` duration. 240 | */ 241 | def adts(res0: Double, res1: Double): Unit = { 242 | sealed trait Duration 243 | case object Whole extends Duration 244 | case object Half extends Duration 245 | case object Quarter extends Duration 246 | 247 | def fractionOfWhole(duration: Duration): Double = 248 | duration match { 249 | case Whole => 1.0 250 | case Half => res0 251 | case Quarter => res1 252 | } 253 | 254 | fractionOfWhole(Half) shouldBe 0.5 255 | fractionOfWhole(Quarter) shouldBe 0.25 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/SyntacticConveniences.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * syntactic_conveniences 22 | */ 23 | object SyntacticConveniences extends ScalaTutorialSection { 24 | 25 | /** 26 | * This section introduces several syntactic sugars supported by the language. 27 | * 28 | * =String Interpolation= 29 | * 30 | * To splice values into constant `String` at runtime, you can use ''string interpolation'': 31 | */ 32 | def stringInterpolation(res0: String): Unit = { 33 | def greet(name: String): String = 34 | s"Hello, $name!" 35 | 36 | greet("Scala") shouldBe "Hello, Scala!" 37 | greet("Functional Programming") shouldBe res0 38 | } 39 | 40 | /** 41 | * After having prefixed the string literal with `s` you can introduce dynamic values in it with 42 | * `$`. 43 | * 44 | * If you want to splice a complex expression (more than just an identifier), surround it with 45 | * braces: 46 | */ 47 | def stringInterpolation2(res0: String): Unit = { 48 | def greet(name: String): String = 49 | s"Hello, ${name.toUpperCase}!" 50 | 51 | greet("Scala") shouldBe res0 52 | } 53 | 54 | /** 55 | * =Tuples= 56 | * 57 | * We saw earlier that case classes are useful to aggregate information. However, sometimes you 58 | * want to aggregate information without having to define a complete case class for it. In such a 59 | * case you can use ''tuples'': 60 | */ 61 | def tuples(res0: (Int, String)): Unit = { 62 | def pair(i: Int, s: String): (Int, String) = (i, s) 63 | 64 | pair(42, "foo") shouldBe (42, "foo") 65 | pair(0, "bar") shouldBe res0 66 | } 67 | 68 | /** 69 | * In the above example, the type `(Int, String)` represents a pair whose first element is an 70 | * `Int` and whose second element is a `String`. 71 | * 72 | * Similarly, the value `(i, s)` is a pair whose first element is `i` and whose second element is 73 | * `s`. 74 | * 75 | * More generally, a type `(T1, …, Tn)` is a ''tuple type'' of n elements whose i^th^ element has 76 | * type `Ti`. 77 | * 78 | * And a value `(t1, … tn)` is a ''tuple value'' of n elements. 79 | * 80 | * ==Manipulating Tuples== 81 | * 82 | * You can retrieve the elements of a tuple by using a ''tuple pattern'': 83 | */ 84 | def tupleExtraction(res0: String): Unit = { 85 | val is: (Int, String) = (42, "foo") 86 | 87 | is match { 88 | case (i, s) => 89 | i shouldBe 42 90 | s shouldBe res0 91 | } 92 | } 93 | 94 | /** 95 | * Or, simply: 96 | */ 97 | def tupleExtraction2(res0: String): Unit = { 98 | val is: (Int, String) = (42, "foo") 99 | 100 | val (i, s) = is 101 | i shouldBe 42 102 | s shouldBe res0 103 | } 104 | 105 | /** 106 | * Alternatively, you can retrieve the 1st element with the `_1` member, the 2nd element with the 107 | * `_2` member, etc: 108 | */ 109 | def tupleManipulation(res0: String): Unit = { 110 | val is: (Int, String) = (42, "foo") 111 | is._1 shouldBe 42 112 | is._2 shouldBe res0 113 | } 114 | 115 | /** 116 | * =Functions as Objects= 117 | * 118 | * We have seen that Scala's numeric types and the `Boolean` type can be implemented like normal 119 | * classes. 120 | * 121 | * But what about functions? 122 | * 123 | * In fact function values ''are'' treated as objects in Scala. 124 | * 125 | * The function type `A => B` is just an abbreviation for the class `scala.Function1[A, B]`, which 126 | * is defined as follows. 127 | * 128 | * {{{ 129 | * package scala 130 | * trait Function1[A, B] { 131 | * def apply(x: A): B 132 | * } 133 | * }}} 134 | * 135 | * So functions are objects with `apply` methods. 136 | * 137 | * There are also traits `Function2`, `Function3`, ... for functions which take more parameters 138 | * (currently up to 22). 139 | * 140 | * ==Expansion of Function Values== 141 | * 142 | * An anonymous function such as 143 | * 144 | * {{{ 145 | * (x: Int) => x * x 146 | * }}} 147 | * 148 | * is expanded to: 149 | * 150 | * {{{ 151 | * { 152 | * class AnonFun extends Function1[Int, Int] { 153 | * def apply(x: Int) = x * x 154 | * } 155 | * new AnonFun 156 | * } 157 | * }}} 158 | * 159 | * or, shorter, using ''anonymous class syntax'': 160 | * 161 | * {{{ 162 | * new Function1[Int, Int] { 163 | * def apply(x: Int) = x * x 164 | * } 165 | * }}} 166 | * 167 | * ==Expansion of Function Calls== 168 | * 169 | * A function call, such as `f(a, b)`, where `f` is a value of some class type, is expanded to: 170 | * 171 | * {{{ 172 | * f.apply(a, b) 173 | * }}} 174 | * 175 | * So the OO-translation of: 176 | * 177 | * {{{ 178 | * val f = (x: Int) => x * x 179 | * f(7) 180 | * }}} 181 | * 182 | * would be: 183 | * 184 | * {{{ 185 | * val f = new Function1[Int, Int] { 186 | * def apply(x: Int) = x * x 187 | * } 188 | * f.apply(7) 189 | * }}} 190 | * 191 | * ==Functions and Methods== 192 | * 193 | * Note that a method such as 194 | * 195 | * {{{ 196 | * def f(x: Int): Boolean = … 197 | * }}} 198 | * 199 | * is not itself a function value. 200 | * 201 | * But if `f` is used in a place where a Function type is expected, it is converted automatically 202 | * to the function value 203 | * 204 | * {{{ 205 | * (x: Int) => f(x) 206 | * }}} 207 | * 208 | * =`for` expressions= 209 | * 210 | * You probably noticed that several data types of the standard library have methods named `map`, 211 | * `flatMap` and `filter`. 212 | * 213 | * These methods are so common in practice that Scala supports a dedicated syntax: ''for 214 | * expressions''. 215 | * 216 | * ==`map`== 217 | * 218 | * Thus, instead of writing the following: 219 | * 220 | * {{{ 221 | * xs.map(x => x + 1) 222 | * }}} 223 | * 224 | * You can write: 225 | * 226 | * {{{ 227 | * for (x <- xs) yield x + 1 228 | * }}} 229 | * 230 | * You can read it as “for every value, that I name ‘x’, in ‘xs’, return ‘x + 1’”. 231 | * 232 | * ==`filter`== 233 | * 234 | * Also, instead of writing the following: 235 | * 236 | * {{{ 237 | * xs.filter(x => x % 2 == 0) 238 | * }}} 239 | * 240 | * You can write: 241 | * 242 | * {{{ 243 | * for (x <- xs if x % 2 == 0) yield x 244 | * }}} 245 | * 246 | * The benefit of this syntax becomes more apparent when it is combined with the previous one: 247 | * 248 | * {{{ 249 | * for (x <- xs if x % 2 == 0) yield x + 1 250 | * 251 | * // Equivalent to the following: 252 | * xs.filter(x => x % 2 == 0).map(x => x + 1) 253 | * }}} 254 | * 255 | * ==`flatMap`== 256 | * 257 | * Finally, instead of writing the following: 258 | * 259 | * {{{ 260 | * xs.flatMap(x => ys.map(y => (x, y))) 261 | * }}} 262 | * 263 | * You can write: 264 | * 265 | * {{{ 266 | * for (x <- xs; y <- ys) yield (x, y) 267 | * }}} 268 | * 269 | * You can read it as “for every value ‘x’ in ‘xs’, and then for every value ‘y’ in ‘ys’, return 270 | * ‘(x, y)’”. 271 | * 272 | * ==Putting Things Together== 273 | * 274 | * Here is an example that puts everything together: 275 | * 276 | * {{{ 277 | * for { 278 | * x <- xs if x % 2 == 0 279 | * y <- ys 280 | * } yield (x, y) 281 | * }}} 282 | * 283 | * The equivalent de-sugared code is the following: 284 | * 285 | * {{{ 286 | * xs.filter { x => 287 | * x % 2 == 0 288 | * }.flatMap { x => 289 | * ys.map { y => 290 | * (x, y) 291 | * } 292 | * } 293 | * }}} 294 | * 295 | * =Method’s Parameters= 296 | * 297 | * ==Named Parameters== 298 | * 299 | * It can sometimes be difficult to figure out what is the meaning of each parameter passed to a 300 | * function. Consider for instance the following expression: 301 | * 302 | * {{{ 303 | * Range(1, 10, 2) 304 | * }}} 305 | * 306 | * What does it mean? We can improve the readability by using ''named parameters''. 307 | * 308 | * Based on the fact that the `Range` constructor is defined as follows: 309 | * 310 | * {{{ 311 | * case class Range(start: Int, end: Int, step: Int) 312 | * }}} 313 | * 314 | * We can rewrite our expression as follows: 315 | * 316 | * {{{ 317 | * Range(start = 1, end = 10, step = 2) 318 | * }}} 319 | * 320 | * It is now clearer that this expression defines a range of numbers from 1 to 10 by increments of 321 | * 2. 322 | * 323 | * ==Default Values== 324 | * 325 | * Methods’ parameters can have default values. Let’s refine the `Range` constructor: 326 | * 327 | * {{{ 328 | * case class Range(start: Int, end: Int, step: Int = 1) 329 | * }}} 330 | * 331 | * Here, we say that the `step` parameter has a default value, `1`. 332 | * 333 | * Then, at use site we can omit to supply this parameter and the compiler will supply it for us, 334 | * by using its default value: 335 | */ 336 | def defaultParameters(res0: Int): Unit = { 337 | case class Range(start: Int, end: Int, step: Int = 1) 338 | 339 | val xs = Range(start = 1, end = 10) 340 | 341 | xs.step shouldBe res0 342 | } 343 | 344 | /** 345 | * ==Repeated Parameters== 346 | * 347 | * You can define a function that can receive an arbitrary number of parameters (of the same type) 348 | * as follows: 349 | */ 350 | def repeatedParameters(res0: Double): Unit = { 351 | def average(x: Int, xs: Int*): Double = 352 | (x :: xs.toList).sum.toDouble / (xs.size + 1) 353 | 354 | average(1) shouldBe 1.0 355 | average(1, 2) shouldBe 1.5 356 | average(1, 2, 3) shouldBe res0 357 | } 358 | 359 | /** 360 | * The `average` function takes at least one `Int` parameter and then an arbitrary number of other 361 | * values and computes their average. By forcing users to supply at least one parameter, we make 362 | * it impossible for them to compute the average of an empty list of numbers. 363 | * 364 | * Sometimes you want to supply each element of a list as many parameters. You can do that by 365 | * adding a `: _*` type ascription to your list: 366 | * 367 | * {{{ 368 | * val xs: List[Int] = … 369 | * average(1, xs: _*) 370 | * }}} 371 | * 372 | * =Type Aliases= 373 | * 374 | * In the same way as you can give meaningful names to expressions, you can give meaningful names 375 | * to ''type expressions'': 376 | */ 377 | def typeAlias(res0: Either[String, (Int, Int)]): Unit = { 378 | type Result = Either[String, (Int, Int)] 379 | def divide(dividend: Int, divisor: Int): Result = 380 | if (divisor == 0) Left("Division by zero") 381 | else Right((dividend / divisor, dividend % divisor)) 382 | divide(6, 4) shouldBe Right((1, 2)) 383 | divide(2, 0) shouldBe Left("Division by zero") 384 | divide(8, 4) shouldBe res0 385 | } 386 | 387 | } 388 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/TailRecursion.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import scala.annotation.tailrec 20 | 21 | /** 22 | * @param name 23 | * tail_recursion 24 | */ 25 | object TailRecursion extends ScalaTutorialSection { 26 | 27 | /** 28 | * =Recursive Function Application= 29 | * 30 | * Let’s compare the evaluation steps of the application of two recursive methods. 31 | * 32 | * First, consider `gcd`, a method that computes the greatest common divisor of two numbers. 33 | * 34 | * Here's an implementation of `gcd` using Euclid's algorithm. 35 | * 36 | * {{{ 37 | * def gcd(a: Int, b: Int): Int = 38 | * if (b == 0) a else gcd(b, a % b) 39 | * }}} 40 | * 41 | * `gcd(14, 21)` is evaluated as follows: 42 | * 43 | * {{{ 44 | * gcd(14, 21) 45 | * if (21 == 0) 14 else gcd(21, 14 % 21) 46 | * if (false) 14 else gcd(21, 14 % 21) 47 | * gcd(21, 14 % 21) 48 | * gcd(21, 14) 49 | * if (14 == 0) 21 else gcd(14, 21 % 14) 50 | * if (false) 21 else gcd(14, 21 % 14) 51 | * gcd(14, 21 % 14) 52 | * gcd(14, 7) 53 | * if (7 == 0) 14 else gcd(7, 14 % 7) 54 | * if (false) 14 else gcd (7, 14 % 7) 55 | * gcd(7, 14 % 7) 56 | * gcd(7, 0) 57 | * if (0 == 0) 7 else gcd(0, 7 % 0) 58 | * if (true) 7 else gcd(0, 7 % 0) 59 | * 7 60 | * }}} 61 | * 62 | * Now, consider `factorial`: 63 | * 64 | * {{{ 65 | * def factorial(n: Int): Int = 66 | * if (n == 0) 1 else n * factorial(n - 1) 67 | * }}} 68 | * 69 | * `factorial(4)` is evaluated as follows: 70 | * 71 | * {{{ 72 | * factorial(4) 73 | * if (4 == 0) 1 else 4 * factorial(4 - 1) 74 | * 4 * factorial(3) 75 | * 4 * (3 * factorial(2)) 76 | * 4 * (3 * (2 * factorial(1))) 77 | * 4 * (3 * (2 * (1 * factorial(0))) 78 | * 4 * (3 * (2 * (1 * 1))) 79 | * 24 80 | * }}} 81 | * 82 | * What are the differences between the two sequences? 83 | * 84 | * One important difference is that in the case of `gcd`, we see that the reduction sequence 85 | * essentially oscillates. It goes from one call to `gcd` to the next one, and eventually it 86 | * terminates. In between we have expressions that are different from a simple call like if then 87 | * else's but we always come back to this shape of the call of `gcd`. If we look at `factorial`, 88 | * on the other hand we see that in each couple of steps we add one more element to our 89 | * expressions. Our expressions becomes bigger and bigger until we end when we finally reduce it 90 | * to the final value. 91 | * 92 | * =Tail Recursion= 93 | * 94 | * That difference in the rewriting rules actually translates directly to a difference in the 95 | * actual execution on a computer. In fact, it turns out that if you have a recursive function 96 | * that calls itself as its last action, then you can reuse the stack frame of that function. This 97 | * is called ''tail recursion''. 98 | * 99 | * And by applying that trick, a tail recursive function can execute in constant stack space, so 100 | * it's really just another formulation of an iterative process. We could say a tail recursive 101 | * function is the functional form of a loop, and it executes just as efficiently as a loop. 102 | * 103 | * If we look back at `gcd`, we see that in the else part, `gcd` calls itself as its last action. 104 | * And that translates to a rewriting sequence that was essentially constant in size, and that 105 | * will, in the actual execution on a computer, translate into a tail recursive call that can 106 | * execute in constant space. 107 | * 108 | * On the other hand, if you look at `factorial` again, then you'll see that after the call to 109 | * `factorial(n - 1)`, there is still work to be done, namely, we had to multiply the result of 110 | * that call with the number `n`. So, that recursive call is not a tail recursive call, and it 111 | * becomes evident in the reduction sequence, where you see that actually there’s a buildup of 112 | * intermediate results that we all have to keep until we can compute the final value. So, 113 | * `factorial` would not be a tail recursive function. 114 | * 115 | * Both `factorial` and `gcd` only call itself but in general, of course, a function could call 116 | * other functions. So the generalization of tail recursion is that, if the last action of a 117 | * function consists of calling another function, maybe the same, maybe some other function, the 118 | * stack frame could be reused for both functions. Such calls are called ''tail calls''. 119 | * 120 | * =Tail Recursion in Scala= 121 | * 122 | * In Scala, only directly recursive calls to the current function are optimized. 123 | * 124 | * One can require that a function is tail-recursive using a `@tailrec` annotation: 125 | * 126 | * {{{ 127 | * @tailrec 128 | * def gcd(a: Int, b: Int): Int = … 129 | * }}} 130 | * 131 | * If the annotation is given, and the implementation of `gcd` were not tail recursive, an error 132 | * would be issued. 133 | * 134 | * =Exercise= 135 | * 136 | * Complete the following definition of a tail-recursive version of `factorial`: 137 | */ 138 | def tailRecFactorial(res0: Int, res1: Int): Unit = { 139 | def factorial(n: Int): Int = { 140 | @tailrec 141 | def iter(x: Int, result: Int): Int = 142 | if (x == res0) result 143 | else iter(x - 1, result * x) 144 | 145 | iter(n, res1) 146 | } 147 | 148 | factorial(0) shouldBe 1 149 | factorial(3) shouldBe 6 150 | factorial(4) shouldBe 24 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/TermsAndTypes.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | /** 20 | * @param name 21 | * terms_and_types 22 | */ 23 | object TermsAndTypes extends ScalaTutorialSection { 24 | 25 | /** 26 | * =Scala Tutorial= 27 | * 28 | * The following set of sections provides a quick tutorial on the Scala language. 29 | * 30 | * The contents is based on the MOOCS 31 | * [[https://www.coursera.org/learn/progfun1/home Functional Programming Principles in Scala]] and 32 | * [[https://www.coursera.org/learn/progfun2/home Functional Program Design in Scala]]. 33 | * 34 | * The target audience is people who already have ''some'' experience of programming and who are 35 | * familiar with the JVM. 36 | * 37 | * =Elements of Programming= 38 | * 39 | * Programming languages give programmers ways to express computations. 40 | * 41 | * Every non-trivial programming language provides: 42 | * 43 | * - primitive expressions representing the simplest elements ; 44 | * - ways to ''combine'' expressions ; 45 | * - ways to ''abstract'' expressions, which introduce a name for an expression by which it can 46 | * then be referred to. 47 | * 48 | * =Primitive Expressions= 49 | * 50 | * Here are some examples of ''primitive expressions'': 51 | * 52 | * - The number “1”: 53 | * 54 | * {{{ 55 | * 1 56 | * }}} 57 | * 58 | * - The boolean value “true”: 59 | * 60 | * {{{ 61 | * true 62 | * }}} 63 | * 64 | * - The text “Hello, Scala!”: 65 | * 66 | * {{{ 67 | * "Hello, Scala!" 68 | * }}} 69 | * 70 | * (Note the usage of double quotes, `"`). 71 | * 72 | * =Compound Expressions= 73 | * 74 | * More complex expressions can be expressed by ''combining'' simpler expressions using 75 | * ''operators''. They can therefore express more complex computations: 76 | * 77 | * - How many is one plus two? 78 | * 79 | * {{{ 80 | * 1 + 2 81 | * }}} 82 | * 83 | * - What is the result of the concatenation of the texts “Hello, ” and “Scala!”? 84 | * 85 | * {{{ 86 | * "Hello, " ++ "Scala!" 87 | * }}} 88 | * 89 | * =Evaluation= 90 | * 91 | * A non-primitive expression is evaluated as follows. 92 | * 93 | * 1. Take the leftmost operator 94 | * 1. Evaluate its operands (left before right) 95 | * 1. Apply the operator to the operands 96 | * 97 | * The evaluation process stops once it results in a value. 98 | * 99 | * ==Example== 100 | * 101 | * Here is the evaluation of an arithmetic expression: 102 | * 103 | * {{{ 104 | * (1 + 2) * 3 105 | * 3 * 3 106 | * 9 107 | * }}} 108 | */ 109 | def evaluation(res0: Int, res1: String): Unit = { 110 | 1 + 2 shouldBe res0 111 | "Hello, " ++ "Scala!" shouldBe res1 112 | } 113 | 114 | /** 115 | * =Method Calls= 116 | * 117 | * Another way to make complex expressions out of simpler expressions is to call ''methods'' on 118 | * expressions: 119 | * 120 | * - What is the size of the text “Hello, Scala!”? 121 | * 122 | * {{{ 123 | * "Hello, Scala!".size 124 | * }}} 125 | * 126 | * Methods are ''applied'' on expressions using the ''dot notation''. 127 | * 128 | * The object on which the method is applied is named the ''target object''. 129 | * 130 | * - What is the range of numbers between 1 and 10? 131 | * 132 | * {{{ 133 | * 1.to(10) 134 | * }}} 135 | * 136 | * Methods can have ''parameters''. They are supplied between parentheses. 137 | * 138 | * In the below examples, the `abs` method returns the absolute value of a number, and the 139 | * `toUpperCase` method returns the target `String` in upper case. 140 | */ 141 | def methods(res0: String, res1: Int): Unit = { 142 | "Hello, Scala!".toUpperCase shouldBe res0 143 | -42.abs shouldBe res1 144 | } 145 | 146 | /** 147 | * =Operators Are Methods= 148 | * 149 | * Actually, operators are just methods with symbolic names: 150 | * 151 | * {{{ 152 | * 3 + 2 == 3.+(2) 153 | * }}} 154 | * 155 | * The ''infix syntax'' allows you to omit the dot and the parentheses. 156 | * 157 | * The infix syntax can also be used with regular methods: 158 | * 159 | * {{{ 160 | * 1.to(10) == (1 to 10) 161 | * }}} 162 | * 163 | * Any method with a parameter can be used like an infix operator. 164 | * 165 | * =Values and Types= 166 | * 167 | * Expressions have a ''value'' and a ''type''. The evaluation model defines how to get a value 168 | * out of an expression. Types classify values. 169 | * 170 | * Both `0` and `1` are numbers, their type is `Int`. 171 | * 172 | * `"foo"` and `"bar"` are text, their type is `String`. 173 | * 174 | * =Static Typing= 175 | * 176 | * The Scala compiler statically checks that you don’t combine incompatible expressions. 177 | * 178 | * Fill the following blank with values whose type is different from `Int` and see the result: 179 | */ 180 | def staticTyping(res0: Int): Unit = 181 | 1 to res0 182 | 183 | /** 184 | * =Common Types= 185 | * 186 | * - `Int`: 32-bit integers (e.g. `1`, `23`, `456`) 187 | * - `Double`: 64-bit floating point numbers (e.g. `1.0`, `2.3`, `4.56`) 188 | * - `Boolean`: boolean values (`true` and `false`) 189 | * - `String`: text (e.g. `"foo"`, `"bar"`) 190 | * 191 | * Note that type names always begin with an upper case letter. 192 | * 193 | * =Exercise= 194 | * 195 | * Here are some more methods of standard types. Can you guess what they do? If you get stuck, try 196 | * evaluating each statement in turn in a scala REPL to see what the result is. 197 | */ 198 | def moreMethods(res0: String, res1: Boolean, res2: Boolean, res3: String, res4: String): Unit = { 199 | 16.toHexString shouldBe res0 200 | (0 to 10).contains(10) shouldBe res1 201 | (0 until 10).contains(10) shouldBe res2 202 | "foo".drop(1) shouldBe res3 203 | "bar".take(2) shouldBe res4 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/sections/TypeClasses.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import scalatutorial.utils.Rational 20 | import scalatutorial.utils.Sorting.insertionSort 21 | 22 | /** 23 | * @param name 24 | * type_classes 25 | */ 26 | object TypeClasses extends ScalaTutorialSection { 27 | 28 | /** 29 | * Remember the sorting function: 30 | * 31 | * {{{ 32 | * def insertionSort(xs: List[Int]): List[Int] = { 33 | * def insert(y: Int, ys: List[Int]): List[Int] = 34 | * ys match { 35 | * case List() => y :: List() 36 | * case z :: zs => 37 | * if (y < z) y :: z :: zs 38 | * else z :: insert(y, zs) 39 | * } 40 | * 41 | * xs match { 42 | * case List() => List() 43 | * case y :: ys => insert(y, insertionSort(ys)) 44 | * } 45 | * } 46 | * 47 | * }}} 48 | * 49 | * How to parameterize `insertionSort` so that it can also be used for lists with elements other 50 | * than `Int` (like, for instance, `Rational`)? 51 | * 52 | * {{{ 53 | * def insertionSort[T](xs: List[T]): List[T] = ... 54 | * }}} 55 | * 56 | * The above attempt does not work, because the comparison `<` in `insert` is not defined for 57 | * arbitrary types `T`. 58 | * 59 | * Idea: parameterize `insert` with the necessary comparison function. 60 | * 61 | * =Parameterization of Sort= 62 | * 63 | * The most flexible design is to make the function `insertionSort` polymorphic and to pass the 64 | * comparison operation as an additional parameter: 65 | * 66 | * {{{ 67 | * def insertionSort[T](xs: List[T])(lessThan: (T, T) => Boolean): List[T] = { 68 | * def insert(y: T, ys: List[T]): List[T] = 69 | * ys match { 70 | * … 71 | * case z :: zs => 72 | * if (lessThan(y, z)) y :: z :: zs 73 | * else … 74 | * } 75 | * 76 | * xs match { 77 | * … 78 | * case y :: ys => insert(y, insertionSort(ys)(lessThan)) 79 | * } 80 | * } 81 | * }}} 82 | * 83 | * =Calling Parameterized Sort= 84 | * 85 | * We can now call `insertionSort` as follows: 86 | * 87 | * {{{ 88 | * val nums = List(-5, 6, 3, 2, 7) 89 | * val fruit = List("apple", "pear", "orange", "pineapple") 90 | * 91 | * insertionSort(nums)((x: Int, y: Int) => x < y) 92 | * insertionSort(fruit)((x: String, y: String) => x.compareTo(y) < 0) 93 | * }}} 94 | * 95 | * Or, since parameter types can be inferred from the call `insertionSort(xs)`: 96 | * 97 | * {{{ 98 | * insertionSort(nums)((x, y) => x < y) 99 | * }}} 100 | * 101 | * =Parametrization with Ordered= 102 | * 103 | * There is already a class in the standard library that represents orderings. 104 | * 105 | * {{{ 106 | * scala.math.Ordering[T] 107 | * }}} 108 | * 109 | * provides ways to compare elements of type `T`. So instead of parameterizing with the `lessThan` 110 | * operation directly, we could parameterize with `Ordering` instead: 111 | * 112 | * {{{ 113 | * def insertionSort[T](xs: List[T])(ord: Ordering[T]): List[T] = { 114 | * def insert(y: T, ys: List[T]): List[T] = 115 | * … if (ord.lt(y, z)) … 116 | * 117 | * … insert(y, insertionSort(ys)(ord)) … 118 | * } 119 | * }}} 120 | * 121 | * =Ordered Instances:= 122 | * 123 | * Calling the new `insertionSort` can be done like this: 124 | * 125 | * {{{ 126 | * insertionSort(nums)(Ordering.Int) 127 | * insertionSort(fruits)(Ordering.String) 128 | * }}} 129 | * 130 | * This makes use of the values `Int` and `String` defined in the `scala.math.Ordering` object, 131 | * which produce the right orderings on integers and strings. 132 | * 133 | * =Implicit Parameters= 134 | * 135 | * Problem: Passing around `lessThan` or `ord` values is cumbersome. 136 | * 137 | * We can avoid this by making `ord` an implicit parameter: 138 | * 139 | * {{{ 140 | * def insertionSort[T](xs: List[T])(implicit ord: Ordering[T]): List[T] = { 141 | * def insert(y: T, ys: List[T]): List[T] = 142 | * … if (ord.lt(y, z)) … 143 | * 144 | * … insert(y, insertionSort(ys)) … 145 | * } 146 | * }}} 147 | * 148 | * Then calls to `insertionSort` can avoid the ordering parameters: 149 | * 150 | * {{{ 151 | * insertionSort(nums) 152 | * insertionSort(fruits) 153 | * }}} 154 | * 155 | * The compiler will figure out the right implicit to pass based on the demanded type. 156 | * 157 | * =Rules for Implicit Parameters= 158 | * 159 | * Say, a function takes an implicit parameter of type `T`. 160 | * 161 | * The compiler will search an implicit definition that 162 | * 163 | * - is marked `implicit` 164 | * - has a type compatible with `T` 165 | * - is visible at the point of the function call, or is defined in a companion object 166 | * associated with `T`. 167 | * 168 | * If there is a single (most specific) definition, it will be taken as actual argument for the 169 | * implicit parameter. Otherwise it's an error. 170 | * 171 | * =Type Classes= 172 | * 173 | * The combination of types parameterized and implicit parameters is also called ''type classes''. 174 | * 175 | * =Exercises= 176 | * 177 | * Define an ordering for the `Rational` type: 178 | * 179 | * {{{ 180 | * class Rational(x: Int, y: Int) { 181 | * 182 | * private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) 183 | * private val g = gcd(x, y) 184 | * 185 | * lazy val numer: Int = x / g 186 | * lazy val denom: Int = y / g 187 | * } 188 | * }}} 189 | */ 190 | def rationalOrdering(res0: (Rational, Rational) => Int): Unit = { 191 | 192 | /** 193 | * Returns an integer whose sign communicates how the first parameter compares to the second 194 | * parameter. 195 | * 196 | * The result sign has the following meaning: 197 | * - Negative if the first parameter is less than the second parameter 198 | * - Positive if the first parameter is greater than the second parameter 199 | * - Zero otherwise 200 | */ 201 | val compareRationals: (Rational, Rational) => Int = res0 202 | 203 | implicit val rationalOrder: Ordering[Rational] = 204 | new Ordering[Rational] { 205 | def compare(x: Rational, y: Rational): Int = compareRationals(x, y) 206 | } 207 | 208 | val half = new Rational(1, 2) 209 | val third = new Rational(1, 3) 210 | val fourth = new Rational(1, 4) 211 | val rationals = List(third, half, fourth) 212 | insertionSort(rationals) shouldBe List(fourth, third, half) 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/BankAccount.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | class BankAccount { 20 | 21 | private var balance = 0 22 | 23 | def deposit(amount: Int): Unit = 24 | if (amount > 0) balance = balance + amount 25 | 26 | def withdraw(amount: Int): Int = 27 | if (0 < amount && amount <= balance) { 28 | balance = balance - amount 29 | balance 30 | } else throw new Error("insufficient funds") 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/IntSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | abstract class IntSet { 20 | def incl(x: Int): IntSet 21 | def contains(x: Int): Boolean 22 | } 23 | 24 | object Empty extends IntSet { 25 | def contains(x: Int): Boolean = false 26 | def incl(x: Int): IntSet = new NonEmpty(x, Empty, Empty) 27 | } 28 | class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet { 29 | 30 | def contains(x: Int): Boolean = 31 | if (x < elem) left contains x 32 | else if (x > elem) right contains x 33 | else true 34 | 35 | def incl(x: Int): IntSet = 36 | if (x < elem) new NonEmpty(elem, left incl x, right) 37 | else if (x > elem) new NonEmpty(elem, left, right incl x) 38 | else this 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/Note.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | case class Note(name: String, duration: String, octave: Int) 20 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/Rational.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | class Rational(x: Int, y: Int) { 20 | 21 | private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) 22 | private val g = gcd(x, y) 23 | 24 | lazy val numer: Int = x / g 25 | lazy val denom: Int = y / g 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/animals.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | trait Animal { 20 | def fitness: Int 21 | } 22 | 23 | trait Reptile extends Animal 24 | 25 | trait Mammal extends Animal 26 | 27 | trait Zebra extends Mammal { 28 | def zebraCount: Int 29 | } 30 | 31 | trait Giraffe extends Mammal 32 | -------------------------------------------------------------------------------- /src/main/scala/scalatutorial/utils/sorting.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.utils 18 | 19 | object Sorting { 20 | 21 | def insertionSort[A](xs: List[A])(implicit ord: Ordering[A]): List[A] = { 22 | def insert(y: A, ys: List[A]): List[A] = 23 | ys match { 24 | case List() => y :: List() 25 | case z :: zs => 26 | if (ord.lt(y, z)) y :: z :: zs 27 | else z :: insert(y, zs) 28 | } 29 | 30 | xs match { 31 | case List() => List() 32 | case y :: ys => insert(y, insertionSort(ys)) 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/ClassesVsCaseClassesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class ClassesVsCaseClassesSpec extends RefSpec with Checkers { 26 | 27 | def `check creation and manipulation`(): Unit = 28 | check(Test.testSuccess(ClassesVsCaseClasses.creationAndManipulation _, "C" :: HNil)) 29 | 30 | def `check equality`(): Unit = 31 | check(Test.testSuccess(ClassesVsCaseClasses.equality _, false :: true :: HNil)) 32 | 33 | def `check encoding`(): Unit = 34 | check( 35 | Test.testSuccess( 36 | ClassesVsCaseClasses.encoding _, 37 | "Note(C,Quarter,3)" :: false :: "Note(C,Quarter,4)" :: HNil 38 | ) 39 | ) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/DefinitionsAndEvaluationSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class DefinitionsAndEvaluationSpec extends RefSpec with Checkers { 26 | 27 | def `check using square`(): Unit = 28 | check(Test.testSuccess(DefinitionsAndEvaluation.usingSquare _, 9.0 :: HNil)) 29 | 30 | def `check area exercise`(): Unit = 31 | check(Test.testSuccess(DefinitionsAndEvaluation.areaExercise _, 314.159 :: HNil)) 32 | 33 | def `check triangle area`(): Unit = 34 | check(Test.testSuccess(DefinitionsAndEvaluation.triangleAreaExercise _, 2.0 :: 15.0 :: HNil)) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/FunctionalLoopsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.{Arbitrary, Gen} 20 | //import org.scalacheck.ScalacheckShapeless._ 21 | import org.scalaexercises.Test 22 | import org.scalatest.refspec.RefSpec 23 | import org.scalatestplus.scalacheck.Checkers 24 | import shapeless._ 25 | 26 | class FunctionalLoopsSpec extends RefSpec with Checkers { 27 | 28 | implicit val arb: Arbitrary[Int :: Int :: HNil] = Arbitrary { 29 | for { 30 | num1 <- Gen.choose(-10, 0) 31 | num2 <- Gen.choose(0, 10) 32 | } yield num1 :: num2 :: HNil 33 | } 34 | 35 | def `factorial exercise with recursion`(): Unit = 36 | check(Test.testSuccess(FunctionalLoops.factorialExercise _, 0 :: 1 :: HNil)) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/HigherOrderFunctionsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | //import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalacheck.{Arbitrary, Gen} 21 | import org.scalaexercises.Test 22 | import org.scalatest.refspec.RefSpec 23 | import org.scalatestplus.scalacheck.Checkers 24 | import shapeless.{::, HNil} 25 | 26 | class HigherOrderFunctionsSpec extends RefSpec with Checkers { 27 | 28 | implicit val arb: Arbitrary[Int :: Int :: HNil] = Arbitrary { 29 | for { 30 | num1 <- Gen.posNum[Int] 31 | num2 <- Gen.posNum[Int] 32 | } yield num1 :: num2 :: HNil 33 | } 34 | 35 | def `check tail recursive sum function`(): Unit = 36 | check(Test.testSuccess(HigherOrderFunctions.tailRecSum _, 0 :: 10 :: HNil)) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/ImperativeProgrammingSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class ImperativeProgrammingSpec extends RefSpec with Checkers { 26 | 27 | def `check observational equivalence`(): Unit = 28 | check(Test.testSuccess(ImperativeProgramming.observationalEquivalence _, 10 :: HNil)) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/LazyEvaluationSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class LazyEvaluationSpec extends RefSpec with Checkers { 26 | 27 | def `check lazy list range`(): Unit = 28 | check(Test.testSuccess(LazyEvaluation.llRangeExercise _, 3 :: HNil)) 29 | 30 | def `check lazy val`(): Unit = 31 | check(Test.testSuccess(LazyEvaluation.lazyVal _, "xzyz" :: HNil)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/LexicalScopesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class LexicalScopesSpec extends RefSpec with Checkers { 26 | 27 | def `check scope rules`(): Unit = 28 | check(Test.testSuccess(LexicalScopes.scopeRules _, 16 :: HNil)) 29 | 30 | def `check objects scopes`(): Unit = 31 | check(Test.testSuccess(LexicalScopes.objectScopes _, 3 :: HNil)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/ObjectOrientedProgrammingSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class ObjectOrientedProgrammingSpec extends RefSpec with Checkers { 26 | 27 | def `check dynamic binding`(): Unit = 28 | check(Test.testSuccess(ObjectOrientedProgramming.dynamicBinding _, false :: true :: HNil)) 29 | 30 | def `check reducer`(): Unit = 31 | check(Test.testSuccess(ObjectOrientedProgramming.reducer _, 24 :: 10 :: HNil)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/PolymorphicTypesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class PolymorphicTypesSpec extends RefSpec with Checkers { 26 | 27 | def `check size`(): Unit = 28 | check(Test.testSuccess(PolymorphicTypes.sizeExercise _, 0 :: 1 :: HNil)) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/StandardLibrarySpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class StandardLibrarySpec extends RefSpec with Checkers { 26 | 27 | def `check insertion sort`(): Unit = 28 | check( 29 | Test.testSuccess( 30 | StandardLibrary.insertionSort _, 31 | ((_: Int) < (_: Int)) :: List.empty[Int] :: HNil 32 | ) 33 | ) 34 | 35 | def `check either`(): Unit = 36 | check( 37 | Test.testSuccess( 38 | StandardLibrary.either _, 39 | (Right[String, Int](3): Either[String, Int]) :: (Left[String, Int]("not a number"): Either[ 40 | String, 41 | Int 42 | ]) :: HNil 43 | ) 44 | ) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/StructuringInformationSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class StructuringInformationSpec extends RefSpec with Checkers { 26 | 27 | def `check case class projection`(): Unit = 28 | check(Test.testSuccess(StructuringInformation.caseClassProjection _, "Quarter" :: 3 :: HNil)) 29 | 30 | def `check case class equals`(): Unit = 31 | check(Test.testSuccess(StructuringInformation.caseClassEquals _, true :: false :: HNil)) 32 | 33 | def `check adts`(): Unit = 34 | check(Test.testSuccess(StructuringInformation.adts _, 0.5 :: 0.25 :: HNil)) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/SyntacticConveniencesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class SyntacticConveniencesSpec extends RefSpec with Checkers { 26 | 27 | def `check string interpolation`(): Unit = 28 | check( 29 | Test.testSuccess( 30 | SyntacticConveniences.stringInterpolation _, 31 | "Hello, Functional Programming!" :: HNil 32 | ) 33 | ) 34 | 35 | def `check string interpolation2`(): Unit = 36 | check(Test.testSuccess(SyntacticConveniences.stringInterpolation2 _, "Hello, SCALA!" :: HNil)) 37 | 38 | def `check tuples`(): Unit = 39 | check(Test.testSuccess(SyntacticConveniences.tupleExtraction _, "foo" :: HNil)) 40 | 41 | def `check tuples2`(): Unit = 42 | check(Test.testSuccess(SyntacticConveniences.tupleExtraction2 _, "foo" :: HNil)) 43 | 44 | def `check tuples manipulation`(): Unit = 45 | check(Test.testSuccess(SyntacticConveniences.tupleManipulation _, "foo" :: HNil)) 46 | 47 | def `check default parameters`(): Unit = 48 | check(Test.testSuccess(SyntacticConveniences.defaultParameters _, 1 :: HNil)) 49 | 50 | def `check repeated parameters`(): Unit = 51 | check(Test.testSuccess(SyntacticConveniences.repeatedParameters _, 2.0 :: HNil)) 52 | 53 | def `check type alias`(): Unit = 54 | check( 55 | Test.testSuccess( 56 | SyntacticConveniences.typeAlias _, 57 | (Right((2, 0)): Either[String, (Int, Int)]) :: HNil 58 | ) 59 | ) 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/TailRecursionSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class TailRecursionSpec extends RefSpec with Checkers { 26 | 27 | def `factorial exercise tail recursive`(): Unit = 28 | check(Test.testSuccess(TailRecursion.tailRecFactorial _, 0 :: 1 :: HNil)) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/TermsAndTypesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalacheck.ScalacheckShapeless._ 20 | import org.scalaexercises.Test 21 | import org.scalatest.refspec.RefSpec 22 | import org.scalatestplus.scalacheck.Checkers 23 | import shapeless.HNil 24 | 25 | class TermsAndTypesSpec extends RefSpec with Checkers { 26 | 27 | def `check evaluation`(): Unit = 28 | check(Test.testSuccess(TermsAndTypes.evaluation _, 3 :: "Hello, Scala!" :: HNil)) 29 | 30 | def `check methods`(): Unit = 31 | check(Test.testSuccess(TermsAndTypes.methods _, "HELLO, SCALA!" :: 42 :: HNil)) 32 | 33 | def `check more methods`(): Unit = 34 | check( 35 | Test.testSuccess(TermsAndTypes.moreMethods _, "10" :: true :: false :: "oo" :: "ba" :: HNil) 36 | ) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/scala/scalatutorial/sections/TypeClassesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2020 47 Degrees Open Source 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scalatutorial.sections 18 | 19 | import org.scalatest.refspec.RefSpec 20 | import org.scalatestplus.scalacheck.Checkers 21 | 22 | import scalatutorial.utils.Rational 23 | 24 | class TypeClassesSpec extends RefSpec with Checkers { 25 | 26 | def `check rational ordering`(): Unit = { 27 | val ordering = 28 | (x: Rational, y: Rational) => x.numer * y.denom - y.numer * x.denom 29 | 30 | TypeClasses.rationalOrdering(ordering) 31 | } 32 | 33 | } 34 | --------------------------------------------------------------------------------