├── .cspell.json ├── .cspell └── practicalli-custom-dictionary.txt ├── .gitattributes ├── .github ├── CODEOWNERS ├── config │ ├── gitleaks.toml │ ├── lychee.toml │ ├── markdown-link-check.json │ ├── markdown-lint.jsonc │ ├── megalinter.yaml │ └── secretlintrc.json ├── pull_request_template.md └── workflows │ ├── changelog-check.yaml │ ├── megalinter.yaml │ ├── publish-book.yaml │ ├── scheduled-stale-check.yaml │ └── scheduled-version-check.yaml ├── .gitignore ├── CHANGELOG.md ├── GLOSSARY.md ├── LICENSE ├── Makefile ├── README.md ├── docs ├── .nojekyll ├── alternative-tools │ ├── clojure-cli │ │ ├── basic-repl.md │ │ ├── evaluate-an-expression.md │ │ └── set-namespace-on-repl-startup.md │ └── index.md ├── assets │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.svg │ ├── images │ │ ├── practicalli-logo.png │ │ └── social │ │ │ └── README.md │ ├── practicalli-logo.svg │ └── stylesheets │ │ ├── extra.css │ │ └── practicalli-light.css ├── automation │ ├── index.md │ └── make.md ├── clojure-cli │ ├── built-in-commands.md │ ├── clojure-style.md │ ├── defining-aliases.md │ ├── design-journal.md │ ├── execution-options.md │ ├── index.md │ ├── practicalli-config.md │ ├── projects │ │ ├── add-libraries.md │ │ ├── hotload-in-project.md │ │ ├── index.md │ │ ├── migrate-project.md │ │ ├── namespace.md │ │ ├── package │ │ │ ├── index.md │ │ │ └── tools-build.md │ │ ├── rich-comments.md │ │ └── templates │ │ │ ├── design-templates.md │ │ │ ├── index.md │ │ │ └── practicalli │ │ │ ├── application.md │ │ │ ├── index.md │ │ │ ├── landing-page.md │ │ │ ├── minimal.md │ │ │ └── service.md │ ├── repl-reloaded.md │ ├── repl-startup.md │ └── repl │ │ ├── coding.md │ │ ├── help.md │ │ ├── index.md │ │ ├── libraries.md │ │ ├── repl-uncovered.md │ │ └── troubleshooting.md ├── clojure-editors │ ├── clojure-lsp │ │ ├── index.md │ │ ├── practicalli-snippets.md │ │ └── snippets.md │ └── index.md ├── clojure-spec │ ├── add-spec-to-projects.md │ ├── data │ │ ├── and-or-specifications.md │ │ ├── composite-specifications.md │ │ ├── conform.md │ │ ├── defining-specifications.md │ │ ├── entity-maps.md │ │ ├── explain.md │ │ ├── hierarchical-specifications.md │ │ ├── index.md │ │ ├── literal-values.md │ │ ├── map-literals.md │ │ ├── predicate-functions.md │ │ ├── registry.md │ │ └── valid-q.md │ ├── functions │ │ ├── documentation.md │ │ ├── function-definition-specifications.md │ │ ├── higher-order-functions.md │ │ └── index.md │ ├── generative-testing │ │ ├── example-projects │ │ │ ├── index.md │ │ │ └── next-jdbc.md │ │ ├── index.md │ │ └── predicate-generators.md │ ├── index.md │ ├── organising-specs.md │ ├── projects │ │ ├── bank-account │ │ │ ├── account-holder-specification.md │ │ │ ├── customer-details-specification.md │ │ │ ├── function-specifications.md │ │ │ ├── generate-test-data.md │ │ │ ├── index.md │ │ │ ├── rebel-readline.md │ │ │ ├── test-functions-against-spec.md │ │ │ ├── unit-tests-with-spec.md │ │ │ ├── validate-customer-details-specification.md │ │ │ └── write-failing-tests.md │ │ ├── card-game │ │ │ └── index.md │ │ └── index.md │ ├── testing │ │ ├── checking arguments.md │ │ └── index.md │ └── using-spec-in-the-repl.md ├── coding-challenges │ ├── 4clojure │ │ └── index.md │ ├── advent-of-code.md │ ├── codewars │ │ └── index.md │ ├── exercism │ │ ├── bob │ │ │ ├── bob-regular-expression-approach.md │ │ │ ├── bob-string-approach.md │ │ │ └── index.md │ │ ├── hamming.md │ │ ├── index.md │ │ ├── nucleotide-count.md │ │ ├── rna-transcription.md │ │ └── space-age.md │ ├── index.md │ ├── koans.md │ └── palindrome │ │ ├── index.md │ │ └── simple-palindrome-test.md ├── continuous-integration │ ├── circle-ci │ │ ├── circle-ci-sample-project.md │ │ ├── detailed-config.md │ │ ├── index.md │ │ ├── random-clojure-function.md │ │ └── status-monitor.md │ ├── github-workflow │ │ └── index.md │ └── index.md ├── control-flow │ ├── cond.md │ ├── if.md │ └── when.md ├── core-async.md ├── core.async │ ├── bike-assembly-line │ │ └── index.md │ ├── clacks-messages │ │ └── index.md │ ├── index.md │ └── toy-car-assembly-line │ │ └── index.md ├── data-inspector │ ├── clojure-inspector.md │ ├── index.md │ └── portal.md ├── data-structures │ ├── alternatives.md │ ├── hash-maps │ │ ├── access.md │ │ ├── accessing-nested-hash-maps.md │ │ ├── create.md │ │ ├── index.md │ │ └── update.md │ ├── index.md │ ├── list.md │ ├── naming.md │ ├── pretty-printing.md │ ├── set.md │ ├── shared-memory.md │ └── vector.md ├── defining-behaviour-with-functions │ ├── anonymous-functions.md │ ├── calling-functions.md │ ├── examples.md │ ├── index.md │ ├── parameters.md │ └── syntax.md ├── designing-data-structures │ ├── index.md │ ├── modeling-alphabet-codes.md │ ├── modeling-name-generation-map.md │ ├── with-maps-of-maps.md │ ├── with-maps.md │ ├── with-vectors-of-maps.md │ ├── with-vectors-of-vectors.md │ └── with-vectors.md ├── development-environments │ ├── index.md │ ├── java.md │ ├── leiningen.md │ ├── lighttable.md │ └── other-tools.md ├── explaining-macros.md ├── games │ ├── index.md │ └── tictactoe-cli │ │ ├── create-project.md │ │ └── index.md ├── index.md ├── install │ ├── clojure-cli.md │ ├── index.md │ └── java.md ├── introduction │ ├── clojure-in-15-minutes.md │ ├── concepts │ │ ├── all-bytecode-in-the-end.md │ │ ├── clojure-made-simple.md │ │ ├── design.md │ │ ├── features.md │ │ ├── functional-reactive-programming.md │ │ ├── index.md │ │ ├── misc.md │ │ ├── naming-local.md │ │ ├── naming-things.md │ │ ├── purpose.md │ │ ├── rationale.md │ │ └── what-is-functional-programming.md │ ├── first-taste-of-clojure.md │ ├── five-steps-to-clojure.md │ ├── learning-clojure.md │ ├── repl-workflow.md │ ├── study-guide.md │ └── who-uses-clojure.md ├── io.md ├── iterate-over-data │ ├── apply.md │ ├── comp.md │ ├── filter-remove.md │ ├── index.md │ ├── map-fn.md │ ├── map-partial.md │ ├── map.md │ ├── reduce.md │ └── transduce.md ├── lazy-evaluation.md ├── libraries │ ├── clojars.md │ ├── clojure-core-lisp-comprehension.md │ ├── clojure-core.md │ ├── edn.md │ ├── index.md │ └── om.md ├── modifying-data-structures │ ├── index.md │ ├── lists.md │ ├── maps.md │ ├── sets.md │ └── vectors.md ├── performance │ ├── index.md │ ├── load-testing.md │ └── testing-functions.md ├── puzzles │ ├── index.md │ └── random-seat-assignment.md ├── quickstart │ └── quick-reference.md ├── reference │ ├── basic-syntax.md │ ├── books.md │ ├── changing-state.md │ ├── clojure-cli │ │ ├── example-alias-definitions.md │ │ ├── index.md │ │ └── jvm-options.md │ ├── clojure-svg │ │ └── index.md │ ├── clojure-syntax │ │ ├── assigning-names.md │ │ ├── code-documentation.md │ │ ├── comments.md │ │ ├── control-flow.md │ │ ├── defining-functions.md │ │ ├── global-definitions.md │ │ ├── java-interop.md │ │ ├── local-bindings.md │ │ ├── more-java-fun.md │ │ ├── naming.md │ │ ├── numbers-maths.md │ │ ├── parenthesis.md │ │ ├── private-functions.md │ │ ├── quick-look-at-types.md │ │ ├── ratios.md │ │ ├── strings.md │ │ ├── syntax.md │ │ ├── syntax │ │ │ └── index.md │ │ ├── threading-syntactic-sugar.md │ │ └── whats-my-environment.md │ ├── code-analysis.md │ ├── control-flow.md │ ├── core-async.md │ ├── creative-coding │ │ └── index.md │ ├── doc-and-source-functions.md │ ├── index.md │ ├── jvm │ │ ├── common-options.md │ │ ├── experimental-options.md │ │ ├── index.md │ │ ├── java-17-flags.md │ │ ├── profile-tools.md │ │ └── understanding-memory-usage.md │ ├── kebab-case.md │ ├── naming-conventions.md │ ├── naming.md │ ├── performance │ │ └── index.md │ ├── prasmatic-schema.md │ ├── reader-macros.md │ ├── recursion.md │ ├── sequences.md │ ├── standard-library │ │ ├── collections.md │ │ ├── cond-thread-macro.md │ │ ├── destructuring.md │ │ ├── index.md │ │ ├── predicate-functions.md │ │ ├── regular-expressions │ │ │ ├── common-regex-patterns.md │ │ │ ├── index.md │ │ │ ├── matching-sub-sequences.md │ │ │ ├── matching-sub-strings.md │ │ │ ├── matching-with-groups.md │ │ │ ├── string-replace-with-regex.md │ │ │ ├── string-split-with-regex.md │ │ │ └── sub-expression-matches.md │ │ └── sequences.md │ ├── tagged-literals │ │ ├── index.md │ │ └── uuid.md │ └── threading-macros.md ├── simple-projects │ ├── data-transformation │ │ ├── common-english-words.csv │ │ ├── index.md │ │ └── most-common-word.md │ ├── encode-decode │ │ ├── caesar-cipher-rot13.md │ │ ├── clacks.md │ │ ├── convert-boolean-values.md │ │ ├── index.md │ │ └── rna-dna.md │ ├── generate-web-page.md │ ├── index.md │ ├── mutating-state │ │ ├── index.md │ │ └── mutants-assemble.md │ ├── random-clojure-function.md │ ├── split-the-bill.md │ ├── tdd-kata │ │ ├── index.md │ │ ├── recent-song-list.md │ │ └── salary-slip-generator.md │ └── tripple-lock.md ├── testing-in-clojure.md ├── testing │ ├── clojure-test │ │ └── index.md │ ├── index.md │ ├── integration-testing │ │ └── index.md │ ├── test-runners │ │ ├── aero.md │ │ ├── congnitect-labs-test-runner.md │ │ ├── example-projects.md │ │ ├── index.md │ │ └── kaocha-test-runner.md │ └── unit-testing │ │ ├── clojure-test-expectations.md │ │ ├── configure-projects-for-tests.md │ │ ├── fixtures.md │ │ ├── index.md │ │ ├── test-selectors.md │ │ └── writing-unit-tests.md ├── thinking-functionally │ ├── arity.md │ ├── example-hitchhikers-guide.md │ ├── first-class-functions.md │ ├── function-composition.md │ ├── functors.md │ ├── higher-order-functions.md │ ├── homoiconicity.md │ ├── immutability.md │ ├── immutable-collections.md │ ├── immutable-local-bindings.md │ ├── immutable-values.md │ ├── impure-functions.md │ ├── index.md │ ├── iterate-over-values.md │ ├── lazy-evaluation.md │ ├── list-comprehension.md │ ├── managing-state-changes.md │ ├── map-with-partial.md │ ├── partial-functions.md │ ├── pattern-matching.md │ ├── persistent-data-structures.md │ ├── polymorphism.md │ ├── pure-functions.md │ ├── recursion-polymorphism.md │ ├── recursion.md │ ├── sequence-abstractions.md │ ├── sequences.md │ ├── side-effects.md │ ├── tail-recursion.md │ ├── threading-macros.md │ └── transducers.md └── using-data-structures │ ├── applying-functions.md │ ├── destructuring.md │ ├── index.md │ ├── lazy-sequences.md │ ├── mapping-data-structures.md │ └── sequences.md ├── mkdocs.yml ├── notes-for-clojure-practicalli.org ├── overrides ├── 404.html ├── main.html └── partials │ ├── header.html │ ├── palette.html │ └── source.html └── use-cases.org /.cspell.json: -------------------------------------------------------------------------------- 1 | {"dictionaryDefinitions": [ 2 | { 3 | "name": "practicalli", 4 | "path": "./.cspell/practicalli-custom-dictionary.txt", 5 | "description": "Practicalli Custom Dictionary", 6 | "addWords": true} 7 | ], 8 | "dictionaries": ["practicalli"], 9 | "ignorePaths": ["**/*.yml", "**/*.yaml"] 10 | } 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Configure Languages for GitHub repository using Linguist 2 | 3 | # Markdown & Make detection, 4 | # exclude HTML, CSS & JavaScript 5 | docs/** linguist-detectable 6 | *.md linguist-detectable=true 7 | make linguist-detectable=true 8 | *.css linguist-detectable=false 9 | *.js linguist-detectable=false 10 | *.html linguist-detectable=false 11 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Codeowners 2 | 3 | # Default owner accounts for the current repository 4 | # Automatically added as a reviewr to all pull requests (not including drafts) 5 | 6 | * @practicalli-johnny 7 | -------------------------------------------------------------------------------- /.github/config/gitleaks.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [allowlist] 4 | description = "global allow lists" 5 | paths = [ 6 | '''gitleaks.toml''', 7 | '''(.*?)(jpg|gif|doc|docx|zip|xls|pdf|bin|svg|socket)$''', 8 | '''(go.mod|go.sum)$''', 9 | '''gradle.lockfile''', 10 | '''node_modules''', 11 | '''package-lock.json''', 12 | '''pnpm-lock.yaml''', 13 | '''Database.refactorlog''', 14 | '''vendor''', 15 | ] 16 | 17 | [[rules]] 18 | description = "AWS Example API Key" 19 | id = "aws-example-api-key" 20 | regex = '''AKIAIOSFODNN7EXAMPLE''' 21 | keywords = [ 22 | "awstoken", 23 | ] 24 | -------------------------------------------------------------------------------- /.github/config/lychee.toml: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # Base URL or website root directory to check relative URLs. 4 | base = "https://practical.li/clojure/" 5 | 6 | # Only test links with the given schemes (e.g. https). 7 | # Omit to check links with any other scheme. 8 | # At the moment, we support http, https, file, and mailto. 9 | scheme = ["https"] 10 | 11 | # ---------------------------------------- 12 | # Exclusions 13 | 14 | # Exclude URLs and mail addresses from checking (supports regex). 15 | exclude = ['^https://127.0.0.0', '^https://www\.linkedin\.com', '^https://web\.archive\.org/web/'] 16 | 17 | # Exclude these filesystem paths from getting checked. 18 | exclude_path = ["mkdocs.yml", "overrides", "includes", ".github", ".git"] 19 | 20 | # Exclude all private IPs from checking. 21 | # Equivalent to setting `exclude_private`, `exclude_link_local`, and 22 | # `exclude_loopback` to true. 23 | exclude_all_private = true 24 | 25 | # Check mail addresses 26 | include_mail = false 27 | # ---------------------------------------- 28 | -------------------------------------------------------------------------------- /.github/config/markdown-link-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^http://localhost" 5 | }, 6 | { 7 | "pattern": "^mailto:*" 8 | }, 9 | { 10 | "pattern": "^#*" 11 | }, 12 | { 13 | "pattern": "^https://127.0.0.0/" 14 | } 15 | ], 16 | "timeout": "20s", 17 | "retryOn429": true, 18 | "retryCount": 5, 19 | "fallbackRetryDelay": "30s", 20 | "aliveStatusCodes": [ 21 | 200, 22 | 206 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/config/secretlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "id": "@secretlint/secretlint-rule-basicauth", 5 | "options": { 6 | "allows": [ 7 | "hostname.domain.com", 8 | "jdbc:postgresql://:port/?user=&password=", 9 | "postgres://postgres://username:password@hostname.domain.com:1234/database-name" 10 | ] 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | :memo: Description 2 | 3 | 4 | :white_check_mark: Checklist 5 | 6 | - [ ] Commits should be cryptographically signed (SSH or GPG) 7 | 8 | 9 | ## Practicalli Guidelines 10 | 11 | Please follow these guidelines when submitting a pull request 12 | 13 | - refer to all relevant issues, using `#` followed by the issue number (or paste full link to the issue) 14 | - PR should contain the smallest possible change 15 | - PR should contain a very specific change 16 | - PR should contain only a single commit (squash your commits locally if required) 17 | - Avoid multiple changes across multiple files (raise an issue so we can discuss) 18 | - Avoid a long list of spelling or grammar corrections. These take too long to review and cherry pick. 19 | 20 | ## Submitting articles 21 | 22 | [Create an issue using the article template](https://github.com/practicalli/blog-content/issues/new?assignees=&labels=article&template=article.md&title=Suggested+article+title), 23 | providing as much detail as possible. 24 | 25 | ## Website design 26 | 27 | Suggestions about website design changes are most welcome, especially in terms of usability and accessibility. 28 | 29 | Please raise an issue so we can discuss changes first, especially changes related to aesthetics. 30 | 31 | ## Review process 32 | 33 | All pull requests are reviewed by @practicalli-johnny and feedback provided, usually the same day but please be patient. 34 | -------------------------------------------------------------------------------- /.github/workflows/changelog-check.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Check CHANGELOG.md file updated for every pull request 3 | 4 | name: Changelog Check 5 | on: 6 | pull_request: 7 | paths-ignore: 8 | - "README.md" 9 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 10 | 11 | jobs: 12 | changelog: 13 | name: Changelog Update Check 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}" 17 | - run: echo "🐧 Job running on ${{ runner.os }} server" 18 | - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository" 19 | 20 | # Git Checkout 21 | - name: Checkout Code 22 | uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 0 25 | sparse-checkout: | 26 | docs 27 | overrides 28 | .github 29 | CHANGELOG.md 30 | - run: echo "🐙 Sparse Checkout of ${{ github.repository }} repository to the CI runner." 31 | 32 | # Changelog Enforcer 33 | - name: Changelog Enforcer 34 | uses: dangoslen/changelog-enforcer@v3 35 | with: 36 | changeLogPath: "CHANGELOG.md" 37 | skipLabels: "skip-changelog-check" 38 | 39 | # Summary and status 40 | - run: echo "🎨 Changelog Enforcer quality checks completed" 41 | - run: echo "🍏 Job status is ${{ job.status }}." 42 | -------------------------------------------------------------------------------- /.github/workflows/megalinter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # MegaLinter GitHub Action configuration file 3 | # More info at https://megalinter.io 4 | # All variables described in https://megalinter.io/latest/configuration/ 5 | 6 | name: MegaLinter 7 | on: 8 | workflow_dispatch: 9 | pull_request: 10 | branches: [main] 11 | push: 12 | branches: [main] 13 | 14 | # Run Linters in parallel 15 | # Cancel running job if new job is triggered 16 | concurrency: 17 | group: "${{ github.ref }}-${{ github.workflow }}" 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | megalinter: 22 | name: MegaLinter 23 | runs-on: ubuntu-latest 24 | steps: 25 | - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}" 26 | - run: echo "🐧 Job running on ${{ runner.os }} server" 27 | - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository" 28 | 29 | # Git Checkout 30 | - name: Checkout Code 31 | uses: actions/checkout@v4 32 | with: 33 | fetch-depth: 0 34 | sparse-checkout: | 35 | docs 36 | overrides 37 | .github 38 | - run: echo "🐙 Sparse Checkout of ${{ github.repository }} repository to the CI runner." 39 | 40 | # MegaLinter Configuration 41 | - name: MegaLinter Run 42 | id: ml 43 | ## latest release of major version 44 | uses: oxsecurity/megalinter/flavors/documentation@v8 45 | env: 46 | # ADD CUSTOM ENV VARIABLES OR DEFINE IN MEGALINTER_CONFIG file 47 | MEGALINTER_CONFIG: .github/config/megalinter.yaml 48 | 49 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" # report individual linter status 50 | # Validate all source when push on main, else just the git diff with live. 51 | VALIDATE_ALL_CODEBASE: >- 52 | ${{ github.event_name == 'push' && github.ref == 'refs/heads/main'}} 53 | 54 | # Upload MegaLinter artifacts 55 | - name: Archive production artifacts 56 | if: ${{ success() }} || ${{ failure() }} 57 | uses: actions/upload-artifact@v4 58 | with: 59 | name: MegaLinter reports 60 | path: | 61 | megalinter-reports 62 | mega-linter.log 63 | 64 | # Summary and status 65 | - run: echo "🎨 MegaLinter quality checks completed" 66 | - run: echo "🍏 Job status is ${{ job.status }}." 67 | -------------------------------------------------------------------------------- /.github/workflows/publish-book.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Publish Book 3 | on: 4 | # Manually trigger workflow 5 | workflow_dispatch: 6 | 7 | # Run work flow conditional on linter workflow success 8 | workflow_run: 9 | workflows: 10 | - "MegaLinter" 11 | paths: 12 | - "docs/**" 13 | - "includes/**" 14 | - "overrides/**" 15 | - "mkdocs.yaml" 16 | branches: 17 | - main 18 | types: 19 | - completed 20 | 21 | permissions: 22 | contents: write 23 | 24 | jobs: 25 | publish-book: 26 | name: MkDocs Publish 27 | runs-on: ubuntu-latest 28 | steps: 29 | - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}" 30 | - run: echo "🐧 Job running on ${{ runner.os }} server" 31 | - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository" 32 | 33 | - name: Checkout Code 34 | uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | sparse-checkout: | 38 | docs 39 | includes 40 | overrides 41 | - run: echo "🐙 ${{ github.repository }} repository sparse-checkout to the CI runner." 42 | 43 | - name: Setup Python 44 | uses: actions/setup-python@v5 45 | with: 46 | python-version: 3.x 47 | 48 | - name: Cache 49 | uses: actions/cache@v4 50 | with: 51 | key: ${{ github.ref }} 52 | path: .cache 53 | 54 | - run: pip install mkdocs-material mkdocs-callouts mkdocs-glightbox mkdocs-git-revision-date-localized-plugin mkdocs-redirects pillow cairosvg 55 | - run: mkdocs gh-deploy --force 56 | - run: echo "🐙 ." 57 | 58 | # Summary and status 59 | - run: echo "🎨 MkDocs Publish Book workflow completed" 60 | - run: echo "🍏 Job status is ${{ job.status }}." 61 | -------------------------------------------------------------------------------- /.github/workflows/scheduled-stale-check.yaml: -------------------------------------------------------------------------------- 1 | # ---------------------------------------- 2 | # Scheduled stale issue & pull request check 3 | # 4 | # Adds 'stale' label after a set piece of time, 5 | # then closes stale issues & pull requests a short period after 6 | # 7 | # Using "Close Stale Issues" action 8 | # https://github.com/marketplace/actions/close-stale-issues 9 | # ---------------------------------------- 10 | 11 | name: 'Scheduled stale check' 12 | on: 13 | workflow_dispatch: 14 | schedule: 15 | - cron: "0 1 1 * *" # at 01:00 on first day of month 16 | 17 | jobs: 18 | stale: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}" 22 | - run: echo "🐧 Job running on ${{ runner.os }} server" 23 | - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository" 24 | 25 | - uses: actions/stale@v9 26 | with: 27 | stale-issue-message: 'After 30 days with no activity, the issue was automatically marked stale. Remove stale label or add a comment to prevent the issue being closed in 5 days.' 28 | stale-pr-message: 'After 45 days with no activity, the Pull Request was automatically marked stale. Remove stale label or comment to prevent the PR being closed in 10 days.' 29 | close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' 30 | close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.' 31 | days-before-issue-stale: 30 32 | days-before-pr-stale: 45 33 | days-before-issue-close: 5 34 | days-before-pr-close: 10 35 | start-date: '2025-04-05T00:00:00Z' # only affect issues/PRs from date created (ISO 8601 or RFC 2822 format) 36 | any-of-labels: 'future,keep' # labels to keep 37 | exempt-issue-assignees: 'practicalli-johnny' 38 | exempt-pr-assignees: 'practicalli-johnny' 39 | 40 | # Summary 41 | - run: echo "🎨 Issues & Pull Request checked with actions/stale" 42 | - run: echo "🍏 Job status is ${{ job.status }}." 43 | -------------------------------------------------------------------------------- /.github/workflows/scheduled-version-check.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # ------------------------------------------ 3 | # Scheduled check of versions 4 | # - use as non-urgent report on versions 5 | # - Uses POSIX Cron syntax 6 | # - Minute [0,59] 7 | # - Hour [0,23] 8 | # - Day of the month [1,31] 9 | # - Month of the year [1,12] 10 | # - Day of the week ([0,6] with 0=Sunday) 11 | # 12 | # Using liquidz/anta to check: 13 | # - GitHub workflows 14 | # - deps.edn 15 | # ------------------------------------------ 16 | 17 | name: "Scheduled Version Check" 18 | on: 19 | schedule: 20 | # - cron: "0 4 * * *" # at 04:04:04 ever day 21 | # - cron: "0 4 * * 5" # at 04:04:04 ever Friday 22 | - cron: "0 4 1 * *" # at 04:04:04 on first day of month 23 | workflow_dispatch: # Run manually via GitHub Actions Workflow page 24 | 25 | jobs: 26 | scheduled-version-check: 27 | name: "Scheduled Version Check" 28 | runs-on: ubuntu-latest 29 | steps: 30 | - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}" 31 | - run: echo "🐧 Job running on ${{ runner.os }} server" 32 | - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository" 33 | 34 | - name: Checkout Code 35 | uses: actions/checkout@v4 36 | with: 37 | fetch-depth: 0 38 | sparse-checkout: | 39 | .github 40 | - run: echo "🐙 ${{ github.repository }} repository sparse-checkout to the CI runner." 41 | - name: "Antq Check versions" 42 | uses: liquidz/antq-action@main 43 | with: 44 | excludes: "" 45 | skips: "boot clojure-cli pom shadow-cljs leiningen" 46 | 47 | # Summary 48 | - run: echo "🎨 library versions checked with liquidz/antq" 49 | - run: echo "🍏 Job status is ${{ job.status }}." 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude all files from root directory 2 | /* 3 | 4 | # ------------------------ 5 | # Common project files 6 | !CHANGELOG.md 7 | !README.md 8 | !LICENSE 9 | 10 | # ------------------------ 11 | # Include MkDocs files 12 | !docs/ 13 | !includes/ 14 | !overrides/ 15 | !mkdocs.yml 16 | 17 | # ------------------------ 18 | # Project automation 19 | !Makefile 20 | 21 | # ------------------------ 22 | # Version Control 23 | !.gitignore 24 | !.gitattributes 25 | !.github/ 26 | 27 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/.nojekyll -------------------------------------------------------------------------------- /docs/alternative-tools/clojure-cli/basic-repl.md: -------------------------------------------------------------------------------- 1 | # Basic Terminal REPL UI 2 | 3 | The `clojure` command will start a REPL by default or if given the `--repl` or `-r` argument. The basic repl does not provide history of commands. 4 | 5 | `clj` is a script that wraps the `clojure` command and requires `rlwrap`, an external readline command, to navigate REPL history via the ++arrow-up++ and ++arrow-down++ keys. 6 | 7 | Use `clj` when you want to run a repl (or preferably use [rebel readline](rebel-readline/) instead) and `clojure` for everything else. 8 | 9 | !!! HINT "Rebel Rich Terminal UI" 10 | [rebel readline](/clojure/clojure-cli/repl/) is a terminal REPL UI that provides interactive help, function autocomplete, signature prompts and many other features to provide a very rich REPL experience. 11 | 12 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) includes the `-M:repl/rebel` alias to run rebel readline REPL. 13 | 14 | 15 | `clj` command in a terminal window starts a Clojure REPL and shows the version of Clojure used. The command does not need to be in a directory containing a Clojure project. 16 | 17 | ```shell 18 | clj 19 | ``` 20 | 21 | Type in a Clojure expression at the `=> user` REPL prompt and press ++enter++ to see the result 22 | 23 | ++ctrl+"d"++ to exit the REPL 24 | 25 | ![Clojure CLI Tools REPL](https://clojure.org/images/content/guides/repl/show-terminal-repl.gif) 26 | -------------------------------------------------------------------------------- /docs/alternative-tools/clojure-cli/evaluate-an-expression.md: -------------------------------------------------------------------------------- 1 | # Evaluating an expression with Clojure CLI tools 2 | 3 | An expression is a piece of Clojure code that can be evaluated and return a result 4 | 5 | This expression calls the `+` function with the arguments `1 2 3 4 5`. As this code works, we get a result. 6 | 7 | ```clojure 8 | (+ 1 2 3 4 5) 9 | ``` 10 | 11 | 12 | Using the `-e` option an expression can be passed to the Clojure CLI tools and a value returned 13 | 14 | 15 | ```shell 16 | clojure -e (+ 1 2 3 4 5) 17 | ``` 18 | 19 | 20 | ## Expressions returning `nil` 21 | 22 | If the expressing used returns a value of `nil`, a legal value in Clojure, then no result is printed out. 23 | 24 | 25 | ## When to use this? 26 | 27 | `clojure -e` is a quick way to see what an expression does without having to set anything up (although starting a REPL is very little extra effort). 28 | 29 | Using the `-e` option is useful for running simple scripts written in Clojure, especially on servers and remote environments. 30 | 31 | The lack of return value for nil is very useful when using Clojure CLI tools to evaluate Clojure code within another script. 32 | -------------------------------------------------------------------------------- /docs/alternative-tools/index.md: -------------------------------------------------------------------------------- 1 | # Alternative Tools 2 | -------------------------------------------------------------------------------- /docs/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/favicon-16x16.png -------------------------------------------------------------------------------- /docs/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/images/practicalli-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure/2f735c6fc3a37d8e1ada105ac779e7d5671bf262/docs/assets/images/practicalli-logo.png -------------------------------------------------------------------------------- /docs/assets/images/social/README.md: -------------------------------------------------------------------------------- 1 | # Social Cards 2 | 3 | Social Cards are visual previews of the website that are included when sending links via social media platforms. 4 | 5 | Material for MkDocs is [configured to generate beautiful social cards automatically](https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/), using the colors, fonts and logos defined in `mkdocs.yml` 6 | 7 | Generated images are stored in this directory. 8 | -------------------------------------------------------------------------------- /docs/automation/index.md: -------------------------------------------------------------------------------- 1 | # Automation 2 | 3 | Automation tools can provide a consistent command line interface across a wide range of projects. 4 | 5 | Whilst the Clojure CLI is a very extensible tool that flexibility can also add some complexity to its command line interface. 6 | 7 | Automation tools abstract the command line to provide a consistent and simple user experience whilst keeping underlying flexibility. 8 | 9 | !!! HINT "Practicalli recommends make" 10 | A Makefile is not reliant on programming language knowledge so has no barrier to those who are unfamiliar with the Clojure language. 11 | 12 | Make is useful when working with mixed language teams to create a unified tool and command line across a wide range of projects. 13 | 14 | 15 | ## Automation tooling 16 | 17 | * [Make](make.md) - ubiquitous task automation tool, programming language agnostic 18 | * Shell Scripts 19 | * Babashka - create task automation tool with Clojure 20 | 21 | 22 | ## Make 23 | 24 | Make is very simple to use and has a long history as a build tool and wider task automation tool. 25 | 26 | Task are defined in a `Makefile` and task can depend on each other. Any commands or combination of commands that run on the command line can be used as make tasks. 27 | 28 | make provides tab completion of tasks defined in the Makefile without additional configuration. 29 | 30 | `make` is available for all operating systems. 31 | 32 | !!! HINT "Practicalli Project Templates include Makefile" 33 | Creating new projects with `:project/create` and [Practicalli Project Templates](/clojure/clojure-cli/projects/templates/) provide a Makefile with a wide range of common tasks for Clojure development. 34 | 35 | 36 | ### Shell Scripts 37 | 38 | Shell scripts provide a very common way to create a relatively ubiquitous approach to running tools, even across multiple Shell implementations (Bash, Zsh, fish, etc.) and operating systems. 39 | 40 | Shell scripting language is very powerful especially for manipulation of the operating system, although scripts require development and maintenance. 41 | 42 | 43 | ## Babashka 44 | 45 | Write automation scripts with Clojure code using the [Babashka task runner](https://book.babashka.org/#tasks){target=_blank} 46 | 47 | Babashka can use a wide range of Clojure functions and libraries, although as a general script tool then additional coding and maintenance may be reqiured compared to a dedicated tool. 48 | 49 | [Babashka task runner](https://book.babashka.org/#tasks){target=_blank .md-button} 50 | -------------------------------------------------------------------------------- /docs/clojure-cli/design-journal.md: -------------------------------------------------------------------------------- 1 | # Design Journal 2 | 3 | A design journal captures with code and comments the decisions taken for a project, invaluable to anyone trying to get up to speed with a project. 4 | 5 | 6 | ## Using a design-journal namespace 7 | 8 | A single namespace encourages the journal to flow as the design of the application flows. So onboarding onto a project is essentially reading and evaluating code from top to bottom. 9 | 10 | 11 | ## Using comment blocks 12 | 13 | It is useful to include examples of how you expect key parts of the system to be called and the kind of arguments they receive. Placing these examples in a (comment ,,,) expression ensures they are not accidentally evaluated whilst showing the most important function calls in a particular namespace. 14 | -------------------------------------------------------------------------------- /docs/clojure-cli/projects/package/index.md: -------------------------------------------------------------------------------- 1 | # Package with Clojure tools.build 2 | 3 | The [:globe_with_meridians: Clojure.org tools.build project](https://clojure.org/guides/tools_build) is used to build jar files to deploy libraries and uberjar files to run deployed projects (e.g. in Docker containers or directly on an Operating System with Java JVM installed). 4 | 5 | [Clojure tools.build](tools-build.md) is a library to define build related tasks using Clojure code. 6 | 7 | 8 | !!! HINT "Practicalli Project Templates includes tools.build configuration" 9 | Clojure projects created with [Practicalli Project Templates](https://practical.li/clojure/clojure-cli/projects/templates/practicalli/) include a `build.clj` configuration to build an uberjar of the project. 10 | 11 | The `make build-jar` runs the `clojure -T:build jar` command to build an uberjar. 12 | 13 | 14 | ??? HINT "Java ARchive - jar file" 15 | A `.jar` file is a zip archive of the project containing all the files for running a Clojure project. The archive should contain metatdata files such as Manifest and pom.xml and can contain Clojure sources or compiled class files from the project (or both). 16 | 17 | An ubjerjar is `.jar` file that also contains all the project dependencies including Clojure. The uberjar is a self-contained file that can be easily deployed and requires only a Java run-time (Java Virtual Machine), using the `java -jar project-uberjar.jar` command, with the option to pass arguments to the Uberjar also. 18 | 19 | 20 | === "Practicalli Project Build tasks" 21 | [:fontawesome-solid-book-open: Practicalli Project templates](/clojure/clojure-cli/projects/templates/) include a `build.clj` configuration with `jar` and `uberjar` tasks. 22 | 23 | Create a runnable Clojure archive 24 | 25 | !!! NOTE "" 26 | ```shell 27 | clojure -T:project/build uberjar 28 | ``` 29 | 30 | Create a Clojure library archive 31 | 32 | !!! NOTE "" 33 | ```shell 34 | clojure -T:project/build jar 35 | ``` 36 | 37 | === "Clojure tools.build" 38 | 39 | [tools.build](tools-build.md) provides an API for pragmatically defining tasks to build Clojure projects. 40 | 41 | Create a `build.clj` configuration with tasks for building a library jar or runable uberjar. 42 | 43 | [:fontawesome-solid-book-open: Define build.clj configuration for tools.build](tools-build.md){target=_blank .md-button} 44 | -------------------------------------------------------------------------------- /docs/clojure-cli/projects/templates/practicalli/application.md: -------------------------------------------------------------------------------- 1 | # Practicalli Application template 2 | 3 | Create a general Clojure application with REPL Reloaded workflow. 4 | 5 | 6 | ## Using the project 7 | 8 | Run the REPL 9 | 10 | ```shell 11 | make repl 12 | ``` 13 | 14 | The REPL prompt is displayed using Rebel for a rich UI experience. 15 | 16 | Portal data inspector window is displayed and all evaluation results and mulog events are automatically sent to Portal. 17 | 18 | An nREPL server is running in the background for connecting Clojure aware editors. 19 | 20 | 21 | Run tests (stopping on first failing test) 22 | 23 | ```shell 24 | make test 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/clojure-cli/projects/templates/practicalli/landing-page.md: -------------------------------------------------------------------------------- 1 | # Practicalli Landing Page 2 | 3 | Build simple websites and landing pages using ClojureScript and figwheel-main. 4 | 5 | ```shell 6 | clojure -T:project/create :template landing-page :name practicalli/website-name 7 | ``` 8 | 9 | 10 | ## Using the project 11 | 12 | Run the REPL 13 | 14 | ```shell 15 | make repl 16 | ``` 17 | 18 | Run tests (stopping on first failing test) 19 | 20 | ```shell 21 | make test 22 | ``` 23 | 24 | 25 | 26 | ## Template design 27 | 28 | Configuration files 29 | 30 | - `deps.edn` project dependencies and aliases defining figwheel builds 31 | - `dev.cljs.edn` development build configuration 32 | - `live.cljs.edn` live build configuration (GitHub pages deployment by default) 33 | - `figwheel-main.edn` general figwheel configuration 34 | 35 | Clojure code 36 | 37 | - `src/project/landing-page.clj` compose components to render the website 38 | - `src/project/components.clj` functions that define component and associated helper functions 39 | - `src/project/data.clj` data structure passed in part or whole to each component, via the `landing-page`. 40 | 41 | > `project.data` namespace defines an example data structure as a static value (def). Use an atom to contain the data structure if the data should be updated by components in the project. 42 | -------------------------------------------------------------------------------- /docs/clojure-cli/projects/templates/practicalli/minimal.md: -------------------------------------------------------------------------------- 1 | # Practicalli Minimal Project Template 2 | 3 | A Clojure project with minimal dependencies and example code, useful for experimenting or building a new project from a minimal setup. 4 | 5 | 6 | ## Using the project 7 | 8 | Run the REPL 9 | 10 | ```shell 11 | make repl 12 | ``` 13 | 14 | The REPL prompt is displayed using Rebel for a rich UI experience. 15 | 16 | Portal data inspector window is displayed and all evaluation results and mulog events are automatically sent to Portal. 17 | 18 | An nREPL server is running in the background for connecting Clojure aware editors. 19 | 20 | 21 | Run tests (stopping on first failing test) 22 | 23 | ```shell 24 | make test 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/and-or-specifications.md: -------------------------------------------------------------------------------- 1 | # Combining specifications with and and or 2 | 3 | `clojure.core/and` function and `clojure.core/or` function can be used to define a specification with multiple parts. 4 | 5 | ## Conform to One or more specifications 6 | 7 | A specification for residential address included either a house number or name. The `clojure.core/or` function allows either type of value to be used and conform to the specification. 8 | 9 | ```clojure 10 | (spec/def ::house-name-number (or string? int?)) 11 | ``` 12 | 13 | Using `spec/or` then unique keys are required for each possible type of value. Keys are used to explain where a failure occurred if values do not conform to the specification. 14 | 15 | ```clojure 16 | (spec/def ::house-name-number (spec/or :string string? 17 | :number int?)) 18 | ``` 19 | 20 | If specifications are uses as the options in the `clojure.spec.alpha/or` then those specification names are used as the keys to explain where failure to conform to the specification happened. 21 | 22 | ```clojure 23 | 24 | (spec/def ::social-security-id (spec/or ::social-security-id-uk 25 | ::social-security-id-usa)) 26 | ``` 27 | 28 | 29 | ## Conform to all specifications 30 | 31 | Create a composite specification using `clojure.spec.alpha/and` when all specifications should be conformed by the values 32 | 33 | 34 | For an arranged banking overdraft limit, the value should be a positive number, that is an integer type and is less than 1000. 35 | 36 | ```clojure 37 | (spec/def ::arranged-overdraft-limit (spec/and pos? int? #(> 1000 %))) 38 | ``` 39 | 40 | If a value does not conform to any of the three specifications then the value fails the `::arranged-overdraft-limit` specification. 41 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/composite-specifications.md: -------------------------------------------------------------------------------- 1 | # Composing Specifications 2 | 3 | No spec is an island 4 | 5 | Composing individual specifications is an effective way to build larger abstractions in specifications without creating fixed hierarchical structures that are harder to refactor. 6 | 7 | Require specification namespace to the page 8 | 9 | ```clojure 10 | (ns practicalli.clojure 11 | (:require [clojure.spec.alpha :as spec])) 12 | ``` 13 | 14 | 15 | `spec/and` is used when all specifications should be true. 16 | 17 | ```clojure 18 | (spec/def ::meaning-of-life 19 | (spec/and int? 20 | even? 21 | #(= 42 %))) 22 | ``` 23 | 24 | `spec/or` is use when one or more specifications should be true 25 | 26 | ```clojure 27 | (spec/def ::meaning-of-life-int-or-string 28 | (spec/or :integer #(= 42 %) 29 | :string #(= "forty two" %))) 30 | ``` 31 | 32 | Each condition in the spec is annotated with a label for each conditional branches. 33 | 34 | Labels are included in the return result from `spec/explain` when values do not conform to a specification, providing context as to why a value failed the specification. 35 | 36 | When an or is conformed, it returns a vector with the condition name and conformed value. 37 | 38 | 39 | ```clojure 40 | (spec/conform ::meaning-of-life-int-or-string 42) 41 | ``` 42 | 43 | ```clojure 44 | (spec/conform ::meaning-of-life-int-or-string "forty two") 45 | ``` 46 | 47 | 48 | ```clojure 49 | (spec/conform ::meaning-of-life-int-or-string :entropy) 50 | ``` 51 | 52 | 53 | ```clojure 54 | (spec/explain ::meaning-of-life-int-or-string :entropy) 55 | ``` 56 | 57 | 58 | ## Individual specifications 59 | 60 | Before composing a more abstract specification, first define individual specifications 61 | 62 | ```clojure 63 | (spec/def ::first-name string?) 64 | ``` 65 | 66 | ```clojure 67 | (spec/def ::last-name string?) 68 | ``` 69 | 70 | ```clojure 71 | (spec/def ::residential-address string?) 72 | ``` 73 | 74 | 75 | ## Composing hash-map specification 76 | 77 | The individual specifications can now be composed into a single specification. 78 | 79 | `keys` function combines specifications to form a composite specification in the form of a Clojure hash-map. 80 | 81 | ```clojure 82 | (spec/def ::customer-details 83 | (spec/keys 84 | :req [::first-name ::last-name ::residential-address])) 85 | ``` 86 | 87 | Use the composite specification with a value 88 | 89 | ```clojure 90 | (spec/conform ::customer-details 91 | {::first-name "Jenny" 92 | ::last-name "Jetpack" 93 | ::residential-address "42 meaning of life street, Earth"}) 94 | ``` 95 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/conform.md: -------------------------------------------------------------------------------- 1 | ## Does a value conform to a specification? 2 | 3 | `clojure.spec.alpha/conform` takes two arguments 4 | 5 | - a specification 6 | - a value to test against the specification 7 | 8 | `:clojure.spec.alpha/invalid` is returned when a value does not conform to a specification. 9 | 10 | If the value does conform to the specification, then the value is returned. This value is referred to as a conformed value. 11 | 12 | ## Require the Clojure spec library 13 | 14 | Set the namespace for the page and require clojure.spec.alpha library, setting the alias to `spec` 15 | 16 | ```clojure 17 | (ns practicalli.clojure.specifications 18 | (:require [clojure.spec.alpha :as spec])) 19 | ``` 20 | 21 | ## Using conform 22 | 23 | If the value conforms to the spec, a conformed value is returned 24 | 25 | ```clojure 26 | (spec/conform odd? 101) 27 | ``` 28 | 29 | 30 | 31 | When a value does not conform to a spec, the value `:clojure.spec.alpha/invalid` is returned 32 | 33 | ```clojure 34 | (spec/conform even? 101) 35 | ``` 36 | 37 | 38 | 39 | ```clojure 40 | (spec/conform integer? 1) 41 | ``` 42 | 43 | 44 | 45 | ```clojure 46 | (spec/conform seq? [1 2 3]) 47 | ``` 48 | 49 | 50 | 51 | ```clojure 52 | (spec/conform seq? (range 10)) 53 | ``` 54 | 55 | 56 | 57 | ```clojure 58 | (spec/conform map? {}) 59 | ``` 60 | 61 | 62 | 63 | ```clojure 64 | (spec/conform map? (hash-map :a 1 :b 2)) 65 | ``` 66 | 67 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/defining-specifications.md: -------------------------------------------------------------------------------- 1 | # Defining specifications 2 | 3 | `clojure.spec.alpha/def` binds a name to a specification, just like `clojure.core/def` binds a name to a value. 4 | 5 | Binding a name means specifications are available throughout the code and in other projects if the project is included as a library. 6 | 7 | 8 | ## Naming - fully qualified keywords 9 | 10 | Specification names should use fully qualified keywords, typically using the namespace in which the specification is defined in. 11 | 12 | Define a namespace for the page and require Clojure Spec 13 | 14 | ```clojure 15 | (ns practicalli.clojure.specifications 16 | (:require [clojure.spec.alpha :as spec])) 17 | ``` 18 | 19 | 20 | ```clojure 21 | (spec/def :practicalli.clojure.specifications/number number?) 22 | ``` 23 | 24 | 25 | ## auto-resolve macro 26 | 27 | `::` double colon is the auto-resolve macro, which will pre-pend the current namespace to the specification keyword. The `::` notation removes the need to edit fully qualified names should a specification be moved to a different namespace. 28 | 29 | ```clojure 30 | (spec/def ::number number?) 31 | ``` 32 | 33 | !!! HINT "Fully Qualified keywords" 34 | Using fully qualified keywords ensures they are unique and therefore can be used across all projects. 35 | 36 | Namespaces are usually unique as they include the name of the company or organization behind the code and any project or component names used to organize the code. 37 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/explain.md: -------------------------------------------------------------------------------- 1 | # Explaining non-conforming values 2 | 3 | `clojure.spec.alpha/explain` describes why a value does not satisfy a specification. 4 | 5 | `clojure.spec.alpha/explain` takes two arguments 6 | 7 | - a specification 8 | - a value to test against the specification 9 | 10 | `Success` string is sent to standard out if the value meets the specification 11 | 12 | A string explaining where the value deviates from the specification is sent to standard out if the value does not meet the specification. 13 | 14 | There are several variations on the explain function for different situations 15 | 16 | - `explain` - sends the return value to the standard out / REPL 17 | - `explain-str` - returns a human readable result. 18 | - `explain-data` - returns a data structure of the error to be processed by other code 19 | 20 | ## Example of a failing value 21 | 22 | First define a namespace and require the Clojure Spec namespace 23 | 24 | ```clojure 25 | (ns practicalli.clojure.specifications 26 | (:require [clojure.spec.alpha :as spec])) 27 | 28 | (spec/def ::meaning-of-life #(= 42 %)) 29 | ``` 30 | 31 | Given the following specification 32 | 33 | ```clojure 34 | (spec/explain ::meaning-of-life 24) 35 | ``` 36 | 37 | Using the value `24` with that specification will fail. Using explain we can see why 38 | 39 | ```clojure 40 | (spec/def ::meaning-of-life-int-or-string 41 | (spec/or :integer #(= 42 %) 42 | :string #(= "forty two" %))) 43 | ``` 44 | 45 | In this case explain returned the 46 | 47 | - value being checked against the spec 48 | - result of that check (failed) 49 | - predicate used to check the value 50 | - spec name used to check the value 51 | 52 | Notice that the value failed on the first condition, `:integer`, then stopped without checking the second, `:string`. The `spec/and` macro works the same as `clojure.core/and` in that is stops as soon as something fails. 53 | 54 | ```clojure 55 | (spec/explain ::meaning-of-life-int-or-string 24) 56 | ``` 57 | 58 | In this case we still have the value checked, the result and the predicate 59 | More information is provided as to where in the spec the value failed 60 | `:at` shows the path in the spec where the failure occurred, very useful for nested structures 61 | This shows the value of naming your specs descriptively 62 | 63 | 64 | ## Explain with a string 65 | 66 | rather than send information to the system out 67 | 68 | ```clojure 69 | (spec/explain-str ::meaning-of-life 24) 70 | ``` 71 | 72 | 73 | ```clojure 74 | (spec/explain-data ::meaning-of-life 24) 75 | ``` 76 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/hierarchical-specifications.md: -------------------------------------------------------------------------------- 1 | # Hierarchical Specifications 2 | 3 | Defining specifications for data that is hierarchical or nested in nature. 4 | 5 | ```clojure 6 | (ns practicalli.clojure 7 | (:require [clojure.spec.alpha :as spec])) 8 | ``` 9 | 10 | ## Example hierarchical data 11 | 12 | ```clojure 13 | {:top-level-key {:nested-key "value"}} 14 | ``` 15 | 16 | ## Individual specifications 17 | 18 | ```clojure 19 | (spec/def ::first-name string?) 20 | ``` 21 | 22 | ```clojure 23 | (spec/def ::last-name string?) 24 | ``` 25 | 26 | ```clojure 27 | (spec/def ::residential-address string?) 28 | ``` 29 | 30 | ## Composite Specification 31 | 32 | `keys` function combines specifications to form a composite specification in the form of a Clojure hash-map. 33 | 34 | ```clojure 35 | (spec/def ::customer-details 36 | (spec/keys 37 | :req [::first-name ::last-name ::residential-address])) 38 | ``` 39 | 40 | ## Hierarchical Specification 41 | 42 | A user account is composed of a user-id and customer details. Rather than include the individual customer details, the composite customer-details specification. 43 | 44 | The `::user-id` specification is as follows 45 | 46 | ```clojure 47 | (spec/def ::user-id uuid?) 48 | ``` 49 | 50 | The `::user-account` specification 51 | 52 | ```clojure 53 | (spec/def ::user-account 54 | (spec/keys 55 | :req [::user-id ::customer-details])) 56 | ``` 57 | 58 | The following data structure will conform to the specification 59 | 60 | ```clojure 61 | {::user-id #uuid "97bda55b-6175-4c39-9e04-7c0205c709dc" 62 | ::customer-details {::first-name "Jenny" 63 | ::last-name "Jetpack" 64 | ::residential-address "Earth"}} 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Spec for data 2 | 3 | Specifications can be defined for any data in Clojure, be that simple values or complex data structures. More complex specifications are composed of individual specifications, providing a flexible way to define specifications without building a brittle hierarchy. 4 | 5 | ## What is a specification 6 | 7 | Specifications can be [predicates](predicate-functions.md) (return true or false), [literal values](literal-values.md) in sets and [entity maps](entity-maps.md). 8 | 9 | There are many [predicate functions that come with Clojure](/reference/standard-library/predicate-functions.md) which help speed the creation of specifications. Clojure function definitions (`fn`, `defn`) can be used to define custom predicate functions too. 10 | 11 | ## Do values meet a specification 12 | 13 | The functions use to compare data with a specification are: 14 | 15 | * `conform` - test if data conforms to a specification, returning the conformed value 16 | * `valid?` - predicate to test if data conforms to a specification, returning true of false 17 | * `explain` - explain why a value is not conforming to a specification 18 | 19 | There are variations on explain, that present the results in different formats. 20 | 21 | ## Workflow for data specifications 22 | 23 | Using Clojure Specification is very flexible, they can be used as much or as little as required. 24 | 25 | Typically Specifications are created when data structures are being modeled, which can be done via experimenting in the REPL or taking a test first approach. Either way is viable. 26 | 27 | The generative tests section shows how specifications are used to generate mock data, so creating specifications earlier on in the development process will provide a wider range of data for unit tests and repl experimentation. 28 | 29 | 30 | !!! HINT "Spec and nil values" 31 | Some predicates do not consider `nil` as a valid value, espcially those predicates that check for a specific type 32 | 33 | `spec/nilable` will transform a predicate to use nil 34 | 35 | ```clojure 36 | (spec/valid? string? nil) 37 | 38 | (type "what type am I") 39 | (type nil) 40 | 41 | (spec/valid? (spec/nilable string?) nil) 42 | ``` 43 | 44 | 47 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/literal-values.md: -------------------------------------------------------------------------------- 1 | # Literal values 2 | 3 | Sets can be used as predicate functions returning true if the value is within the set 4 | 5 | Checking valid playing cards 6 | 7 | Define a namespace for the page and require Clojure Spec 8 | 9 | ```clojure 10 | (ns practicalli.clojure 11 | (:require [clojure.spec.alpha :as spec])) 12 | ``` 13 | 14 | ```clojure 15 | (spec/valid? #{:club :diamond :heart :spade} :club) 16 | ``` 17 | 18 | ```clojure 19 | (spec/valid? #{:club :diamond :heart :spade} 42) 20 | ``` 21 | 22 | Answer to the ultimate question? 23 | 24 | ```clojure 25 | (spec/valid? #{42} 42) 26 | ``` 27 | 28 | Using sets for literal values is similar to using the `clojure.core/contains?` function with a set collection type. 29 | 30 | ```clojure 31 | (contains? #{:clubs :diamonds :hearts :spades} :hearts ) 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/predicate-functions.md: -------------------------------------------------------------------------------- 1 | # Spec - Predicate functions 2 | 3 | A predicate is a function that returns a true or false value and their names end with `?` by convention. 4 | 5 | ```clojure 6 | (odd? 1) 7 | ``` 8 | 9 | ```clojure 10 | (string? "am i a string") 11 | ``` 12 | 13 | ```clojure 14 | (int? 2.3) 15 | ``` 16 | 17 | ```clojure 18 | (int? 2.3) 19 | ``` 20 | 21 | !!! HINT "`clojure.core` predicate functions" 22 | [`clojure.core` defines 80+ predicate functions](/reference/standard-library/predicate-functions.md) 23 | 24 | ## Predicate functions in specs 25 | 26 | Predicate functions can be used as un-named specifications to test values conform. 27 | 28 | Include the `clojure.spec.alpha` namespace to access the spec functions. 29 | 30 | ```clojure 31 | (require '[clojure.spec.alpha :as spec]) 32 | ``` 33 | 34 | ```clojure 35 | (spec/conform int? 42) 36 | ``` 37 | 38 | ```clojure 39 | (spec/conform seq? (range 4)) 40 | ``` 41 | 42 | ## Custom predicate functions 43 | 44 | Define custom predicate functions with `defn` or `fn` or the short form `#()` 45 | 46 | Using an anonymous function 47 | 48 | ```clojure 49 | (spec/conform (fn [value] (= value 42)) 42) 50 | ``` 51 | 52 | When the expression is quite terse, then the short form of an anonymous function is typically used. The `%` represents the value passed as an argument. 53 | 54 | ```clojure 55 | (spec/conform #(= % 42) 42) 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/registry.md: -------------------------------------------------------------------------------- 1 | # Registry for unique and re-usable specifications 2 | 3 | So far we have just use predicate functions directly in the code examples. 4 | 5 | Using a registry, specs can be uniquely defined across the whole project. Defining a spec gives that spec a name that has a fully qualified namespace 6 | 7 | Use the spec specific `def` function to bind a new spec name and fully qualified namespace and place it in the registry 8 | 9 | ```clojure 10 | (spec/def :playing-card/suit #{:club :diamond :heart :spade} ) 11 | ``` 12 | 13 | ```clojure 14 | (spec/conform :playing-card/suit :diamond) 15 | ``` 16 | 17 | ```clojure 18 | (spec/def :show-cats/cat-bread #{:abyssinian :birman :chartreau :devon-rex 19 | :domestic-short-hair :domestic-long-hair}) 20 | ``` 21 | 22 | ## Removing specs from the registry 23 | 24 | Named specifications can be removed from the registry by binding the name to `nil`. 25 | 26 | If specification names are to be refactored, then the original name should be set to `nil` and evaluated, before changing the name. This will ensure stale specifications are not residing in the REPL. 27 | 28 | Here is a named specification as an example 29 | 30 | ```clojure 31 | (spec/def ::unwanted #{:abandoned}) 32 | ``` 33 | 34 | The specification is evaluated in the REPL (above) and currently works. 35 | 36 | ```clojure 37 | (spec/conform ::unwanted :abandoned) 38 | ``` 39 | 40 | Remove this specification from the registry by binding it to nil 41 | 42 | ```clojure 43 | (spec/def ::unwanted nil) 44 | ``` 45 | 46 | Now the specification is unavailable 47 | 48 | ```clojure 49 | (spec/conform ::unwanted :abandoned) 50 | ``` 51 | 52 | !!! HINT "Registry not persistent" 53 | Restarting the REPL will loose all specification names in the registry as it is not persistent across REPL sessions. 54 | -------------------------------------------------------------------------------- /docs/clojure-spec/data/valid-q.md: -------------------------------------------------------------------------------- 1 | # Is the value valid? 2 | 3 | `clojure.spec.alpha/valid?` takes two arguments 4 | 5 | - a specification 6 | - a value to test against the specification 7 | 8 | `clojure.spec.alpha/valid?` is a predicate function. 9 | 10 | `true` is returned if the value meets the specification, otherwise `false` is returned. 11 | 12 | ## Require the Clojure spec library 13 | 14 | Set the namespace for the page and require clojure.spec.alpha library, setting the alias to `spec` 15 | 16 | ```clojure 17 | (ns practicalli.clojure.specifications 18 | (:require [clojure.spec.alpha :as spec])) 19 | ``` 20 | 21 | ## Using valid? 22 | 23 | If the value is valid then a boolean true is returned. Experiment with different values and [predicate functions](/reference/clojure/predicate-functions.md). 24 | 25 | ```clojure 26 | (spec/valid? even? 180) 27 | ``` 28 | 29 | 30 | ```clojure 31 | (spec/valid? string? "Am I a valid string") 32 | ``` 33 | 34 | 35 | # using custom predicate functions 36 | 37 | Create `fn` definitions to use as predicate functions. Any function that returns true or false can be used. 38 | 39 | ```clojure 40 | (spec/valid? (fn [value] (> value 1024)) 8080) 41 | ``` 42 | 43 | The custom predicate function may also be written in the shorter form of a `fn` definition 44 | 45 | ```clojure 46 | (spec/valid? #(> % 1024) 8080) 47 | ``` 48 | 49 | Use `def` to bind names to custom predicate functions if they are used more than once in the code base. 50 | 51 | In this example a name is bound to a function that checks if a port is within the [range of IANA registered networking ports][1]. 52 | 53 | ```clojure 54 | (def registered-port-range? 55 | "Network port number within IANA registered port range" 56 | #(and (> % 1024) #(< % 49151) ) 57 | 58 | (spec/valid? registered-port-range? 8080) 59 | ``` 60 | 61 | [1]: https://en.wikipedia.org/wiki/Port_(computer_networking)#Common_port_numbers 62 | -------------------------------------------------------------------------------- /docs/clojure-spec/functions/documentation.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | The Clojure `doc` function shows the doc string included in a function definition, eg. `defn` expressions. 4 | 5 | When a specification is defined for a function using `fdef` the specification is included in the output of the Clojure `doc` function. 6 | 7 | Including specification details clarifies the precise way to use the function and the information it expects. When a function has a specification the doc string for that function can focus on the purpose of the function rather than the specific types of data used, as that is covered by the function specification. 8 | 9 | ## Example 10 | 11 | ```clojure 12 | (clojure.repl/doc ::rank) 13 | 14 | ;; :practicalli.card-game-specifications/rank 15 | ;; Spec 16 | ;; (into #{:king :queen :ace :jack} (range 2 11)) 17 | ``` 18 | 19 | When adding a specification to a function definition, `doc` will also show the specification details along with the function doc-string. 20 | 21 | ## Live example 22 | 23 | Define the namespace and include clojure spec and clojure.repl (which contains the doc function) 24 | 25 | ```clojure 26 | (ns practicalli.clojure 27 | (:require [clojure.repl :as repl] 28 | [clojure.spec.alpha :as spec])) 29 | ``` 30 | 31 | Print the documentation for the `map` function 32 | 33 | ```clojure 34 | (repl/doc map) 35 | ``` 36 | 37 | Print the documentation for the `:playing-card/suit` 38 | 39 | ```clojure 40 | (clojure.repl/doc :playing-card/suit) 41 | ``` 42 | 43 | ```clojure 44 | #{:spade :heart :diamond :club} 45 | ``` 46 | 47 | ```clojure 48 | (repl/doc :cat-show:cat-bread) 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/clojure-spec/functions/function-definition-specifications.md: -------------------------------------------------------------------------------- 1 | # Function definition specifications 2 | 3 | `clojure.spec.alpha/fdef` defines a specification for a function definition, providing specific specification for 4 | 5 | * arguments passed when calling a function 6 | * return value expected 7 | * relationships between arguments and return value 8 | 9 | ## Examples 10 | 11 | The `practicalli.database-access/new-account-holder` function takes a customer details specification and returns an `account-holder-id` specification. 12 | 13 | !!! EXAMPLE "practicalli.database-access/new-account-holder" 14 | ``` 15 | (spec/fdef practicalli.database-access/new-account-holder 16 | :args (spec/cat :customer ::customer-details) 17 | :ret ::account-holder-id) 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/clojure-spec/functions/higher-order-functions.md: -------------------------------------------------------------------------------- 1 | # Higher order functions 2 | 3 | Higher order functions are common in Clojure and spec provides fspec to support spec’ing them. 4 | 5 | ```clojure 6 | (defn value-added-tax 7 | [tax-rate] 8 | #(+ (* tax-rate %) %)) 9 | ``` 10 | 11 | The value-added-tax function returns an anonymous function that adds the value of tax to the given value. 12 | 13 | Define a namespace for the page and require Clojure Spec 14 | 15 | ```clojure 16 | (ns practicalli.clojure 17 | (:require [clojure.spec.alpha :as spec])) 18 | ``` 19 | 20 | Declare a function spec for value-added-tax using `clojure.spec.alpha/fspec` for the return value: 21 | 22 | ```clojure 23 | (s/fdef value-added-tax 24 | :args (spec/cat :tax-rate number?) 25 | :ret (spec/fspec :args (s/cat :value number?) 26 | :ret number?)) 27 | ``` 28 | 29 | The `:ret` specification uses `fspec` to declare that the returning function takes and returns a number. 30 | -------------------------------------------------------------------------------- /docs/clojure-spec/functions/index.md: -------------------------------------------------------------------------------- 1 | # Specification for function definitions 2 | 3 | Define specifications for your custom functions 4 | 5 | * Additional documentation - argument and return values and the relationship between them. 6 | * Instrumenting functions - checking for correct argument values 7 | * Generative testing - using argument specifications to generate comprehensive test data. 8 | 9 | Many of the functions in `clojure.core` have [specifications](https://github.com/clojure/core.specs.alpha) in the latest version of Clojure. The specifications for clojure.core functions can be found in the [clojure/core.specs.alpha](https://github.com/clojure/core.specs.alpha) repository on GitHub. 10 | 11 | ## clojure.core examples 12 | 13 | Specifications used for the `defn`, `defn-`, `fn` functions in `clojure.core` 14 | 15 | !!! EXAMPLE "clojure.core specification examples" 16 | ```clojure 17 | (s/def ::param-list 18 | (s/and 19 | vector? 20 | (s/cat :params (s/* ::binding-form) 21 | :var-params (s/? (s/cat :ampersand #{'&} :var-form ::binding-form))))) 22 | 23 | (s/def ::params+body 24 | (s/cat :params ::param-list 25 | :body (s/alt :prepost+body (s/cat :prepost map? 26 | :body (s/+ any?)) 27 | :body (s/* any?)))) 28 | 29 | (s/def ::defn-args 30 | (s/cat :fn-name simple-symbol? 31 | :docstring (s/? string?) 32 | :meta (s/? map?) 33 | :fn-tail (s/alt :arity-1 ::params+body 34 | :arity-n (s/cat :bodies (s/+ (s/spec ::params+body)) 35 | :attr-map (s/? map?))))) 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/clojure-spec/generative-testing/example-projects/index.md: -------------------------------------------------------------------------------- 1 | # Example projects using Clojure Spec 2 | 3 | | Project | How the project uses Spec | 4 | |----------------------------------------|-----------------------------------------------------------------------------------| 5 | | [seancorfield/next-jdbc](next-jdbc.md) | Data specifications using predicates, function definition argument specifications | 6 | 7 | !!! HINT "More examples welcome" 8 | Other example projects that use interesting features of Spec are most welcome. [Raise an issue on the project issue tracker](https://github.com/practicalli/clojure-practicalli-content/issues) with details. 9 | -------------------------------------------------------------------------------- /docs/clojure-spec/generative-testing/example-projects/next-jdbc.md: -------------------------------------------------------------------------------- 1 | # Projects using Clojure spec - next-jdbc 2 | 3 | The next-jdbc project is a modern low-level Clojure wrapper for JDBC-based access to databases. 4 | 5 | The project defines data specifications using predicates and 6 | 7 | ## Defining specifications 8 | 9 | Specifications are defined within a single file [`src/next/jdbc/specs.clj`](https://github.com/seancorfield/next-jdbc/blob/master/src/next/jdbc/specs.clj). 10 | 11 | Specifications start with `clojure.spec.alpha/def` expressions, using predicate functions as specifications. There is also a custom predicate function called 12 | 13 | Function definition specifications follow, using the `clojure.spec.alpha/fdef` function. The `fdef` functions define the specification for the arguments of each function. The `fdef` function name is the same as the function definition it is defining a specification for. 14 | 15 | ## Instrumenting specifications 16 | 17 | Instrumenting functions provides automatic checking that argument in a function call conforms to the specification. 18 | 19 | Rather than write individual expressions to instrument each function, a var called `fns-with-specs` contains a collection of names for all the `fdef` function definition specifications. 20 | 21 | ```clojure 22 | (def ^:private fns-with-specs 23 | [`jdbc/get-datasource 24 | `jdbc/get-connection 25 | `jdbc/prepare 26 | `jdbc/plan 27 | `jdbc/execute! 28 | `jdbc/execute-one! 29 | `jdbc/transact 30 | `jdbc/with-transaction 31 | `connection/->pool 32 | `prepare/execute-batch! 33 | `prepare/set-parameters 34 | `prepare/statement 35 | `sql/insert! 36 | `sql/insert-multi! 37 | `sql/query 38 | `sql/find-by-keys 39 | `sql/get-by-id 40 | `sql/update! 41 | `sql/delete!]) 42 | ``` 43 | 44 | Instrument all the functions by passing `fns-with-specs` as an argument to the `clojure.spec.test.alpha/instrument` function. 45 | 46 | This call is wrapped in a simple handler function for convenience. 47 | 48 | ```clojure 49 | (defn instrument [] 50 | (clojure.spec.test.alpha/instrument fns-with-specs)) 51 | 52 | ``` 53 | 54 | To remove the checking of argument specifications, `clojure.spec.test.alpha/unstrument` is passed `fns-with-specs`, again wrapped in a convinced function. 55 | 56 | ```clojure 57 | (defn unstrument [] 58 | (clojure.spec.test.alpha/unstrument fns-with-specs)) 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/clojure-spec/generative-testing/index.md: -------------------------------------------------------------------------------- 1 | # Generative testing with Spec and Spec Test 2 | 3 | Clojure spec has been used so far to create specifications for both data and functions. 4 | 5 | Now spec and spec test libraries are used not just validity checking, but also to generate random samples of the data that can be used for extensive testing. 6 | 7 | Generative testing provides a far more effective alternative to unit testing. 8 | 9 | `clojure.spec.test/check/instrument` verifies that calls to a function satisfy the function's specification, the `:arg` in `fdef`. 10 | 11 | `clojure.spec.test/check` function generates 1000 data values to be used as the inputs to a function, checks that the invocation of the function satisfies its specification, the `:ret` and `:fn` in `fdef`. The argument specification, `:arg` in `fdef` is used to generate a wide range of results, which are more capable of finding edge cases that fail. 12 | 13 |

14 | 15 |

16 | 17 | 18 | ## Example: card game 19 | 20 | [practicalli/spec-generative-testing](https://github.com/practicalli/spec-generative-testing) is a simple card game with specifications that are used for basic generative testing. 21 | 22 |

23 | 24 |

25 | 26 | 27 | ## References 28 | 29 | * [Clojure.org guides: Spec - Generators](https://clojure.org/guides/spec#_generators) 30 | * [API reference: clojure.spec.gen.alpha](https://clojure.github.io/spec.alpha/clojure.spec.gen.alpha-api.html) 31 | * [API reference: clojure.spec.test.alpha](https://clojure.github.io/spec.alpha/clojure.spec.test.alpha-api.html) 32 | * [Video: How to do Stateful Property Testing in Clojure?](https://www.youtube.com/watch?v=xw8ZFU8CGdA) 33 | 34 | 45 | -------------------------------------------------------------------------------- /docs/clojure-spec/generative-testing/predicate-generators.md: -------------------------------------------------------------------------------- 1 | # Generators for predicate specifications 2 | 3 | Specifications are used to generate a wide range of random data. A generator for the specification is obtained and then data is generated. 4 | 5 | 6 | 15 | 16 | ## Predicate generators 17 | 18 | ```clojure 19 | (spec-gen/generate (spec/gen int?)) 20 | ``` 21 | 22 | ```clojure 23 | (spec-gen/generate (spec/gen nil?)) 24 | ``` 25 | 26 | ```clojure 27 | (spec-gen/sample (spec/gen string?)) 28 | ``` 29 | 30 | ```clojure 31 | (spec-gen/generate (spec/gen #{:club :diamond :heart :spade})) 32 | ``` 33 | 34 | ```clojure 35 | (spec-gen/sample (spec/gen #{:club :diamond :heart :spade})) 36 | ``` 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/clojure-spec/organising-specs.md: -------------------------------------------------------------------------------- 1 | # Organizing Specifications 2 | 3 | Data and function definition specifications are typically placed in a dedicated `specification` namespaces, e.g `src/domain/project/specification.clj`. 4 | 5 | Add the data specifications (`spec/def`), custom predicate functions and function specifications (`spec/fdef`) to the `specifications` namespace. 6 | 7 | Specifications for an architecture layer can be organised next to the namespaces managing the layer, e.g. database. 8 | 9 | Migrate specifications to a library once they are applicable to multiple projects. 10 | 11 | 12 | 13 | 14 | ## Instrumenting functions 15 | 16 | Add `spec-test/instrument` expressions to the `specifications` file, after the `spec/fdef` expressions. 17 | 18 | Rather than create individual expressions, create a `clojure.core/def` to contain a collection of all the `spec/fdef` expressions. This list can then be used to `instrument` and `unstrument` all the `spec/fdef` specifications. 19 | 20 | ```clojure 21 | (def ^:private function-specifications 22 | [`card-game/deal-cards 23 | `card-game/winning-player]) 24 | ``` 25 | 26 | Write simple helper functions to wrap the instrumenting of function specifications 27 | 28 | ```clojure 29 | (defn instrument-all-functions 30 | [] 31 | (spec-test/instrument function-specifications)) 32 | 33 | (defn unstrument-all-functions 34 | [] 35 | (spec-test/unstrument function-specifications)) 36 | ``` 37 | 38 | ## Unit testing 39 | 40 | Specifications can be incorporated into the existing unit tests, so it is sensible to keep them under the corresponding `test` directory files. 41 | 42 | ## Generative testing 43 | 44 | Using `spec-test/check` will generate 1000 data values for each expression, so by default these tests will take far longer that other tests. 45 | 46 | Configuring generative tests to only generate a small number of values will make `spec-test/check` expressions return almost instantaneously. In this example, only 10 data values are generated 47 | 48 | ```clojure 49 | (spec-test/check `deal-cards 50 | {:clojure.spec.test.check/opts {:num-tests 10}}) 51 | ``` 52 | 53 | Generative testing with small generators can be run regularly during development without impacting fast feedback. 54 | 55 | [:fontawesome-solid-book-open: Testing with Clojure Spec](/clojure/clojure-spec/testing/){.md-button} 56 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/bank-account/index.md: -------------------------------------------------------------------------------- 1 | # Spec project: Bank Account 2 | 3 | A relatively simple bank account application with data and function specifications, including generative testing data and function instrumentation. 4 | 5 | > #### Hint::Under active development 6 | > 7 | > Developed as part of the [Practicalli study guide live broadcasts](https://www.youtube.com/playlist?list=PLpr9V-R8ZxiBWGAuncfBRYhZtY5-Bp75s) 8 | 9 | ## Create deps.edn project 10 | 11 | Use Clojure CLI and `clj-new` 12 | 13 | ```shell 14 | clojure -M:new app practicalli/banking-on-clojure 15 | ``` 16 | 17 | > #### Hint::practicalli/banking-on-clojure repository 18 | > 19 | > [practicalli/banking-on-clojure repository](https://github.com/practicalli/banking-on-clojure-spec) contains the latest code to date for this project. 20 | 21 | ## Outline design of project 22 | 23 | Data Specifications are created for 24 | 25 | * Customer Details ✔ 26 | * Account holder ✔ 27 | * Bank account 28 | * Multiple Bank accounts 29 | * Credit Card 30 | * Mortgage 31 | 32 | Functions and specifications are created for 33 | 34 | * register-account-holder ✔ 35 | * open-credit-account 36 | * open-savings-account 37 | * open-credit-card-account 38 | * open-mortgage-account 39 | * Make a payment 40 | * Send account notification 41 | * Check for overdraft 42 | 43 | ## Development Workflow 44 | 45 | * Write a failing test ✔ 46 | * write mock data ✔ 47 | * write an function definition that returns the argument ✔ 48 | * run tests - tests should fail ✔ 49 | * write a spec for the functions argument - customer ✔ 50 | * write a spec for the return value ✔ 51 | * write a spec for relationship between args and return value 52 | * replace the mock data with generated values from specification ✔ 53 | * update functions and make tests pass ✔ 54 | * instrument functions 55 | * run specification checks 56 | 57 | ✔ 58 | 59 | Images to add 60 | 61 | Running tests that fail on a spec in CIDER 62 | spacemacs-cider-test-spec-fail-banking-on-clojure-project.png 63 | 64 | Running tests that fail on a spec on CircleCI 65 | circle-ci-banking-on-clojure-spec-test-runner-fail-register-account-holder-did-not-conform-to-spec.png 66 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/bank-account/rebel-readline.md: -------------------------------------------------------------------------------- 1 | # Using the project in rebel readline 2 | 3 | > #### Hint::TODO: add screenshots using Rebel readline REPL 4 | 5 | Start the rebel REPL 6 | 7 | ```shell 8 | clojure -M:repl/rebel 9 | ``` 10 | 11 | Once rebel has started a prompt will be displayed. 12 | 13 | First required the main namespace, containing the functions of the application. This loads the code in that namespace into the REPL. 14 | 15 | ```clojure 16 | (require 'practicalli.banking-on-clojure) 17 | ``` 18 | 19 | Now add the specifications for the project 20 | 21 | ```clojure 22 | (require 'practicalli.banking-on-clojure) 23 | ``` 24 | 25 | ? When does the TAB completion start to work ? 26 | 27 | Testing the specifications 28 | 29 | First change into the specifications namespace so the fully qualified names of the specs are not required. 30 | 31 | ```clojure 32 | (in-ns 'practicalli.banking-specifications) 33 | ``` 34 | 35 | Generate sample data from the specifications 36 | 37 | ``` 38 | (spec-gen/sample (spec/gen ::account-id)) 39 | ``` 40 | 41 | The function specifications and the instrument functions are loaded from the requires, so test by calling the instrumented functions, first with bad data and then with correct data. 42 | 43 | ```clojure 44 | (register-account-holder {}) 45 | ``` 46 | 47 | Use the specifications to generate good data 48 | 49 | ```clojure 50 | (register-account-holder ::customer-details) 51 | ``` 52 | 53 | Run generative tests on functions to check the return and fn values 54 | 55 | ```clojure 56 | (spec-test/check `register-account-holder) 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/bank-account/test-functions-against-spec.md: -------------------------------------------------------------------------------- 1 | ## Generative testing with `check` 2 | 3 | `clojure.spec.test.alpha/check` generates 1000 values from the argument section of a function definition specification. 4 | 5 | Pass the name of the function definition that has a specification to the `check` function. 6 | 7 | ```clojure 8 | (spec-test/check ``register-account-holder`) 9 | ``` 10 | 11 | ## Limiting the generated data 12 | 13 | 1000 tests can take a noticeable time to run, so check is not as often used during active development, as it would slow down the normal fast feedback cycle with Clojure. 14 | 15 | `check` takes an optional second argument which configures how the function operates. Passing a hash-map as a second argument will set the number of data values generated `{:clojure.spec.test.check/opts {:num-tests 100}}` 16 | 17 | ```clojure 18 | (spec-test/check 19 | `register-account-holder 20 | {:clojure.spec.test.check/opts {:num-tests 100}}) 21 | ``` 22 | 23 | Configuring `check` to run fewer tests provides a simple way to test multiple values without slowing down the development workflow. 24 | 25 | ## Reporting on Generative testing 26 | 27 | `clojure.spec.test.alpha/summarize-results` will return a brief summary including the total number of results and a count for how many results passed and failed. 28 | 29 | ```clojure 30 | (spec-test/summarize-results 31 | (spec-test/check `register-customer 32 | {:clojure.spec.test.check/opts {:num-tests 10}})) 33 | ``` 34 | 35 | Use the threading macro to summarize the results of multiple check operations 36 | 37 | ```clojure 38 | (->> (spec-test/check `register-account-holder) 39 | (spec-test/check `open-current-bank-account) 40 | (spec-test/summarize-results)) 41 | ``` 42 | 43 | If this expression is bound to a name then it can be called when ever the full suite of `check` generative testing is required. 44 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/bank-account/unit-tests-with-spec.md: -------------------------------------------------------------------------------- 1 | # Unit tests with specs 2 | 3 | Now that customer data and account-holder data has a specification, we can use the `clojure.spec.alpha/valid?` in the unity test code, as that function returns true or false. 4 | 5 | In this example the result of a call to `register-account-holder` is checked to see if it is valid against the `::account-holder` specification. This simplifies the code needed in unit test assertions, as Clojure spec is doing the work. 6 | 7 | ```clojure 8 | (deftest register-account-holder-test 9 | (testing "Basic registration - happy path" 10 | (is (= (set (keys (SUT/register-account-holder customer-mock))) 11 | (set (keys account-holder)))) 12 | 13 | (is (spec/valid? :practicalli.bank-account-spec/account-holder 14 | (SUT/register-account-holder customer-mock) ) ) 15 | )) 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/bank-account/write-failing-tests.md: -------------------------------------------------------------------------------- 1 | # Write failing tests 2 | 3 | In Test Driven Development style, first write unit tests for the banking functions. 4 | 5 | Edit the `src/practicalli/banking_on_clojure_test.clj` and add `deftest` tests 6 | 7 | ```clojure 8 | (deftest register-account-holder-test 9 | (testing "Basic registration - happy path" 10 | (is (= (set (keys (register-account-holder {}))) 11 | (set (keys {:account-id "123" :customer-name "Jenny Jetpack"})))))) 12 | ``` 13 | 14 | ## Write a function stub to run the tests 15 | 16 | The tests cannot run unless they call the function to be tested. A common approach it to write a function that returns the argument. 17 | 18 | ```clojure 19 | (defn register-account-holder 20 | "Register a new customer with the bank 21 | Arguments: 22 | - hash-map of customer-details 23 | Return: 24 | - hash-map of an account-holder (adds account id)" 25 | 26 | [customer-details] 27 | 28 | customer-details) 29 | ``` 30 | 31 | ## Add mock data 32 | 33 | Define some initial mock data to use with the unit tests 34 | 35 | ```clojure 36 | (def customer-mock 37 | {:first-name "Jenny" 38 | :last-name "Jetpack" 39 | :email-address "jenny@jetpack.org" 40 | :residential-address "42 meaning of life street, Earth" 41 | :postal-code "AB3 0EF" 42 | :social-security-id "123456789"}) 43 | ``` 44 | 45 | ```clojure 46 | account is a customer with a bank account id added 47 | 48 | (def account-holder-mock 49 | {:account-id #uuid "97bda55b-6175-4c39-9e04-7c0205c709dc" 50 | :first-name "Jenny" 51 | :last-name "Jetpack" 52 | :email-address "jenny@jetpack.org" 53 | :residential-address "42 meaning of life street, Earth" 54 | :postal-code "AB3 0EF" 55 | :social-security-id "123456789"}) 56 | ``` 57 | 58 | Update the test to use the mock data. 59 | 60 | ```clojure 61 | (deftest register-account-holder-test 62 | (testing "Basic registration - happy path" 63 | (is (= (set (keys (register-account-holder customer-mock))) 64 | (set (keys account-holder-mock)))))) 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/clojure-spec/projects/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Spec Projects 2 | 3 | A series of projects showing approaches to using specifications and generating data for testing. 4 | 5 | | Project | Description | 6 | |----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| 7 | | [card game](card-game/) | writing specifications and generating data from those specifications | 8 | | banking-on-clojure | simplified online bank account using TDD, data and functional specifications and generative testing | 9 | | [Bonbon card game](https://github.com/katox/bonbon) | A flavorful card game with clojure spec | 10 | | [Genetic Programming With clojure.spec](http://gigasquidsoftware.com/blog/2016/07/18/genetic-programming-with-clojure-dot-spec/) | | 11 | | [ClojureScript game with spec](https://deque.blog/2017/03/14/building-a-clojurescript-game-toughts-on-spec/) | | 12 | 13 | ## References 14 | 15 | * [Practicalli - Clojure Spec playlist](https://www.youtube.com/playlist?list=PLpr9V-R8ZxiBWGAuncfBRYhZtY5-Bp75s) - live coding to define specifications and generative testing 16 | -------------------------------------------------------------------------------- /docs/clojure-spec/testing/checking arguments.md: -------------------------------------------------------------------------------- 1 | # Checking arguments in function calls with specifications 2 | 3 | ## Instrument functions during development 4 | 5 | Instrumenting a function enables the checking of arguments in a function call against the specification defined in an `fdef` definition of the same name. 6 | 7 | ```clojure 8 | (clojure.spec.test.alpha/instrument `function-name) 9 | ``` 10 | 11 | Instrumenting a function swaps the function definition var with a wrapped version of the function definition which includes tests the `:args` spec from the `fdef` expression. 12 | 13 | `unstrument` returns the function definition to the original form and tests for that function are no longer run. 14 | 15 | ## Unit (Specification) testing 16 | 17 | You can generate data for interactive testing with gen/sample. 18 | -------------------------------------------------------------------------------- /docs/clojure-spec/testing/index.md: -------------------------------------------------------------------------------- 1 | # Testing with Specifications 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | ## During development 6 | 7 | Create specifications for data and functions 8 | 9 | Selectively instrument function definitions to check function call arguments against the function specification. 10 | 11 | * clojure.spec.test.alpha/instrument - check fdef :args 12 | 13 | ## Unit and integration testing 14 | 15 | Add specification checks along with unit testing and integration testing to provide a very wide range of data values to be tested (with a minimal amount of code). 16 | 17 | * clojure.spec.test.alpha/check - use :args to generate tests to check fdef :ret and :fn 18 | 19 | run a suite of spec-generative tests on an entire ns with `check`. Just one namespace per `check` expression? 20 | 21 | control the number of values check creates for each check expression. As the default is 1000 the checks can take a noticeable time to run (see [practicalli/spec-generative-testing](https://github.com/practicalli/spec-generative-testing)) 22 | 23 | Many built-in generators for `clojure.core` data predicates 24 | 25 | composite specifications can build generators upon predicate generators. 26 | 27 | Pass generator-returning functions to spec, supplying generators for things spec does not know about. 28 | Pass an override map to `gen` in order to supply alternative generators for one or more sub-paths of a spec. 29 | 30 | Define your own generators 31 | 32 | ## At run time 33 | 34 | Use specifications for run time checking, typically using `conform` and `valid?` functions. 35 | 36 | Specification are typically the minimal checks required for the system, compared to more extensive checks during test and system integration. 37 | 38 | Create lightweight private specifications for tests that run in the production environment. 39 | -------------------------------------------------------------------------------- /docs/coding-challenges/exercism/space-age.md: -------------------------------------------------------------------------------- 1 | # Space Age 2 | 3 | ![Exercism](https://raw.githubusercontent.com/practicalli/graphic-design/live/banners/exercism/exercisim-exercise-space-age-banner.png) 4 | 5 | ## Topics covered 6 | 7 | 8 | !!! HINT "Code for this solution on GitHub" 9 | [practicalli/exercism-clojure-guides](https://github.com/practicalli/exercism-clojure-guides/) contains the design journal and solution to this exercise and many others. 10 | 11 | ## Create the project 12 | 13 | Download the RNA transcription exercise using the exercism CLI tool 14 | 15 | !!! NOTE "" 16 | ```shell 17 | exercism download --exercise=rna-transcription --track=clojure 18 | ``` 19 | 20 | !!! HINT "Use the REPL workflow to explore solutions locally" 21 | Open the project in a [Clojure aware editor](/clojure/clojure-editors) and [start a REPL](/clojure/coding-challenges/exercism/#repl-workflow), using a rich comment form to experiment with code to solve the challenge. 22 | 23 | 24 | ## Challenge introduction 25 | 26 | Given an age in seconds, calculate how old someone would be on: 27 | 28 | - Earth: orbital period 365.25 Earth days, or 31557600 seconds 29 | - Mercury: orbital period 0.2408467 Earth years 30 | - Venus: orbital period 0.61519726 Earth years 31 | - Mars: orbital period 1.8808158 Earth years 32 | - Jupiter: orbital period 11.862615 Earth years 33 | - Saturn: orbital period 29.447498 Earth years 34 | - Uranus: orbital period 84.016846 Earth years 35 | - Neptune: orbital period 164.79132 Earth years 36 | 37 | So if you were told someone were 1,000,000,000 seconds old, you should be able to say that they're 31.69 Earth-years old. 38 | -------------------------------------------------------------------------------- /docs/coding-challenges/koans.md: -------------------------------------------------------------------------------- 1 | # ClojureScript Koans 2 | 3 | Koans are a collection of small challenges that slowly increase in complexity. They are similar to the 4Clojure challenges in scope. 4 | 5 | > #### TODO::work in progress, sorry 6 | -------------------------------------------------------------------------------- /docs/coding-challenges/palindrome/index.md: -------------------------------------------------------------------------------- 1 | # Project Palindrome 2 | 3 | In this section we will create a simple Clojure project using Leiningen and build up a palindrome checker step by step. 4 | 5 | We will start with the simplest possible thing we can create and steadily add 6 | 7 | ## What is a Palindrome 8 | 9 | For this project it is assumed that a palindrome is a string of characters from the English alphabet and not any other language or an alphanumeric sequence. 10 | 11 | It is assumed that a palindrome is at least 3 characters long, meaning a single character cannot be a palindrome. If a single character was a palindrome, then any valid sequence would contain at least as many palindromes as characters in that sequence. 12 | 13 | ## Challenge 14 | 15 | Write an algorithm to find the 3 longest unique palindromes in a string. For the 3 longest palindromes, report the palindrome, start index and length in descending order of length. Any tests should be included with the submission. 16 | 17 | For example, the output for string, `"sqrrqabccbatudefggfedvwhijkllkjihxymnnmzpop"` should be: 18 | 19 | ```text 20 | Text: hijkllkjih, Index: 23, Length: 10 21 | Text: defggfed, Index: 13, Length: 8 22 | Text: abccba, Index: 5 Length: 6 23 | ``` 24 | 25 | * Check for a palindrome 26 | * Generate a series of palindromes 27 | -------------------------------------------------------------------------------- /docs/coding-challenges/palindrome/simple-palindrome-test.md: -------------------------------------------------------------------------------- 1 | # Simple palindrome test 2 | -------------------------------------------------------------------------------- /docs/continuous-integration/index.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration 2 | 3 | ![Continuous Integration banner](https://raw.githubusercontent.com/practicalli/graphic-design/live/banners/continuous-integration-banner.png) 4 | 5 | Topics to be covered in this section include: 6 | 7 | - [ ] Continuous Integration services 8 | - [X] [Circle CI](circle-ci/) 9 | - [X] [GitHub Workflow](github-workflow/) 10 | - [ ] GitLab CI 11 | - [ ] Configure deployment pipelines 12 | - [ ] Manage environment variables 13 | - [ ] Security & Secrets 14 | - [ ] Deployment 15 | - [X] Amazon AWS 16 | - [X] Render.com 17 | 18 | !!! Hint "CircleCI example in Practicalli Clojure Web Services" 19 | [Banking on Clojure](https://practical.li/clojure-web-services/projects/banking-on-clojure/continuous-integration/){target=_blank} is an example of Continuous Integration using CircleCI, with LambdaIsland/Kaocha as the test runner and Heroku as the deployment pipeline. 20 | 21 | ## 12 Factor approach 22 | 23 | Following the [12 factor principles](https://12factor.net/){target=_blank}, the deployment is driven by source code to multiple environments. 24 | 25 | ## CircleCI service 26 | 27 | Use Yaml language to write CI workflows and tasks, using Docker images as a consistent run-time environment 28 | 29 | A commercial service with [a generous free Cloud plan](https://circleci.com/pricing/){target=_blank} - (6,000 minutes), providing highly optomises container images to run tasks efficiently. The CircleCI Clojure images contain Clojure CLI, Leiningen and Babashka pre-installed. 30 | 31 | [CircleCI Orbs](https://circleci.com/orbs/){target=_blank} package up common configuration and tools, greatly simplifying the configuration and maintenance required. 32 | 33 | [CircleCI Clojure language guide](https://circleci.com/docs/2.0/language-clojure/){target=_blank .md-button} 34 | 35 | ## GitHub Workflow 36 | 37 | Use Yaml language to write CI workflows and tasks. 38 | 39 | A commercial service with a modest free plan (2,000 minutes) for open source projects. GitHub Marketplace contains a wide range of Actions, including [Clojure related actions](https://github.com/marketplace?type=actions&query=clojure+){target=_blank}, simplifying the configuration of CI. 40 | 41 | [Setup Clojure](https://github.com/marketplace/actions/setup-clojure){target=_blank} provides Clojure CLI, Leinigen and boot tools for use within the CI workflow 42 | 43 | [GitHub Actions overview](https://github.com/features/actions){target=_blank .md-button} 44 | -------------------------------------------------------------------------------- /docs/control-flow/cond.md: -------------------------------------------------------------------------------- 1 | # cond 2 | -------------------------------------------------------------------------------- /docs/control-flow/if.md: -------------------------------------------------------------------------------- 1 | # if 2 | -------------------------------------------------------------------------------- /docs/control-flow/when.md: -------------------------------------------------------------------------------- 1 | # when 2 | -------------------------------------------------------------------------------- /docs/core.async/bike-assembly-line/index.md: -------------------------------------------------------------------------------- 1 | # core.async scenario: Bike assembly line 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > Pull requests are welcome 6 | 7 | In this example we are going to use a bicycle assembly line as the process we want to make concurrent. The tasks involved in making our bicycle are: 8 | 9 | * Making the frame 10 | * Painting the wheels 11 | * Making the rims 12 | * Making the wheels (adding hub and spokes to wheels - different hub for front and rear wheels) 13 | * Making the handlebars 14 | * Fitting tyres to the rims (solid rims, so no tubes) 15 | * Attaching the handlebars to the frame 16 | * Attaching wheels to the frame 17 | * Attaching crank to frame 18 | * Attaching peddles to the crank 19 | * Connecting control system wires (gears, breaks) 20 | 21 | ## Current build process 22 | 23 | At the moment, each person creates one bicycle all by themselves. This means they need all sorts of different tools and are switching tasks all the way through assembling the bike. 24 | 25 | We want to move to a more parallel approach, so as we automate this process we will evaluate what actions can be done in parallel and which must be done sequentially (i.e. painting the frame must come after making the frame). 26 | -------------------------------------------------------------------------------- /docs/core.async/clacks-messages/index.md: -------------------------------------------------------------------------------- 1 | # Clacks Messenger with core.async 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > Pull requests are welcome 6 | 7 | In a previous exercise we built a simple encoder/decoder to send messages via the Clacks message service (as designed by Sir Terry Pratchett, RIP). 8 | 9 | Now we will use core.async to create a processing queue between each Clack towers, so we can then model, monitor and visualise a Clacks messenger system with multiple Clacks towers. For additional fun we will enable new Clacks towers to come on line and connect to the existing system. 10 | -------------------------------------------------------------------------------- /docs/core.async/toy-car-assembly-line/index.md: -------------------------------------------------------------------------------- 1 | # core.async example - toy car assembly line 2 | 3 | >####Note::Get the example project and open it in a Clojure REPL 4 | > Clone or download the [Lispcast: Clojure core-async Factory example](https://github.com/ericnormand/lispcast-clojure-core-async) 5 | > 6 | > Open that project in your Clojure editor or run `lein repl` in the top level directory of the project. 7 | 8 | ## The toy car factory 9 | 10 | The toy car factory assembles car parts before distributing them. How can we make this process faster and more scalable? 11 | 12 | ## The current process 13 | 14 | One worker picks out random parts of a car from the parts box until all the parts are collected to assemble the car. 15 | 16 | ## The `time` macro 17 | 18 | We will use the time macro to see how long parts of our code takes to run and help find parts to optimise. 19 | 20 | A simple example would be: 21 | 22 | ```clojure 23 | (time 24 | (map inc (range 0 10000))) 25 | ``` 26 | 27 | ## Timing assembly functions 28 | 29 | Investigate the time it takes to carry out the different assembly line tasks 30 | 31 | ```clojure 32 | (time (take-part)) 33 | 34 | (time (attach-wheel :body :wheel)) 35 | 36 | (time (box-up :body)) 37 | 38 | (time (put-in-truck :body)) 39 | ``` 40 | 41 | And to total time it takes to get a a whole car through the assembly line 42 | 43 | ```clojure 44 | (time (build-car)) 45 | ``` 46 | 47 | The total time can be longer than the sum of the tasks, as the `take-part` function does not always give the required part needed. 48 | 49 | ## Hiring more workers 50 | 51 | Adding 10 more workers is equivalent to adding 10 processes that run the assembly tasks. 52 | 53 | Lets use a go block for a worker 54 | 55 | ```clojure 56 | (do 57 | (go 58 | (dotimes [number 10] 59 | (println "First go block processing:" number) 60 | (Thread/sleep 1200))) 61 | (go 62 | (dotimes [number 10] 63 | (println "Second go block processing:" number) 64 | (Thread/sleep 1200)))) 65 | ``` 66 | 67 | These are two separate go blocks, so their is no co-ordination between the two. You can see the println statement from each go block intertwined. 68 | -------------------------------------------------------------------------------- /docs/data-inspector/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Data Inspector tools 2 | 3 | Clojure has a strong focus on built in data structures (list, vector, hash-map, set) to represent information in the system. Tools to inspect data and browse through nested or large data sets are invaluable in understanding what the system is doing. 4 | 5 | There are many `clojure.core` functions that can be used to explore data structures and transform them to produce specific views on data. 6 | 7 | `tap>` and `datafy` provide an elegant way of exploring data, rather than a classic `println` expression. 8 | 9 | Data Inspector tools capture and visualize results from evaluated expressions as well as tools to specifically visualize `tap>` expressions (Reveal, Portal). 10 | 11 | ## Common approaches 12 | 13 | * [:fontawesome-solid-book-open: Portal](portal.md) tool to navigate and visualise data via `tap>` for use with any editor or directly with a REPL prompt 14 | * [:fontawesome-solid-book-open: Clojure inspector](clojure-inspector.md) built-in Java Swing based inspector 15 | * [:fontawesome-solid-book-open: CIDER inspect](https://practical.li/spacemacs/evaluating-clojure/inspect/){target=_blank} Emacs specific tool (Practicalli Spacemacs) 16 | 17 | 18 | !!! TIP "Practicalli recommends Portal" 19 | [:fontawesome-solid-book-open: Portal](portal.md) works with any Clojure connected editor and can inspect `tap>` expressions and automatically inspect all evaluation results over nREPL for a complete REPL history. 20 | 21 | Practicalli sends all log events to Portal using a custom mulog publisher. 22 | -------------------------------------------------------------------------------- /docs/data-structures/alternatives.md: -------------------------------------------------------------------------------- 1 | # Alternative data structures 2 | 3 | Whist list, vector, hash-map and set are by far the most commonly used data structures, Clojure has many others of interest. 4 | 5 | ## Variations on hash-maps 6 | 7 | ## Variations on sets 8 | 9 | ordered-set 10 | -------------------------------------------------------------------------------- /docs/data-structures/hash-maps/update.md: -------------------------------------------------------------------------------- 1 | # Update Hash-maps 2 | -------------------------------------------------------------------------------- /docs/data-structures/pretty-printing.md: -------------------------------------------------------------------------------- 1 | # Pretty Printing data structures 2 | 3 | Data structures containing small amounts of data are quite human readable, although can benefit from pretty printing to make them very easy for humans to read. 4 | 5 | The larger a data structure becomes, or if a data structure is nested, then there are tools to print out ascii views of the data structures. 6 | 7 | ## Pretty print hash-maps 8 | 9 | ```clojure 10 | (clojure.pprint/pprint 11 | {:account-id 232443344 :account-name "Jenny Jetpack" :balance 9999 :last-update "2021-12-12" :credit-score :aa} ) 12 | ``` 13 | 14 | Each key is printed on a new line, making the hash-map easier to read, especially when there are a large number of keys 15 | 16 | ```clojure 17 | {:account-id 232443344, 18 | :account-name "Jenny Jetpack", 19 | :balance 9999, 20 | :last-update "2021-12-12", 21 | :credit-score :aa} 22 | ``` 23 | 24 | Clojure aware editors can also have an align option when formatting hash-maps, making the results easier to read 25 | 26 | ```clojure 27 | {:account-id 232443344, 28 | :account-name "Jenny Jetpack", 29 | :balance 9999, 30 | :last-update "2021-12-12", 31 | :credit-score :aa} 32 | ``` 33 | 34 | > #### Hint::Pretty Print evaluation results 35 | > 36 | > Clojure aware editors should allow the pretty printing of the evaluation results. 37 | 38 | ## Print Table of nested data structures 39 | 40 | Nested data structures can also be shown as a table, especially the common approach of using a vector of hash-maps where each map has the same keys 41 | 42 | ```clojure 43 | (clojure.pprint/print-table 44 | [{:location "Scotland" :total-cases 42826 :total-mortality 9202} 45 | {:location "Wales" :total-cases 50876 :total-mortality 1202} 46 | {:location "England" :total-cases 5440876 :total-mortality 200202}]) 47 | ``` 48 | 49 | ```none 50 | | :location | :total-cases | :total-mortality | 51 | |-----------+--------------+------------------| 52 | | Scotland | 42826 | 9202 | 53 | | Wales | 50876 | 1202 | 54 | | England | 5440876 | 200202 | 55 | ``` 56 | 57 | ## References 58 | 59 | [Data browsers](/clojure-cli/data-browsers/) (Cider Inspector, Portal, Reveal Free) are very useful for larger and nested data structures. 60 | -------------------------------------------------------------------------------- /docs/data-structures/set.md: -------------------------------------------------------------------------------- 1 | # Set 2 | 3 | A Clojure set is a persistent data structure that holds a unique set of elements. Again the elements can be of any type, however each element must be unique for a valid set. 4 | 5 | > **Note** Explore creating sets from existing collections. Notice what happens if you have duplicate values in the collection. Define sets directly using the `#{}` notation and see what happens if there are duplicate values. 6 | 7 | ```clojure 8 | (set `(1 2 3 4)) 9 | (set `(1 2 1 2 3 4)) 10 | 11 | #{1 2 3 4} 12 | #{:a :b :c :d} 13 | ;; duplicate key error 14 | #{1 2 3 4 1} 15 | ``` 16 | 17 | ## Unique but not ordered 18 | 19 | A set is not ordered by the values it contains. If you need a sorted set then you can use the `sorted-set` function when creating a new set. Or you can run 20 | 21 | ```clojure 22 | (sorted-set 1 4 0 2 9 3 5 3 0 2 7 6 5 5 3 8) 23 | 24 | (sort [9 8 7 6 5]) 25 | (sort-by ) 26 | ``` 27 | 28 | # Looking up values in a set 29 | 30 | ```clojure 31 | (#{:a :b :c} :c) 32 | (#{:a :b :c} :z) 33 | ``` 34 | 35 | Sets can also use the `contains?` function to see if a value exists in a set 36 | 37 | ```clojure 38 | (contains? 39 | #{"Palpatine" "Darth Vader" "Boba Fett" "Darth Tyranus"} 40 | "Darth Vader") 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/data-structures/shared-memory.md: -------------------------------------------------------------------------------- 1 | # Shared memory with Persistent data structures 2 | 3 | The Clojure data structures are immutable, so they initially seem similar to constants rather than variables. Once a collection is created, it cannot be changed. Any functions that run on a collection do not change the collection, instead they return a new collection with the respective changes. 4 | 5 | Creating a new collection each time may seem inefficient, however, the persistent collections use a sharing model. When a new collection is created, it links to all the relevant elements of the original collection and adds any new elements. 6 | 7 | ![Persistent data structures](/images/clojure-persistent-data-structures-sharing.png) 8 | 9 | > **Hint** Read the InfoQ article on [An In-Depth Look at Clojure Collections](https://www.infoq.com/articles/in-depth-look-clojure-collections/). 10 | -------------------------------------------------------------------------------- /docs/defining-behaviour-with-functions/calling-functions.md: -------------------------------------------------------------------------------- 1 | # Calling Functions 2 | 3 | To call a function in Clojure you use the name of the function as the first element of a list. 4 | 5 | In this simple example, a function is defined that takes no arguments, then that function is called. 6 | 7 | ```clojure 8 | (defn my-function [] 9 | (str "I only return this string")) 10 | 11 | (my-function) 12 | ``` 13 | 14 | Functions can be defined to take arguments. 15 | 16 | ## Arity 17 | 18 | This is the term to describe the number of arguments a function takes. This can be a fixed number or variable number of arguments. 19 | 20 | Simple polymorphism can also be used to have one function take different numbers of arguments, as with the `multi-arity` function in the examples below. 21 | 22 | ```clojure 23 | (defn single-arity [] 24 | (str "I do not take any arguments")) 25 | 26 | (defn single-arity [argument] 27 | (str "I take 1 argument only")) 28 | 29 | (defn triple-arity [argument1 argument2 argument3] 30 | (str "I take 3 arguments only")) 31 | 32 | (defn multi-arity 33 | ([argument] 34 | (str "I match 1 argument only")) 35 | ([argument1 argument2] 36 | (str "I match when 2 arguments are used"))) 37 | 38 | (defn variable-arity [argument & more-arguments] 39 | (str "I assign the first argument to argument, 40 | all other arguments to more-arguments")) 41 | ``` 42 | 43 | --- 44 | -------------------------------------------------------------------------------- /docs/defining-behaviour-with-functions/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | -------------------------------------------------------------------------------- /docs/defining-behaviour-with-functions/index.md: -------------------------------------------------------------------------------- 1 | # Defining behaviours with functions 2 | 3 | Clojure has functions, rather than methods for defining behaviour / "algorithms" 4 | 5 | Clojure design at its most basic comprises: 6 | 7 | - one or more data structures 8 | - functions that process those data-structures 9 | 10 | There is a common saying in Clojure: "Its better to have one data structure and many functions, than many data structures and many functions" 11 | -------------------------------------------------------------------------------- /docs/defining-behaviour-with-functions/parameters.md: -------------------------------------------------------------------------------- 1 | # Parameters 2 | -------------------------------------------------------------------------------- /docs/defining-behaviour-with-functions/syntax.md: -------------------------------------------------------------------------------- 1 | # Syntax 2 | 3 | Defining functions is done with the `fn` function 4 | 5 | We have already seen the `def` function to assign names to values. We can also use the same function to give a name to our functions. 6 | -------------------------------------------------------------------------------- /docs/designing-data-structures/index.md: -------------------------------------------------------------------------------- 1 | # Designing Data Structures 2 | 3 | Some common design guides for data structures in Clojure 4 | 5 | # The Basics design approach 6 | 7 | Most data structures in Clojure seem to be created from either vectors or maps or a combination of both. Sets are used where uniqueness of values is important and lists are often used for their lazy properties. 8 | 9 | **Vectors** are the most flexible data structure in Clojure and support none-sequential access as they are indexed. 10 | 11 | **Maps** are really useful for defining semantic meaning to your data structures, helping you create data structures that express the context of the model they represent. Maps give you unordered, arbitrary index arrangement. Access is iteration of key/value pairs or getting a value for a given key. 12 | 13 | **Lists** give you sequential, one-at-a-time arrangement. They allow for efficient iteration, lazy generation, and stack discipline. 14 | 15 | **Sets** give you unordered, unique constraint arrangement. Access is iteration of elements or checking containment. 16 | -------------------------------------------------------------------------------- /docs/designing-data-structures/modeling-alphabet-codes.md: -------------------------------------------------------------------------------- 1 | # Model alphabet codes 2 | 3 | Maps in Clojure are used to model key and value pairs. 4 | 5 | * Keys must be unique within a map. 6 | * A key can be a number, string or keyword. 7 | 8 | Vectors in Clojure are a general data structure that are good for handing any kind of information. 9 | 10 | !!! NOTE "Name a data structure" 11 | Define a name for a data structure where each letter of the alphabet is represented by a 6 digit binary code 12 | 13 | 14 | ??? EXAMPLE "Example solution" 15 | Define a name called `alphabet` that is bound to a map. Each key in the map is a character of the alphabet and each value is a vector of numbers that represent a binary code. 16 | 17 | The map includes a binary code for a full stop and space character, to help create sentences. 18 | 19 | ```clojure 20 | (def alphabet {"A" [0 1 0 0 0 1] 21 | "B" [0 0 1 0 1 0] 22 | "C" [0 1 0 0 1 0] 23 | "D" [1 0 1 0 0 0] 24 | "E" [1 0 1 1 0 0] 25 | "F" [1 1 0 1 0 0] 26 | "G" [1 0 0 1 1 0] 27 | "H" [1 0 1 0 0 1] 28 | "I" [1 1 1 0 0 0] 29 | "J" [0 0 1 1 1 1] 30 | "K" [0 1 0 1 0 1] 31 | "L" [1 1 1 0 0 1] 32 | "M" [1 1 1 0 1 1] 33 | "N" [0 1 1 1 0 1] 34 | "O" [1 1 0 1 1 0] 35 | "P" [1 1 1 1 1 0] 36 | "Q" [1 0 1 1 1 0] 37 | "R" [1 1 1 1 0 0] 38 | "S" [0 1 1 1 1 0] 39 | "T" [1 0 0 1 1 1] 40 | "U" [0 0 1 0 1 1] 41 | "V" [0 1 1 0 0 1] 42 | "W" [1 1 0 1 0 1] 43 | "X" [1 0 1 0 1 0] 44 | "Y" [1 0 0 0 1 1] 45 | "Z" [1 1 0 0 1 1] 46 | "." [1 0 1 1 0 1] 47 | " " [0 0 1 0 0 0]}) 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/designing-data-structures/with-maps.md: -------------------------------------------------------------------------------- 1 | # Design with Maps 2 | 3 | Maps allow you to model data with its contextual meaning. The keys of a map can give the context and the values are the specific data. 4 | 5 | !!! NOTE "" 6 | Define a shopping list of items you want, including how many of each item you want to buy 7 | 8 | ??? EXAMPLE "" 9 | ```clojure 10 | (def shopping-list 11 | {"cat food" 10 12 | "soya milk" 4 13 | "bread" 1 14 | "cheese" 2}) 15 | ``` 16 | 17 | !!! NOTE "" 18 | Define a star-wars characters, eg. luke skywalker, jarjar binks. The star-wars character should include a name and a skill (it doesn't matter what these are). 19 | 20 | > Use the 'get' function to return the value of a given key, eg. name. Use keywords to return a given value if you used keywords for the map keys. 21 | 22 | ??? EXAMPLE "" 23 | In this answer we have defined three different star-wars characters, all using the same map keys. 24 | 25 | ```clojure 26 | (def luke {:name "Luke Skywalker" :skill "Targeting Swamp Rats"}) 27 | (def darth {:name "Darth Vader" :skill "Crank phone calls"}) 28 | (def jarjar {:name "JarJar Binks" :skill "Upsetting a generation of fans"}) 29 | ``` 30 | 31 | Lets see what the specific skill luke has 32 | 33 | ```clojure 34 | (get luke :skill) 35 | ``` 36 | 37 | When you use a keyword, eg. :name, as the key in a map, then that keyword can be used as a function call on the map to return its associated value. Maps can also act as functions too. 38 | 39 | ```clojure 40 | (:name luke) 41 | (luke :name) 42 | ``` 43 | 44 | There are also specific functions that work on maps that give all the `keys` of a map and all the `values` of that map 45 | 46 | ```clojure 47 | (keys luke) 48 | (vals luke) 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/designing-data-structures/with-vectors-of-vectors.md: -------------------------------------------------------------------------------- 1 | # With Vectors of Vectors 2 | 3 | The most frequent use of you will see is in the `project.clj` file, where a vector of vectors is used to model the library dependencies for a project 4 | 5 | ```clojure 6 | [[org.clojure/clojure "1.8.0"] 7 | [org.clojure/core.match "0.3.0-alpha4"]] 8 | 9 | [[org.clojure/clojure "1.6.0"] 10 | [ring "1.4.0-beta2"] 11 | [compojure "1.3.4"] 12 | [hiccup "1.0.5"]] 13 | ``` 14 | 15 | > **Fixme** Think of an exercise to create a vector of vectors as a data model 16 | -------------------------------------------------------------------------------- /docs/designing-data-structures/with-vectors.md: -------------------------------------------------------------------------------- 1 | # With Vectors 2 | 3 | Vectors as the simplest data structure in Clojure to work with. They are very similar to an array in other languages, although they have additional qualities in Clojure. 4 | 5 | Vectors 6 | 7 | * can be of any length 8 | * are indexed so have fast random access 9 | * can contain any types 10 | * are immutable 11 | 12 | !!! NOTE "" 13 | Define a data structure for a simple shopping list with any items you would typically want to buy. 14 | 15 | 16 | ??? EXAMPLE "" 17 | ```clojure 18 | (def shopping-list ["Cerial" "Baked Beans" "Cat food" "Quorn chicken pieces" ]) 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/development-environments/index.md: -------------------------------------------------------------------------------- 1 | # Development Environments 2 | 3 | ![Leiningen and LightTable logo](/images/clojure-practicalli-banner.png) 4 | 5 | This workshop encourages [LightTable](http://lighttable.com/) & [Leiningen](http://leiningen.org/) as the development environment, as they are the easiest tools to set up. 6 | 7 | Leiningen is the build automation tool used to manage Clojure projects. It will create projects from templates and run our Clojure environment (REPL). 8 | 9 | LightTable is a Clojure aware editor that supports the dynamic workflow of Clojure development in a REPL. LightTable is also written in Clojure (and ClojureScript). 10 | 11 | The following pages will show you how to set up LightTable and Leiningen. 12 | -------------------------------------------------------------------------------- /docs/development-environments/java.md: -------------------------------------------------------------------------------- 1 | ## Java - a host platform for Clojure 2 | 3 | [![Oracle Java](/images/java-banner.png)](https://www.java.com) 4 | 5 | You will need to have a Java Runtime Edition (usually installed on most computers by default) to run any Clojure applications. Version 8 is recommended (although version 6 & 7 should work). 6 | 7 | To test if you have Java on your computer, open a command line window and run the command 8 | 9 | java -version 10 | 11 | ## Installing the Java Runtime Edition 12 | 13 | Download and install the latest [Oracle Java SDK](https://www.java.com) (version 1.8 at time of writing). 14 | 15 | Alternatively, install [OpenJDK](http://openjdk.java.net/install/index.html) or [Zulu build of OpenJDK](http://zulu.org/) 16 | 17 | ## Ubuntu 18 | 19 | The OpenJDK is available as a package on Ubuntu and can be installed via the Ubuntu software center or via the command line: 20 | 21 | sudo apt-get install openjdk-8-jre 22 | 23 | ## Why is Java Required 24 | 25 | Clojure was designed as a hosted language, which means it is developed and run on top of Java's Virtual Machine (JVM). However, _its not necessary to learn the Java language to use Clojure_. 26 | 27 | Clojure is compiled into Java bytecode when you evaluate the code. This compilation happens in the background so you dont usually see it happening. For example, if you are using the Clojure REPL then each time you evaluate an expression it is compiled into Java bytecode and then injected into the running REPL and the results are then returned. This all happens pretty instantaneously. 28 | 29 | Most of the current Clojure tooling was developed for Clojure on the JVM, for example Leiningen. 30 | 31 | As Clojure runs on Java you can also use all the other libraries that run on the Java Virtual machine, regardless of whether those libraries were written in Java, Clojure, Scala, JRuby, jython, Groovy, etc. 32 | -------------------------------------------------------------------------------- /docs/development-environments/leiningen.md: -------------------------------------------------------------------------------- 1 | # Leiningen Build tool 2 | 3 | [![Leiningen - automating Clojure projects](/images/leiningen-banner.png)](http://leiningen.org/) 4 | 5 | [leiningen.org](http://leiningen.org/) (pronounced line-ing-en) is a very powerful build automation tool for automating Clojure projects. With Leiningen you can: 6 | 7 | * Create Clojure Projects with templates 8 | * Define and manage dependencies 9 | * Run an interactive Clojure environment (REPL) 10 | * Run unit tests using Clojure.test 11 | * Run your Clojure application 12 | * Create a deployable Clojure application, as Java Jar file 13 | * Deploy a Clojure library to a remote repository 14 | 15 | ![Leiningen - build automation for Clojure](/images/leiningen-repl-custom-prompt.png) 16 | 17 | ## Install Leiningen 18 | 19 | Download the install script from [leiningen.org](http://leiningen.org/) and run the Leiningen script in a terminal 20 | 21 | On Linux and MacOSX, make the script executable first 22 | 23 | chmod a+x lein 24 | ./lein 25 | 26 | > **Hint** I put the `lein` script in `~/bin` directory which is part of my operating system execution path ($PATH). To include the `~/bin` directory in the system path, I add the following code to the `~/.profile` file 27 | 28 | 29 | 30 | ## Testing Leiningen is working 31 | 32 | Test that Leiningen is installed with the following command 33 | 34 | lein version 35 | 36 | Output should look similar to: 37 | 38 | Leiningen 2.6.1 on Java 9-internal OpenJDK 64-Bit Server VM 39 | -------------------------------------------------------------------------------- /docs/development-environments/lighttable.md: -------------------------------------------------------------------------------- 1 | # LightTable 2 | 3 | LightTable is a simple development tool that supports Clojure, ClojureScript, JavaScript and Python languages. The tool is open source and written in Clojure & ClojureScript (with a little JavaScript & CSS) 4 | 5 | ![](/images/lighttable-screens.png) 6 | 7 | # Install Lighttable 8 | 9 | Download [lighttable.com](http://lighttable.com) and follow the suggested instructions: 10 | 11 | **MacOSX** 12 | Install the `lighttable.dmg` file just as any other MacOSX package 13 | 14 | **Linux** 15 | Extract the contents of the downloaded lighttable file to a suitable directory (`/usr/local` or `~/apps`). Add `LightTable` to the system `$PATH`, or add the following script to the system `$PATH`. 16 | 17 | 18 | 19 | **Windows** 20 | Download the windows zip file for LightTable and extract the installer, following the instructions inside the installer. 21 | 22 | ## LightTable configuration 23 | 24 | Lighttable configuration is in the file `user.behaviours`. Open the user behaviours file, `Ctrl-space` and type `user behaviors`. When you save the file, `Ctrl-s`, changes are applied immediately. 25 | 26 | **Sample User Behaviours file** 27 | 28 | Here is a sample of user behaviours file for LightTable 29 | 30 | 31 | 32 | ## Using LightTable 33 | 34 | LightTable has an online tutorial entitled [Getting started with LightTable](http://docs.lighttable.com/tutorials/full/) 35 | 36 | I create a project first with Leiningen, open the project directory in the LightTable workspace and open any files I want to work with. I then connect the open editor window for the file by pressing `Ctrl-Enter` at the end of an expression. 37 | 38 | > **Hint** my approach is documented in the [quick demo section of my Clojure & LightTable slides](http://jr0cket.co.uk/slides/jax-london-2013-light-table.html#/sec-12) from JAXLondon 2013. 39 | -------------------------------------------------------------------------------- /docs/games/index.md: -------------------------------------------------------------------------------- 1 | # Writing Games with Clojure 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > Pull requests are welcome 6 | 7 | Games are driven by events and require state to be managed, so are a good way to explore how to manage state with immutable values. 8 | 9 | For games in Clojure the events are simply function calls and we prefer to pass the state around rather than have a central mutable container for our state. 10 | 11 | This section will contain several games that have been built using a functional approach with immutable data structures. 12 | 13 | * TicTacToe on the command line 14 | 15 | > #### Hint::Games in ClojureScript 16 | > 17 | > There is a section on games in the Practicalli ClojureScript book, including [a TicTacToe game using Reagent](https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/index.html) (react.js style library) and Scalable Vector Graphics (SVG). 18 | -------------------------------------------------------------------------------- /docs/games/tictactoe-cli/create-project.md: -------------------------------------------------------------------------------- 1 | # Create a Clojure project 2 | > 3 | > #### TODO::work in progress, sorry 4 | 5 | Create a project for our game. 6 | 7 | {% tabs deps="deps.edn projects", lein="Leiningnen projects" %} 8 | 9 | {% content "deps" %} 10 | Create a new project using `clj-new` alias, found in [:fontawesome-solid-book-open: Practicalli Clojure CLI Config]({{ book.P9IClojureDepsEdn }}) 11 | 12 | ```shell 13 | clojure -M:new practicalli/tictactoe-cli 14 | ``` 15 | 16 | Open the project in [a Clojure aware editor](/clojure-editors/) or run a rebel REPL 17 | 18 | ```shell 19 | clojure -M:repl/rebel 20 | ``` 21 | 22 | Once the rebel REPL is running, load the project and change to the main namespace 23 | 24 | ```clojure 25 | (require 'practicalli/tictactoe-cli) 26 | 27 | (in-ns 'practicalli/tictactoe-cli) 28 | ``` 29 | 30 | {% content "lein" %} 31 | The default Leiningen template is suitable fine for the project as no additional libraries are used. 32 | 33 | ``` 34 | lein new tictactoe-cli 35 | ``` 36 | 37 | > #### Hint::Alternatively clone the github repository 38 | > 39 | > You can also clone the tictactoe-cli game from GitHub 40 | 41 | ```shell 42 | git clone https://github.com/practicalli/tictactoe-cli.git 43 | ``` 44 | 45 | ## Updating Clojure version and licence 46 | 47 | In the `project.clj` file I have updated Clojure to version 1.10.0 and changed the licence to be the more open Creative Commons license. 48 | 49 | ```clojure 50 | (defproject tictactoe-cli "0.1.0-SNAPSHOT" 51 | :description "TicTacToe game played on the command line" 52 | :url "https://github.com/practicalli/tictactoe-cli" 53 | :license {:name "Creative Commons Attribution Share-Alike 4.0 International" 54 | :url "https://creativecommons.org"} 55 | :dependencies [[org.clojure/clojure "1.10.0"]]) 56 | ``` 57 | 58 | I also removed the `license` file and added a brief description of the project to the `README.md` file 59 | 60 | {% endtabs %} 61 | -------------------------------------------------------------------------------- /docs/introduction/concepts/all-bytecode-in-the-end.md: -------------------------------------------------------------------------------- 1 | # Its all Bytecode in the end 2 | 3 | > The REPL is your compiler 4 | 5 | ![Clojure - conceptual view of how Clojure runs](../images/clojure-evaluation-process.png) 6 | 7 | As soon as you evaluate your code in the REPL it is also being compiled in the background into Java Bytecode. So there is no need for a separate build and run phase. 8 | 9 | Injecting code into the running environment provides the means for fast iterative development of code. 10 | -------------------------------------------------------------------------------- /docs/introduction/concepts/naming-local.md: -------------------------------------------------------------------------------- 1 | # Naming - local scope 2 | 3 | ## Local names in functions 4 | 5 | You can define names for things within the scope of a function using the `let` function. 6 | 7 | ### Example 8 | 9 | You can use the let function to define a simple expression, for which everything will go out of scope once it has been evaluated 10 | 11 | ``` 12 | (let [local-name "some value"]) 13 | (let [minutes-in-a-day (* 60 60 24)]) 14 | ``` 15 | 16 | You can also use `let` inside a function to do something with the arguments passed to that function. Here we calculate the hourly-rate from a yearly salary, returning the calculated-rate. 17 | 18 | (defn hourly-rate [yearly-salary weeks-in-year days-in-week] 19 | (let [calculated-rate (/ yearly-salary weeks-in-year days-in-week)] 20 | calculated-rate)) 21 | 22 | (hourly-rate 60000 48 5) 23 | 24 | ``` 25 | 26 | 27 | 28 | ## Local names in data structures 29 | 30 | When defining a map you are creating a series of key value pairs. The key is essentially a name that represents the value it is paired with. Keys are often defined using a `:keyword`. 31 | 32 | ```clojure 33 | {:radius 10, :pi 22/7 :colour purple} 34 | 35 | (def my-circle {:radius 10, :pi 22/7 :colour purple}) 36 | ``` 37 | 38 | > **Fixme** This is incorrect, as a Clojure keyword type (a name starting with :) have global scope within a namespace. If the keys were strings, then they would have the scope of just the collection. 39 | -------------------------------------------------------------------------------- /docs/introduction/concepts/naming-things.md: -------------------------------------------------------------------------------- 1 | # Naming things - data structures and functions 2 | 3 | The `def` function is used to name data structures in Clojure. 4 | 5 | You can also use `def` to name functions, however it is more common to use `defn` (which is a macro around def) to give a function a name. 6 | 7 | ## Keeping things private 8 | 9 | There is less emphasis on keeping functions and data structures private (compared to Java, C++, C#). If you want to define a function name so that it is only accessible by other functions of the same namespace, you can use the `defn-` function. 10 | 11 | There is no private equivalent for `def` (as of Clojure 1.6) however you can use metadata to specify this 12 | 13 | (def ^:private name data) 14 | 15 | > TODO: check if there is anything new around this or other common practices 16 | 17 | ## Misc - writing a private def macro 18 | 19 | You could write your own macro to create a private `def` called `def-` 20 | 21 | ```clojure 22 | (defmacro def- [item value] 23 | `(def ^{:private true} ~item ~value) 24 | ) 25 | ``` 26 | 27 | > There are no naming conventions for a private symbol name. As its defined an used within the scope of that one namespace (file), then there is no real need to make a special convention. Private functions will just be called as normal within the namespace and it will be quite clear from the function definition that it is private. 28 | 29 | [Clojure community style guide](https://github.com/bbatsov/clojure-style-guide) 30 | 31 | ## example 32 | 33 | Learning Clojure #4: private functions 34 | 35 | 36 | Sometimes in a Clojure file you just want some helper functions that shouldn’t be exposed outside the namespace. You can create a private function using the special defn- macro instead of defn. 37 | 38 | For instance, create a file foo/bar.clj with a public and a private function: 39 | 40 | (ns foo.bar) 41 | (defn- sq [x] (* x x)) 42 | (defn sum-squares [a b] (+ (sq a) (sq b))) 43 | 44 | Then use it from the REPL: 45 | 46 | user=> (use 'foo.bar) 47 | nil 48 | user=> (sum-squares 3 4) 49 | 25 50 | user=> (sq 5) 51 | java.lang.Exception: Unable to resolve symbol: sq in this context (NO_SOURCE_FILE:6) 52 | -------------------------------------------------------------------------------- /docs/introduction/concepts/purpose.md: -------------------------------------------------------------------------------- 1 | # When to use Clojure 2 | 3 | Clojure is a general purpose language suitable for any kind of application or service. As Clojure implementations run across multiple technology platforms and operating systems, there are very few barriers to its use. 4 | 5 | So Clojure is great for webapps, data science, big data, finance industry (banking, trading, insurance, etc), devops tools (log analysis, etc) and anything else really. 6 | 7 | There are areas where Clojure obviously excels. 8 | 9 | ## Effective Data Manipulation 10 | 11 | Fundamentally all software systems take in data (in the form of values or events), process or react to that data and return as a result. 12 | 13 | The persistent data structures in Clojure (list, vector, hash-map and set) provide an efficient way to use immutable collections of data. 14 | 15 | The `clojure.core` library contains a vast number of data processing functions in Clojure so data is easily transformed 16 | 17 | ## Highly Scalable 18 | 19 | Clojure code is encouraged to be immutable and functions to be pure, you can run millions of parallel instances of your application or service for massive processing power. These features also vastly simplify concurrent programming. 20 | 21 | ## Reducing Complexity 22 | 23 | Clojure encourages a component design through functional composition, breaking down problems into components 24 | 25 | Clojure and its libraries are all great examples of well designed components and the community strongly encourages this approach. 26 | 27 | > #### Hint::Functional Reactive Programming 28 | > 29 | > You can also use ClojureScript for Functional Reactive programming of client-side apps for browsers and mobile device. 30 | -------------------------------------------------------------------------------- /docs/introduction/concepts/what-is-functional-programming.md: -------------------------------------------------------------------------------- 1 | # What is Functional Programming 2 | 3 | Functional programming can seem quite different from imperative programming used in languages like C, C++ and Java. 4 | 5 | Imperative languages may seem easier initially, as defining one step after another is familiar approach to many things in live. As the scale of a system grows, so does complexity. Imperative languages applied object oriented design to manage complexity with varied rates of success. 6 | 7 | When shared mutable state is common in an OO design, then a system quickly becomes complex and very difficult to reason about. 8 | 9 | Functional programming is actually simpler that the OO approach, although initially it may be unfamiliar and not considered as easy. As systems grow in complexity, the building blocks are still simple and deterministic, creating a system that is far easier to reason about. 10 | 11 | ## Imperative programming languages 12 | 13 | In Imperative languages code is written that specifies a **sequential of instructions** that complete a task. These instructions typically **modifies program state** until the desired result is achieved. 14 | 15 | Variables typically represent **memory addresses that are mutable** (can be changed) by default. 16 | 17 | ![Imperative program - conceptual view](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/functional-programming-imperative-program.png) 18 | 19 | ## Functional programming languages 20 | 21 | Individual tasks are small and achieved by passing data to a function which returns a result. 22 | 23 | Functions are **composed** together to form more complex tasks and satisfy larger business logic. These composed functions pass the result of their evaluation to the next function, until all functions in the composition have been evaluated. 24 | 25 | The entire functional program can be thought of as a single function defined in terms of smaller ones. 26 | 27 | Program execution is an **evaluation of expressions**, with the nesting structure of function composition determining program flow. 28 | 29 | Data is **immutable** and cannot be change once created. Changes are expressed as new values, with complex values [sharing common values](/data-structures/shared-memory.md) for efficiency. 30 | 31 | ![Functional program - conceptual view](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/functional-composition-illustrated.png) 32 | -------------------------------------------------------------------------------- /docs/introduction/five-steps-to-clojure.md: -------------------------------------------------------------------------------- 1 | # 5 Steps to Clojure 2 | 3 | ## Set up your environment 4 | 5 | Install Clojure and a build tool 6 | 7 | Setup a Clojure aware editor 8 | 9 | * Emacs & CIDER - Spacemacs, Doom, Prelude 10 | * Neovim & Conjure 11 | * VSCode & Clover or Calva 12 | * Sublime Text & SublimedClojure 13 | 14 | ## Learn the syntax 15 | 16 | ## Practice the core functions 17 | 18 | * 4clojure.org 19 | * Exercism.io 20 | 21 | ### def / defn / let 22 | 23 | ### map / reduce / apply 24 | 25 | ### for / while / loop / recur 26 | 27 | ## Adopt functional programming practices 28 | 29 | ## Learn the commonly used libraries 30 | 31 | ### Server-side websites 32 | 33 | #### Ring / Compojure / Reitit / Hiccup | Selma 34 | 35 | ### React client-side single page apps 36 | 37 | #### React.js / Om-next / Reagent / Re-frame 38 | 39 | #### core.async 40 | 41 | ### Full Stack apps 42 | 43 | #### Kit Framework 44 | -------------------------------------------------------------------------------- /docs/iterate-over-data/apply.md: -------------------------------------------------------------------------------- 1 | # apply 2 | 3 | > #### TODO::work in progress, sorry 4 | -------------------------------------------------------------------------------- /docs/iterate-over-data/comp.md: -------------------------------------------------------------------------------- 1 | # comp - composing functions 2 | 3 | > #### TODO::work in progress, sorry 4 | -------------------------------------------------------------------------------- /docs/iterate-over-data/filter-remove.md: -------------------------------------------------------------------------------- 1 | # filter and remove 2 | 3 | Use filter and remove with predicate functions, those returning true or false, to create a sub-set of the data. 4 | 5 | filter creates a new collection that contains all the matching values from the predicate function (true). 6 | 7 | `remove` creates a new collection with contains all the values that didn't match the predicate function (false). 8 | 9 | > #### TODO::work in progress, sorry 10 | 11 | ```clojure 12 | (filter odd? [1 2 3 4 5 6 7 8 9]) 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/iterate-over-data/index.md: -------------------------------------------------------------------------------- 1 | # Iterate over data 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | Clojure data is typically within one or more of the built in collection types (vector, map, list, set). 6 | 7 | We can use some functions in Clojure core directly on these collection types. Other clojure core functions need a little help. 8 | 9 | ## map 10 | 11 | Used to create a new collection by applying a given function to each element of the collection in turn. 12 | 13 | ```clojure 14 | (map inc [1 2 3]) 15 | ``` 16 | 17 | If there are multiple collections, map returns a new collection with values created by calling the function with a value from each of the collections. Once map reaches the end of one collection it stops and returns the result. 18 | 19 | ```clojure 20 | (map + [1 2 3] [4 5 6] [7 8 9]) 21 | ``` 22 | 23 | ## apply 24 | 25 | Used to remove all the values from a collection so they are treated as individual arguments to the function given to apply. 26 | 27 | ```clojure 28 | (= (apply + [1 2 3]) 29 | (+ 1 2 3)) 30 | ``` 31 | 32 | ## reduce 33 | 34 | reduce can be used in a similar way as apply, to transform a collection into a different value. 35 | 36 | reduce can also take an argument referred to as an accumulator, used to keep local state as reduce iterates through the values in the collection. 37 | 38 | A function used with reduce is called a reducing function and is a more abstract approach to loop/recur although its possible to give your reducing function a name so is more reusable. 39 | 40 | ## threading macros 41 | 42 | Write code that reads as a sequential series of function calls, rather that the nested function calls typical in lisp. 43 | 44 | A threading macro is often used to thread a collection through a number of function calls and expressions. 45 | 46 | ## comp 47 | 48 | Compose functions together that work over a collection. It can be seen as a more abstract approach to a threading macro or nested function calls. 49 | 50 | ## transduce 51 | 52 | Used like comp to create a pipeline of function calls, however, each function call or expression must return a transducer (transforming reduction). Many `clojure.core` functions return a transducer if you do not provide the collection argument. 53 | -------------------------------------------------------------------------------- /docs/iterate-over-data/map-fn.md: -------------------------------------------------------------------------------- 1 | # map with fn - anonymous function 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | ```clojure 6 | (map (fn [arg] (+ arg 5)) [1 2 3 4 5]) 7 | ``` 8 | 9 | There is a syntactic short-cut for the anonymous function that does not require a name for the arguments 10 | 11 | ```#(+ %1 5)``` 12 | 13 | Adding this into our previous expression we can see that its still quite readable and helps keep the code clean. 14 | 15 | ```clojure 16 | (map #(+ arg 5) [1 2 3 4 5]) 17 | ``` 18 | 19 | > #### Hint::Anonymous function name 20 | > 21 | > Anonymous functions do not have an externally referable name, so must be used in-line with an expression. 22 | > 23 | > The `fn` function can be defined with a name, however, this is only available in the scope of that function definition, the name cannot be used to refer to that function outside of its definition. 24 | > 25 | > Including a name within a `fn` definition enables the function to call itself, therefore creating an anonymous recursive function. 26 | -------------------------------------------------------------------------------- /docs/iterate-over-data/map-partial.md: -------------------------------------------------------------------------------- 1 | # map with partial 2 | 3 | > #### TODO::work in progress, sorry 4 | -------------------------------------------------------------------------------- /docs/iterate-over-data/map.md: -------------------------------------------------------------------------------- 1 | # map function 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > Add examples of using the map function with data 6 | -------------------------------------------------------------------------------- /docs/iterate-over-data/reduce.md: -------------------------------------------------------------------------------- 1 | # reduce 2 | 3 | > #### TODO::work in progress, sorry 4 | -------------------------------------------------------------------------------- /docs/iterate-over-data/transduce.md: -------------------------------------------------------------------------------- 1 | # transduce and transforming reducers 2 | 3 | > #### TODO::work in progress, sorry 4 | -------------------------------------------------------------------------------- /docs/lazy-evaluation.md: -------------------------------------------------------------------------------- 1 | 2 | `repeat` 3 | -------------------------------------------------------------------------------- /docs/libraries/clojars.md: -------------------------------------------------------------------------------- 1 | # Clojars 2 | -------------------------------------------------------------------------------- /docs/libraries/clojure-core-lisp-comprehension.md: -------------------------------------------------------------------------------- 1 | # Libraries: `clojure.core` lisp comprehension 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > discuss functions for list comprehension 6 | > `for` 7 | -------------------------------------------------------------------------------- /docs/libraries/clojure-core.md: -------------------------------------------------------------------------------- 1 | # Understanding Clojure Libraries: `clojure.core` 2 | 3 | > #### Warning::Very rough draft of an idea 4 | > 5 | > Experimenting with the value of grouping functions in clojure.core to help ensure you are exposed to most of the concepts in Clojure 6 | 7 | # Families of functions 8 | 9 | * List Comprehension (for, while, ) 10 | * used to process multiple collections 11 | 12 | * Transformations (map, filter, apply, reduce ) 13 | * transform the contents of a collection 14 | -------------------------------------------------------------------------------- /docs/libraries/edn.md: -------------------------------------------------------------------------------- 1 | # edn 2 | -------------------------------------------------------------------------------- /docs/libraries/index.md: -------------------------------------------------------------------------------- 1 | # Libraries 2 | -------------------------------------------------------------------------------- /docs/libraries/om.md: -------------------------------------------------------------------------------- 1 | # om 2 | -------------------------------------------------------------------------------- /docs/modifying-data-structures/index.md: -------------------------------------------------------------------------------- 1 | # Modifying data structures 2 | 3 | Wait, I thought you said that data structures were immutable! So how can we change them then? 4 | 5 | Yes, lists, vectors, maps and sets are all immutable. However, you can get a new data structure that has the changes you want. To make this approach efficient, the new data structure contains only the new data and links back to the existing data structure for shared data elements. 6 | 7 | We will see some of the most common functions that work with data structures in this section. In actuality, everything can be considered a function that works on a data structure though, as that is the language design of clojure. 8 | -------------------------------------------------------------------------------- /docs/modifying-data-structures/lists.md: -------------------------------------------------------------------------------- 1 | # Lists 2 | 3 | You can change lists with the `cons` function, see `(doc cons)` for details 4 | 5 | (cons 5 '(1 2 3 4)) 6 | 7 | You will see that `cons` does not change the existing list, it create a new list that contains the number 5 and a link to all the elements of the existing list. 8 | 9 | You can also use cons on vectors `(cons 5 [1 2 3 4])` 10 | 11 | ``` 12 | (cons "fish" '("and" "chips")) 13 | 14 | (conj '(1 2 3 4) 5) 15 | 16 | (conj [1 2 3 4] 5) 17 | 18 | 19 | ;; Lets define a simple list and give it a name 20 | (def list-one '(1 2 3)) 21 | 22 | ;; the name evaluates to what we expect 23 | list-one 24 | 25 | ;; If we add the number 4 using the cons function, then we 26 | ;; get a new list in return, with 4 added to the front (because thats how lists work with cons) 27 | (cons 4 list-one) 28 | 29 | ;; If we want to keep the result of adding to the list, we can assign it a different name 30 | (def list-two (cons 4 list-one)) 31 | ;; and we get the result we want 32 | list-two 33 | 34 | ;; we can also pass the original name we used for the list to the new list 35 | (def list-one (cons 4 list-one)) 36 | 37 | ;; If we re-evaluate the definition above, then each time we will get an extra 38 | ;; number 4 added to the list. 39 | 40 | list-one 41 | 42 | ;; Again, this is not changing the original list, we have just moved the name 43 | ;; of the list to point to the new list. 44 | ;; Any other function working with this data structure before reassigning the name 45 | ;; will not be affected by the re-assignment and will use the unchanged list. 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/modifying-data-structures/sets.md: -------------------------------------------------------------------------------- 1 | # Sets 2 | -------------------------------------------------------------------------------- /docs/modifying-data-structures/vectors.md: -------------------------------------------------------------------------------- 1 | # Vectors 2 | -------------------------------------------------------------------------------- /docs/puzzles/index.md: -------------------------------------------------------------------------------- 1 | # Puzzles 2 | 3 | Simple puzzles to help you start thinking functionally 4 | -------------------------------------------------------------------------------- /docs/puzzles/random-seat-assignment.md: -------------------------------------------------------------------------------- 1 | # Random Seat assignment 2 | 3 | 4 | 5 | Take a functional / data oriented approach to solving this problem 6 | 7 | ## Description 8 | 9 | You want to randomly assign seating to a number of people for a fixed number of seats. Each seat is represented by an integer number between 1 and 30. 10 | 11 | How do you randomly assign seats without choosing the same seat twice. 12 | 13 | ## Loop / recur approach 14 | 15 | Bad... 16 | 17 | ## recursive function 18 | -------------------------------------------------------------------------------- /docs/reference/changing-state.md: -------------------------------------------------------------------------------- 1 | # Changing State 2 | -------------------------------------------------------------------------------- /docs/reference/clojure-cli/index.md: -------------------------------------------------------------------------------- 1 | # Reference: Clojure CLI 2 | 3 | A reference on using Clojure CLI and using community tools effectively. 4 | 5 | * structure of the deps.edn configuration 6 | * execution options 7 | * Java Virtual Machine options 8 | * defining custom aliases 9 | * common aliases from practicalli/clojure-deps-edn project 10 | -------------------------------------------------------------------------------- /docs/reference/clojure-svg/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Scalable Vector Graphics - SVG 2 | 3 | Scalable Vector Graphics, SVG, is an image format for two-dimensional (2D) graphics. 4 | 5 | An SVG image uses data to describe how to draw an image, ensuring that images can shrink and scale easily and retain a high quality image. As images are formed from data, shapes can easily be combined or intersected to form new shapes. Using a data format also means SVG images can be created from code and therefore animated. 6 | 7 | Raster image formats like gif, jpeg and png use a grid of squares called pixels to define an image (also known as a bitmap). Each pixel has a colour and position in an image. When zooming into an image the pixels grow larger distorting the sharpness of an image, referred to as [pixelation](https://en.wikipedia.org/wiki/Pixelation), .Multiple versions of raster images are often created at different resolutions to reduce the loss of quality when viewed at different sizes. 8 | 9 | > #### Hint::Work in progress 10 | 11 | ## Concepts 12 | 13 | - viewbox 14 | - style - border, background, width, height, stoke, fill, draw (path) 15 | - shapes - circle, path 16 | 17 | ## Viewbox 18 | 19 | A [viewbox](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) defines a co-ordinate system for the image. Defining a size for the viewbox defining a frame for the image where positions are relative to that frame, irrespective of the size of the image or how that image is scaled. 20 | 21 | A viewbox size should be selected to make the image as simple as possible to define within itself. 22 | 23 | Example: [tictactoe O's and X's and the grid that represents the board](https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/index.html). 24 | 25 | tictactoe O's and X's and the grid that represents the board 26 | 27 | ## Related projects 28 | 29 | - TicTacToe with ClojureScript, Reagent and SVG 30 | - System monitoring 31 | - Practicalli SVG examples library 32 | - Programming SVG with Clojure (TODO) 33 | 34 | ## References 35 | 36 | - [SVG: Scalable Vector Graphics - Mozilla Developer network](https://developer.mozilla.org/en-US/docs/Web/SVG) 37 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/assigning-names.md: -------------------------------------------------------------------------------- 1 | # Assigning Names 2 | 3 | If we have to type the same values over and over, it would be very hard to write a program. What we need are names for values, so we can refer to them in a way we can remember. We do that using `def`. 4 | 5 | ```clojure 6 | (def mangoes 3) 7 | (def oranges 5) 8 | (+ mangoes oranges) 9 | ``` 10 | 11 | When you assign a name to a value, that name is called a _symbol_. You can assign more than simple values to symbols. Try the following: 12 | 13 | ```clojure 14 | (def fruit (+ mangoes oranges)) 15 | (def average-fruit-amount (/ fruit 2)) 16 | average-fruit-amount 17 | ``` 18 | 19 | Look at the last line, and see how we can use symbols by themselves to refer to a value. 20 | 21 | > **Note** Take the Clojure syntax you have learnt to far and write a metric/imperial converter 22 | 23 | Take your height in feet and inches and convert it to inches using arithmetic in Clojure. 24 | 25 | Then convert that to centimeters. There are 2.54 centimeters in an inch. 26 | 27 | Lastly, ask two people near you for their height in centimeters. Find the average of your heights. 28 | 29 | > **Note** Bonus: Convert that average back to feet and inches. The feet and the inches will be separate numbers. `(quot x y)` will give you the whole number part when dividing two numbers. `(mod x y)` will give you the remainder when dividing two numbers. 30 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/global-definitions.md: -------------------------------------------------------------------------------- 1 | # Global definitions 2 | 3 | > **Fixme** work in progress 4 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/local-bindings.md: -------------------------------------------------------------------------------- 1 | # Local Bindings 2 | 3 | > **Fixme** work in progress 4 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/naming.md: -------------------------------------------------------------------------------- 1 | # Naming 2 | 3 | ## Naming when requiring other namespaces 4 | 5 | `(require [cheshire.core :refer :all])` is an example of self-inflicted errors, as this library included a `contains?` function that will over-write the `clojure.core/contains?` function when using `:refer :all` or the `(use )` expression. 6 | 7 | This situation is one example of why `:refer :all` and `use` are not recommended and can cause lots of debugging headaches. 8 | 9 | If a namespace is predominantly about using a specific library, then refer specific functions as they are used within the current namespace 10 | 11 | ```clojure 12 | (ns current.namespace 13 | (:require 14 | [cheshire.core :refer [function-name another-function etc])) 15 | ``` 16 | 17 | > #### Hint::clj-kondo lint tool shows unused functions 18 | > 19 | > Using clj-kondo 20 | 21 | A classic example is a test namespace that uses clojure core 22 | (ns practicalli.random-function-test 23 | (:require [clojure.test :refer [deftest is testing]] 24 | [practicalli.random-function :as sut])) 25 | Otherwise use a meaningful alias, ideally referring to what that library is doing (which makes it easer to swap out with a different library later on if required). As Cheshire is a JSON related library, then 26 | (ns my.ns 27 | (:require [cheshire.core :as json])) 28 | This gives a context to all the functions called from that library and makes code easier for humans to understand. 29 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/private-functions.md: -------------------------------------------------------------------------------- 1 | # Private functions 2 | 3 | > **Fixme** work in progress 4 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/quick-look-at-types.md: -------------------------------------------------------------------------------- 1 | # A quick look at types 2 | 3 | As we mentioned before, underneath Clojure lurks Java byte code so there are going to be types in Clojure. However, Clojure being a dynamic language, most of the time you can just let Clojure manage the types for you. 4 | 5 | > **Hint** When you run Clojure on a different host platform, eg. .Net or Javascript (via Clojurescript), Clojure will use the types of that host platform. 6 | 7 | Should you want to know the type of something you are working on, you can use two functions, `type` and `class`. 8 | 9 | > **Note** Discover the class or type of some common Clojure code 10 | 11 | ```clojure 12 | (class 1) 13 | (class 1.1) 14 | (class "") 15 | (class true) 16 | (class false) 17 | (class nil) 18 | 19 | (class ()) 20 | (class (list 1 2 3 4)) 21 | (class (str 2 3 4 5)) 22 | (class (+ 22/7)) 23 | 24 | (type [1 2 3]) 25 | (type {:a 1 :b 2}) 26 | (type (take 3 (range 10))) 27 | ``` 28 | 29 | ![](../images/clojure-playground-types-examples.png) 30 | 31 | > **Hint** If you cant live without static type checking, look at [core.typed](http://typedclojure.org/), a type system for Clojure all in one library 32 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/ratios.md: -------------------------------------------------------------------------------- 1 | # Ratios 2 | 3 | In mathematics you need to ensure that you manage precision of your calculations when you are dividing numbers. Once you create a decimal number then everything it touches had a greater potential to becoming a decimal. 4 | 5 | > **Note** Calculate a rough approximation to Pi by dividing 22 by 7 6 | 7 | ``` 8 | (/ 22 7) 9 | (class (/ 22 7)) 10 | (/ (* 22/7 3) 3) 11 | ``` 12 | 13 | ![](../images/clojure-playground-ratio-pi.png) 14 | 15 | If the result of an integer calculation would be a decimal number, then Clojure holds the value as a Ratio. This is one example of lazy evaluation. Rather than calculate the decimal value at some particular precision (number of decimal points). Clojure is saving the calculation until its needed, at which time the specific precision required should be known. 16 | 17 | > **Note** Explore the ratio type further and see how to get a decimal value as the result 18 | 19 | ```clojure 20 | (/ 14 4) 21 | (/ 16 12) 22 | (/ 2) 23 | (/ 22 7.0) 24 | (type (/ 22 7.0)) 25 | (float (/ 22 7)) 26 | (double (/ 22 7)) 27 | ``` 28 | 29 | ![](../images/clojure-playground-ratio-examples.png) 30 | 31 | When one or more of the numbers in the division is a decimal, then Clojure will return a decimal value. Or you can coerce a value to a specific decimal type, eg. float or double. 32 | -------------------------------------------------------------------------------- /docs/reference/clojure-syntax/syntax.md: -------------------------------------------------------------------------------- 1 | # Clojure syntax 2 | 3 | Clojure is perceived as having an abundance of `()`, the symbols that represent a list. 4 | 5 | As Clojure is a LISP (List Processing) language then everything is written in the form of a list. This makes Clojure very powerful and also easier to read. 6 | 7 | Using a list structure also demonstrates the data-centric nature of Clojure. Every item in the list has a value, with the first item evaluated by a function call. 8 | 9 | > #### Hint::Parens everywhere 10 | > 11 | > The seemingly abundance of `()` can be confusing until its realized there are fewer "special characters" in Clojure than other languages. Clojure aware editors support matching parens, adding a closed paren when typing an open paren, ensuring it is easy to write correctly formed Clojure. 12 | > 13 | > Syntax differences are a trivial reason to avoid trying Clojure. Syntax aware editors significantly reduce typing by automatically closing parenthesis and eliminating errors due to missing delimiters (ie. no more errors due to missing ; in C-based languages) 14 | 15 | ## Prefix notation 16 | 17 | Instead of having a mix of notations like in many other languages, Clojure uses pre-fix notation entirely. 18 | 19 | In Clojure operators are applied uniformly and there is no room for ambiguity: 20 | 21 | ```clojure 22 | (+ 1 2 3 5 8 13 21) 23 | (+ 1 2 (- 4 1) 5 (* 2 4) 13 (/ 42 2)) 24 | (str "Clojure" " uses " "prefix notation") 25 | ``` 26 | 27 | In Java and other C-based languages you have to explicitly add operators everywhere and there can be a mixture of notations 28 | 29 | ```java 30 | (1 + 2 + 3 + 5 + 8 + 13 + 21); 31 | (1 + 2 + (- 4 1) + 5 + (* 2 4) + 13 + (/ 42 2)); 32 | StringBuffer description = new StringBuffer("C-based languages" + " mix " + "notation"); 33 | x+=1; 34 | x++; 35 | x--; 36 | x+=y; 37 | x-=y; 38 | x*=y; 39 | x/=y; 40 | ``` 41 | 42 | # References 43 | 44 | * [Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide) 45 | -------------------------------------------------------------------------------- /docs/reference/control-flow.md: -------------------------------------------------------------------------------- 1 | # Control Flow 2 | -------------------------------------------------------------------------------- /docs/reference/core-async.md: -------------------------------------------------------------------------------- 1 | # Core.async 2 | -------------------------------------------------------------------------------- /docs/reference/creative-coding/index.md: -------------------------------------------------------------------------------- 1 | # Creative coding with Clojure 2 | 3 | Clojure is a very versatile language and can generate data visualizations and graphics from data. 4 | 5 | * [Quil](http://quil.info/){target=_blank} - generate 2D graphics and animations 6 | * [Thi.ng](http://thi.ng/){target=_blank} - computational design tools 7 | * [play.cljc](https://github.com/oakes/play-cljc){target=_blank} - making games (OpenGL and WebGL) 8 | * [Oz](https://github.com/metasoarous/oz){target=_blank} - data visualization and scientific document processing library (see [practicalli/oz-visualisations](https://github.com/practicalli/oz-visualisations){target=_blank} for examples) 9 | 10 | ## Scalable Vector Graphics 11 | 12 | Clojure can generate [Scalable Vector Graphics (SVG)](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) as they are represented as data. SVG images are _drawn_ from a collection of points and paths. SVG images keep their quality when made larger or smaller. Using SVG images for the web and responsive design is highly recommended. 13 | 14 | This example of an SVG image is made from: 15 | 16 | * a green circle and a smaller blue circle 17 | * a white curvy path 18 | 19 | ```clojure 20 | (defn concentric-circles [] 21 | [:svg {:style {:border "1px solid" 22 | :background "white" 23 | :width "150px" 24 | :height "150px"}} 25 | [:circle {:r 50, :cx 75, :cy 75, :fill "green"}] 26 | [:circle {:r 30, :cx 75, :cy 75, :fill "blue"}] 27 | [:path {:stroke-width 12 28 | :stroke "white" 29 | :fill "none" 30 | :d "M 30,40 C 100,40 50,110 120,110"}]]) 31 | ``` 32 | 33 | Add the following path to the above code to make a curvy lambda symbol 34 | 35 | ```clojure 36 | [:path {:stroke-width 12 37 | :stroke "white" 38 | :fill "none" 39 | :d "M 75,75 C 50,90 50,110 35,110"}] 40 | ``` 41 | 42 | The complete solution: 43 | 44 | ```clojure 45 | (defn concentric-circles [] 46 | [:svg {:style {:border "1px solid" 47 | :background "white" 48 | :width "150px" 49 | :height "150px"}} 50 | [:circle {:r 50, :cx 75, :cy 75, :fill "green"}] 51 | [:circle {:r 30, :cx 75, :cy 75, :fill "blue"}] 52 | [:path {:stroke-width 12 53 | :stroke "white" 54 | :fill "none" 55 | :d "M 30,40 C 100,40 50,110 120,110"}] 56 | [:path {:stroke-width 12 57 | :stroke "white" 58 | :fill "none" 59 | :d "M 75,75 C 50,90 50,110 35,110"}]]) 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/reference/doc-and-source-functions.md: -------------------------------------------------------------------------------- 1 | ## The doc & source functions 2 | 3 | If you are not using a Clojure aware editor or spend a lot of time in the REPL you can also view the documentation of a function by calling the `doc` function and see the source by calling the `source` function. 4 | 5 | To use the `doc` & `source` functions in the REPL you should be in the `user` namespace. 6 | 7 | > **Note** On the command line, start a REPL with the command `lein repl` and then view the documentation for three common functions used in clojure 8 | 9 | Make sure you are in the `user` namespace before calling the `doc` function. If you are in another namespace, either change back using `(ns 'user)` or see the next section on using these functions in another namespace. 10 | 11 | ```clojure 12 | (doc doc) 13 | (doc map) 14 | (doc filter) 15 | (doc cons) 16 | 17 | (source doc) 18 | (source map) 19 | ``` 20 | 21 | Here is the doc string for `doc` 22 | 23 | ![Documentation for the function doc](../images/clojure-playground-doc-doc.png) 24 | 25 | Here is the source code for the `source` function 26 | 27 | ![Source code for the function source](../images/clojure-playground-source-source.png) 28 | 29 | > **Hint** As the documentation for a function is part of its definition, by looking at the source of a function you also get the documentation. 30 | 31 | ## Using doc & source function from another namespace 32 | 33 | The `doc` and `source` functions are only included in the `user` namespace. If you switch to another namespace or your editor places you in the current namespace of your project, these functions will not be available unless you including `core.repl` in the current namespace. 34 | 35 | From the REPL, evaluate the expression: 36 | 37 | ``` 38 | (use 'clojure.repl) 39 | ``` 40 | 41 | You could also `require` the `clojure.repl` library in your own code, however if you have a good editor it should provide these features without including this library. Therefore the following code is shown only as an example and not a recommended approach. 42 | 43 | ``` 44 | (ns foobar 45 | (:require [clojure.repl :refer :all])) 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/reference/index.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | Useful information about Clojure presented as a reference, rather than guides as the rest of the book presents. 4 | 5 | 6 | ## Talk Transcripts 7 | 8 | [:globe_with_meridians: Talk transcripts](https://github.com/matthiasn/talk-transcripts) repository contains slides and text from many different speakers and their presentations, including Rich Hickey, Stuart Halloway, David Nolen and Alex Miller. 9 | -------------------------------------------------------------------------------- /docs/reference/jvm/experimental-options.md: -------------------------------------------------------------------------------- 1 | # Reference: JVM Experimental Options 2 | 3 | 4 | 5 | The HotSpot JVM provides the opportunity to try features that may appear in future release, although are currently not production-ready. 6 | 7 | HotSpot JVM experimental features need to be unlocked by specifying the `-XX:+UnlockExperimentalVMOptions` option. 8 | 9 | For example, the ZGC garbage collector in JDK 11 can be accessed using 10 | 11 | ```shell 12 | java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC 13 | ``` 14 | 15 | > The ZGC collector became a product option in JDK 15, so is no longer experimental. 16 | 17 | ## Manageable 18 | 19 | 20 | 21 | Show locks held by `java.util.concurrent` classes in a HotSpot JVM thread dump: 22 | 23 | ```shell 24 | java -XX:+UnlockExperimentalVMOptions -XX:+PrintConcurrentLocks 25 | ``` 26 | 27 | > These options can be set at runtime via the MXBean API or related JDK tools 28 | 29 | ## Diagnostic 30 | 31 | Accessing advanced diagnostic information about the HotSpot JVM. 32 | 33 | These options require you to use the `-XX:+UnlockDiagnosticVMOptions` option before they can be used. 34 | 35 | View advance compilation optimisations using the `-XX:+LogCompilation` option: 36 | 37 | ```shell 38 | java -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation 39 | ``` 40 | 41 | The HotSpot JVM outputs a log file containing details of all the optimisations made by the JIT compilers. Inspect the output to understand which parts of your program were optimized and to identify parts of the program that might not have been optimized as expected. 42 | 43 | The LogCompilation output is verbose but can be visualized in a tool such as JITWatch, which can tell you about method inlining, escape analysis, lock elision, and other optimizations that the HotSpot JVM made to your running code. 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/reference/kebab-case.md: -------------------------------------------------------------------------------- 1 | # Clojure names use kebab-case 2 | 3 | ![Clojure kebab-case tofu kebabs](/images/tofu-kebabs.png) 4 | 5 | kebab-case is a clean, human-readable way to combine the words that would otherwise have spaces. 6 | 7 | Cloure uses kebab-case to combines words with a dash, `-`, rather than a space. e.g. `rock-paper-scissors`, `tic-tac-toe` or `(def db-spec-development {:db-type "h2" :db-name "banking-on-clojure"})` 8 | 9 | kebab-case is used throughout Clojure, including 10 | 11 | * Var names with `def` and function names with `defn` 12 | * Local names with `let` 13 | * Clojure spec names 14 | 15 | kebab-case is used in lisp languages including Clojure. The style is also used in website URLs, e.g. [practicalli.github.io/clojure-webapps](https://practical.li/clojure-web-services/) 16 | 17 | ## Using meaningful names 18 | 19 | To provide greater clarity to human developers, words may be combined for the names used when writing the code. Using multiple words can give greater context in to the purpose of that code. 20 | 21 | Using a combination of meaningful names makes understanding and debugging code far easier. 22 | 23 | ## Spaces characters have special meaning 24 | 25 | Programming languages remove spaces between words because the space character is used as a separator when parsing the code. 26 | 27 | If spaces were not used as a separator for the some other character would be required, adding complexity to the language syntax. 28 | 29 | ## Other Styles 30 | 31 | * camelCase - used in Java and C-style programming languages 32 | * PascalCase - used in the [Pascal programming language][1] 33 | * snake_case - used for `ENVIRONMENT_VARIABLES` and `database_table_names_and_columns` 34 | 35 | [1]: https://en.wikipedia.org/wiki/Pascal_(programming_language) 36 | -------------------------------------------------------------------------------- /docs/reference/naming-conventions.md: -------------------------------------------------------------------------------- 1 | # Naming Conventions 2 | 3 | ## Kebab-case 4 | 5 | Kebab-case is the naming convention for all Clojure function names than contain more than one word. Its name comes from the Shish Kebab style of cooking, where the words are the tofu and vegetables and the dashes are the skewers. 6 | 7 | ``` 8 | clj-time 9 | string-parser 10 | display-name 11 | ``` 12 | 13 | ## Predicates 14 | 15 | Examples of predicate naming conventions from `clojure.core` 16 | 17 | ``` 18 | contains? 19 | empty? 20 | every? 21 | not-empty? 22 | null? 23 | ``` 24 | 25 | ## Namespace requires and aliases 26 | 27 | Required libraries should be given a contextually meaningful name as an alias, helping to identify the purpose of functions defined outside of the namespace. 28 | 29 | Giving meaningful context helps code to be understood by any person reading the code. It is also easier to search for usage of functions from that context in the current project. 30 | 31 | Aliases are rarely typed more than once in full as Clojure editors have auto-complete, so there is no benefit to short of single character aliases. 32 | 33 | ```clojure 34 | (ns status-monitor.handler 35 | (:require [hiccup.page :refer :as web-page] 36 | [hiccup.form :refer :as web-form])) 37 | ``` 38 | 39 | In very commonly used libraries or very highly used functions through out the code, refer those functions explicitly 40 | 41 | ```clojure 42 | (ns naming.is.hard 43 | (:require [compojure.core :refer [defroutes GET POST]] 44 | [ring.middleware.defaults :refer [wrap-defaults site-defaults]])) 45 | ``` 46 | 47 | ## Converting functions 48 | 49 | When a function takes values in one format or type and converts them to another 50 | 51 | Examples 52 | 53 | ``` 54 | md->html 55 | 56 | map->Record-name ; map factory function of a record -- creates a new record from a map 57 | ->Record-name ; positional factory function of a record -- creates a new record from a list of values 58 | ``` 59 | -------------------------------------------------------------------------------- /docs/reference/naming.md: -------------------------------------------------------------------------------- 1 | # Naming 2 | -------------------------------------------------------------------------------- /docs/reference/prasmatic-schema.md: -------------------------------------------------------------------------------- 1 | # Prasmatic Schema 2 | -------------------------------------------------------------------------------- /docs/reference/reader-macros.md: -------------------------------------------------------------------------------- 1 | # Reader Macros 2 | 3 | > #### Todo::Re-write 4 | 5 | This is a collection of reader macros (think syntactic sugar) that are valid in Clojure. These macros are useful for commenting out expressions, defining sets, ... 6 | 7 | Many reader macros start with the character **#**, which is in fact the _Dispatch macro_ that tells the Clojure reader (the thing that takes a file of Clojure text and parses it for consumption in the compiler) to go and look at another read table for the definition of the next character - in essence this allows extending default reader behaviour. 8 | 9 | * **#_** - Discard macro - ignore the next expression. Often used to comment out code, especially when nested inside other expressions 10 | 11 | * **#'** - Var macro - returns the reference to the var. Used to pass the definition of something rather than the result of evaluating it. 12 | 13 | There is a nice list of reader macros in the article: [The weird and wonderful characters of Clojure](https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful-characters-of-clojure/) by [@kouphax](http://twitter.com/kouphax). 14 | 15 | > **Hint** Reader macros are part of the Clojure language specification, so are different to macros, which can be defined by anyone. 16 | -------------------------------------------------------------------------------- /docs/reference/standard-library/collections.md: -------------------------------------------------------------------------------- 1 | # Standard Library: Collections 2 | 3 | Functions to create and work with the Clojure collection types, mainly maps, vectors and sets 4 | 5 | See [Sequences](sequences.html) for functions around lists and (lazy) sequences 6 | -------------------------------------------------------------------------------- /docs/reference/standard-library/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Standard Library 2 | 3 | Examples of using the functions from the `clojure.core` namespace and other important functions, macros and special forms that are part of the `org.clojure/clojure` library. 4 | 5 | There are approximately 700 functions and macros available in the `clojure.core` namespace. These are referred to as the Clojure Standard Library. 6 | 7 | !!! HINT "Counting functions in `clojure.core`" 8 | To get an accurate number of functions, call the `ns-publics` function with a namespace name 9 | ```clojure 10 | (count (ns-publics 'clojure.core)) 11 | ``` 12 | [Random Function](/clojure/simple-projects/random-clojure-function/) is a simple project that prints out a random function from the given namespace, or from clojure.core by default. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ## Functions, Macros and Special forms 21 | 22 | The majority of times macros and special forms act just like any other defined function (i.e. `fn`, `defn`) 23 | 24 | A macro is a piece of code that evaluates into a function when read by the macro reader, or by the developer using `macroexpand` function. An expanded macro may also contain macros, so expansion could take place several levels (`macroexpand-all`). 25 | 26 | macros are not composable like functions, so functions like `apply` `reduce` `map` cannot use a macro (use a function instead). 27 | 28 | Special forms are built into the Clojure runtime, so will not be found in clojure.core 29 | 30 | - Special forms: `if` `do` `let` `quote` `var` `fn` `loop` `recur` `throw` `try` 31 | - Special forms for Java interop: `.` `new` `set!` 32 | -------------------------------------------------------------------------------- /docs/reference/standard-library/regular-expressions/matching-sub-sequences.md: -------------------------------------------------------------------------------- 1 | ## Matching sub-sequences 2 | 3 | `re-seq` returns a lazy seq of all of the matches. The elements of the seq are the results that `re-find` would return. 4 | 5 | ```clojure 6 | (re-seq #"s+" "Helloween") 7 | ``` 8 | 9 | 10 | ## Most common word 11 | 12 | `re-seq` is used in the most common word challenge to split a string into individual words. 13 | 14 | Extract from Project Guttenburg the text of The importance of being Earnest by Oscar Wilde. This returns a string of the whole book. 15 | 16 | The book is broken down into a collection of individual words using `re-seq` and a regular expression pattern. 17 | 18 | The collection of words is converted to lower case, so that `The` and `the` are not counted as separate words. `frequencies` returns a collection of tuples, each tuple being a word and a value representing how often it occurs. This collection is sorted by the value in descending order to show the word with the most occurrences at the top. 19 | 20 | ```clojure 21 | (->> (slurp "http://www.gutenberg.org/cache/epub/844/pg844.txt") 22 | (re-seq #"[a-zA-Z0-9|']+") 23 | (map #(clojure.string/lower-case %)) 24 | frequencies 25 | (sort-by val dec)) 26 | ``` 27 | 28 | TODO: add link to complete example. 29 | -------------------------------------------------------------------------------- /docs/reference/standard-library/regular-expressions/matching-sub-strings.md: -------------------------------------------------------------------------------- 1 | ## Matching sub-strings 2 | 3 | `re-find` returns the first match within the string, using return values similar to re-matches. 4 | 5 | `nil` is returned when the pattern does not find a match. 6 | 7 | ```clojure 8 | (re-find #"pump" "Halloween") 9 | ``` 10 | 11 | 12 | A matching pattern without groups returns the matched string 13 | 14 | ```clojure 15 | (re-find #"e+" "Halloween") 16 | ``` 17 | 18 | 19 | Match with groups returns a vector of results 20 | 21 | ```clojure 22 | (re-find #"s+(.*)(s+)" "success") 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /docs/reference/standard-library/regular-expressions/matching-with-groups.md: -------------------------------------------------------------------------------- 1 | # Matching with regex groups 2 | 3 | `rematches` takes a pattern and compares it with a string. 4 | 5 | If the pattern does not match the string then `nil` is returned to show the function returned a false value. 6 | 7 | ```clojure 8 | (re-matches #"pumpkin" "Halloween pumpkin") 9 | ``` 10 | 11 | If there is an exact match and there are no groups (parens) in the regex, then the matched string is returned. 12 | 13 | ```clojure 14 | (re-matches #"pumpkin" "pumpkin") 15 | ``` 16 | 17 | If the pattern matches but there are groups, a vector of matching strings is returned. The first element in the vector is the entire match. The remaining elements are the group matches. 18 | 19 | ```clojure 20 | (re-matches #"Halloween(.*)" "Halloween pumpkin") 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/reference/standard-library/regular-expressions/string-replace-with-regex.md: -------------------------------------------------------------------------------- 1 | ## String replace with regex pattern 2 | 3 | `clojure.string/replace` takes a string, a pattern and a substring that will replace matching patterns. 4 | 5 | ```clojure 6 | (clojure.string/replace "mississippi" #"i.." "obb") 7 | ``` 8 | 9 | 10 | Groups can be referred to in the substring replacement 11 | 12 | ```clojure 13 | (clojure.string/replace "mississippi" #"(i)" "$1$1") 14 | ``` 15 | 16 | 17 | Replace with the value of a function applied to the match: 18 | 19 | ```clojure 20 | (clojure.string/replace "mississippi" #"(.)i(.)" 21 | (fn [[_ b a]] 22 | (str (clojure.string/upper-case b) 23 | "--" 24 | (clojure.string/upper-case a)))) 25 | "M--SS--SS--Ppi" 26 | ``` 27 | 28 | `clojure.string/replace-first` is a variation where just the first occurrence is replaced. 29 | -------------------------------------------------------------------------------- /docs/reference/standard-library/regular-expressions/string-split-with-regex.md: -------------------------------------------------------------------------------- 1 | ## String splitting using a regex pattern 2 | 3 | `clojure.string/split` takes a string to be split and a pattern to split the string with. 4 | 5 | ```clojure 6 | (clojure.string/split "This is a string that I am splitting." #"\s+") 7 | ["This" "is" "a" "string" "that" "I" "am" "splitting."] 8 | ``` 9 | 10 | ## Most common words example 11 | 12 | Extract a list of the most commonly used English words, returned as a string of words that are separated by a comma. 13 | 14 | The `#","` regex pattern splits the string of words to form a collection of individual words, each word being its own string. 15 | 16 | ```clojure 17 | (def common-english-words 18 | (set 19 | (clojure.string/split 20 | (slurp 21 | "http://www.textfixer.com/resources/common-english-words.txt") 22 | #","))) 23 | ``` 24 | 25 | TODO: add link to complete example. 26 | -------------------------------------------------------------------------------- /docs/reference/standard-library/sequences.md: -------------------------------------------------------------------------------- 1 | # Standard Library: Sequences 2 | 3 | Functions to create and work with the Clojure sequences, including lists and sequence generators 4 | 5 | ## Sequence access 6 | 7 | | Function | Description | 8 | |-----------|-------------| 9 | | `first` | | 10 | | `second` | | 11 | | `rest` | | 12 | | `last` | | 13 | | `butlast` | | 14 | | `nth` | | 15 | 16 | ## Infinite sequence generators 17 | 18 | | Function | Description | 19 | |-----------|-------------| 20 | | `range` | | 21 | | `cycle` | | 22 | | `iterate` | | 23 | -------------------------------------------------------------------------------- /docs/reference/tagged-literals/index.md: -------------------------------------------------------------------------------- 1 | # Tagged Literals 2 | 3 | Frequently used value types are afforded a "tagged literal" syntax. It is similar to a constructor, but this special syntax makes it de/serializable and easier to read at the REPL. 4 | 5 | Tagged literals start with a # followed by a symbol and a literal: 6 | 7 | `#js [...]`- [JavaScript array literal](https://github.com/cljs/api/blob/master/docfiles/syntax/js-literal.md) 8 | 9 | `#js {...}` - [JavaScript object literal](https://github.com/cljs/api/blob/master/docfiles/syntax/js-literal.md) 10 | 11 | `#inst "..."` - [JavaScript date literal](https://github.com/cljs/api/blob/master/docfiles/syntax/inst-literal.md) 12 | 13 | `#uuid "..."` - [UUID literal](uuid.md) 14 | 15 | `#queue [...]` - [queue literal](https://github.com/cljs/api/blob/master/docfiles/syntax/queue-literal.md) 16 | -------------------------------------------------------------------------------- /docs/reference/tagged-literals/uuid.md: -------------------------------------------------------------------------------- 1 | # uuid tag literal 2 | 3 | A universally unique identifier (UUID). 4 | 5 | #uuid "8-4-4-4-12" - numbers represent the number of hex digits 6 | #uuid "97bda55b-6175-4c39-9e04-7c0205c709dc" - actual example 7 | 8 | Representing UUIDs with #uuid rather than just a plain string has the following benefits: 9 | 10 | the reader will throw an exception on malformed UUIDs 11 | its UUID type is preserved and shown when serialized to edn. 12 | 13 | ## Creating UUIDs - Clojure 14 | 15 | In Clojure, call the randomUUID method of the java.util.UUID class 16 | 17 | ``` 18 | (java.util.UUID/randomUUID) 19 | ``` 20 | 21 | This returns a UUID tagged literal. 22 | 23 | ```clojure 24 | (java.util.UUID/randomUUID) 25 | ;; => #uuid "44f3ffd7-6702-4b8a-af25-11bee4b5ec4f" 26 | ``` 27 | 28 | Looking at the type we can see its a Java object from the java.util.UUID class: 29 | 30 | ```clojure 31 | (type (java.util.UUID/randomUUID)) 32 | ;; => java.util.UUID 33 | ``` 34 | 35 | ## Creating UUIDs - ClojureScript 36 | 37 | Randomly generate a UUID in ClojureScript: 38 | 39 | `cljs.core/random-uuid` 40 | 41 | To label a value as a UUID: 42 | 43 | `cljs.core/uuid` 44 | 45 | > #### Hint::uuid does not validate the value 46 | > 47 | > The [ClojureScript documentation](https://github.com/cljs/api/blob/master/docfiles/cljs.core/uuid.md) states that uuid? does not perform validation. 48 | 49 | ## Testing for a uuid 50 | 51 | `uuid?` tests a given value and returns true if it is a uuid tagged literal value. 52 | 53 | `tagged-literal?` is the more general function for any tagged values. 54 | -------------------------------------------------------------------------------- /docs/reference/threading-macros.md: -------------------------------------------------------------------------------- 1 | # Reference: Threading macros 2 | 3 | Using the threading macro, the result of every function is passed onto the next function in the list. This can be seen very clearly using ,,, to denote where the value is passed to the next function 4 | 5 | ```clojure 6 | (-> 7 | "project.clj" 8 | slurp ,,, 9 | read-string ,,, 10 | (nth ,,, 2)) 11 | ``` 12 | 13 | > #### Hint::Commas in clojure are whitespace 14 | > 15 | > Commas are simply ignored when the Clojure Reader parses code. Commas are rarely used and only to help human readability of the code 16 | 17 | To make this really simple lets create a contrived example of the threading macro. Here we use the `str` function to join strings together. Each individual `str` function joins its own strings together, passing the resulting string as the first argument to the next function. 18 | 19 | ```clojure 20 | (-> 21 | (str "This" " " "is" " ") 22 | (str "the" " " "threading" " " "macro") 23 | (str "in" " " "action.")) 24 | ``` 25 | 26 | Output 27 | 28 | ``` 29 | ;; => "This is the threading macro in action" 30 | ``` 31 | 32 | ## Thread-last macro 33 | 34 | Using the thread-last macro, **->>**, the result of a function is passed as the last argument of the next function call. So in another simple series of str function calls, our text comes out backwards. 35 | 36 | ```clojure 37 | (->> " this" 38 | (str " is") 39 | (str " backwards")) 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/simple-projects/data-transformation/common-english-words.csv: -------------------------------------------------------------------------------- 1 | a,able,about,across,after,all,almost,also,am,among,an,and,any,are,as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your 2 | -------------------------------------------------------------------------------- /docs/simple-projects/data-transformation/index.md: -------------------------------------------------------------------------------- 1 | # Data Transformation 2 | 3 | In a sense all Clojure project are about data transformation, however, these projects will introduce you to many techniques used to transform larger and larger data sets. 4 | 5 | ![data transformation - concept](/images/data-transformation.png) 6 | 7 | | Project | Topics | Overview | 8 | |-----------------------------------------|-----------------------------|----------------------------------------------------------------------------| 9 | | [Most common word](most-common-word.md) | regex filter re-seq sort-by | Find the most common word in a give book that is not a common English word | 10 | -------------------------------------------------------------------------------- /docs/simple-projects/encode-decode/index.md: -------------------------------------------------------------------------------- 1 | # Encoding and Decoding with Clojure 2 | 3 | ![Matrix decode](/images/matrix-decode.png) 4 | 5 | Projects that use a range of ciphers, from simple to more complex, to encode and decode text. 6 | 7 | A common approach to encoding and decoding text is to use a dictionary lookup, defined in Clojure as a hash-map. Each key-value pair provides a mapping for encoding and decoding. Looking up a a character as a key in the map provides a value that is the encrypted character. 8 | 9 | These projects show several ways to transform data in Clojure. 10 | 11 | | Project | Topics | Description | 12 | |------------------------------------------------------|------------------|-------------------------------------------------| 13 | | [Boolean names to 0 or 1](convert-boolean-values.md) | hash-map get | Convert boolean values to classic 1 or 0 values | 14 | | [Caesar cipher - ROT13](caesar-cipher rot13.md) | seq cycle zipmap | A simple alphabet rotation cipher | 15 | | [RNA / DNA converter](rna-dna.md) | | Convert between DNA and RNA | 16 | | [Clacks telegram](clacks.md) | | Encoding and decoding messages with Clacks | 17 | 18 | ## Examples of Encoding 19 | 20 | * [Portable Network Graphics for image compression](https://en.wikipedia.org/wiki/Portable_Network_Graphics) 21 | * [Vorbis for music and video compression](https://en.wikipedia.org/wiki/Vorbis) plus several commercial compression encoders 22 | * [Enigma machine - encrypted communications](https://www.google.com/search?q=clojure+enigma+machine) 23 | -------------------------------------------------------------------------------- /docs/simple-projects/generate-web-page.md: -------------------------------------------------------------------------------- 1 | # Generate Web Page 2 | 3 | Generate a web page from Clojure code, using Hiccup 4 | 5 | Generate a full HTML webpage with content. 6 | 7 | Add a CSS library (bulma.io, bootstrap) to improve generation 8 | 9 | 10 | 11 | ## Summary 12 | 13 | Generating a web page in Clojure shows how easy it is to structure data and transform that data into other structures. 14 | 15 | Although this kind of project is easy enough to just do in a REPL directly, using a Clojure aware editor with a Clojure project makes changes to the code far simpler, without loosing any of the immediate feedback of the REPL. 16 | 17 | Most Clojure developers use the REPL by evaluating code in the editor showing the source code from the project. 18 | 19 | [:fontawesome-solid-book-open: Practicalli Web Services book](https://practical.li/clojure-web-services/) shows how to build websites, create self-documented API's, manage Web Application servers and use databases to persist data. 20 | -------------------------------------------------------------------------------- /docs/simple-projects/index.md: -------------------------------------------------------------------------------- 1 | # Small Projects 2 | 3 | ![Projects](/images/projects.png) 4 | 5 | An effective way to get comfortable with Clojure is to start writing small projects. In this section several small projects are used to walk the audience through how to create and develop a project, as well as learn some Clojure functions and functional programming techniques along the way. 6 | 7 | | Project | Topics | Description | 8 | |-------------------------------------------------------|-----------------------|----------------------------------------------------------------| 9 | | [Random Clojure Function](random-clojure-function.md) | namespace vars | print a random function from the Clojure standard library | 10 | | [Encoding and decoding](encode-decode/) | hash-map dictionaries | transforming messages between one form and another | 11 | | [Data Transformation](data-transformation/) | | transforming larger and larger data sets | 12 | | [Test Driven Development and Kata](tdd-kata/) | Unit testing | Unit testing and solving challenges using different approaches | 13 | 14 | !!! HINT "Create a Clojure project" 15 | [Clojure CLI tools and clj-new](/clojure/clojure-cli/projects/create-from-template/) to create a new Clojure project. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /docs/simple-projects/mutating-state/index.md: -------------------------------------------------------------------------------- 1 | # Mutating State in a Controlled way 2 | 3 | Mutating state should be used carefully and sparingly in Clojure (and all other programming languages). 4 | 5 | `atom` is a mutable container that can manage any value. The atom ensures that only one call at a time can affect the value it manages. This is part of the [software transactions memory system](https://clojure.org/reference/refs) in Clojure. 6 | 7 | As the atom is mutable in that the value it manages can be changed, however, this must be done with special commands (swap!, reset!, compare-and-set!, swap-vals!). 8 | 9 | Even though the atom is mutable, the values it manages are not. They are normal immutable (unchangeable) Clojure values. 10 | 11 | `ref` is similar to `atom` and can manage transactions, ensuring that all changes happen or no changes happen. 12 | 13 | | Project | Topics | Overview | 14 | |------------------|-----------------------|-----------------------------------------------------------------------------------------------| 15 | | Mutants assemble | atom swap! reset! | Using an atom to manage state changes | 16 | | Undo/Redo | atom add-watch | Traversing the history of an atom | 17 | | Poker game | atom swap! reset! ref | Simple transaction management using atom and ref in a card game, using constraints on an atom | 18 | 19 | ## References 20 | 21 | * [Atoms](https://clojure.org/reference/atoms) - clojure.org 22 | * [Refs and Transactions](https://clojure.org/reference/refs) - clojure.org 23 | * [Agents](https://clojure.org/reference/agents) - clojure.org 24 | -------------------------------------------------------------------------------- /docs/simple-projects/split-the-bill.md: -------------------------------------------------------------------------------- 1 | # Split the bill 2 | 3 | 4 | 5 | In a restaurant a group of friends and relatives are having a reunion dinner after a year of not seeing each other. 6 | 7 | Once the meal comes to an end, its time to pay the bill. So how would you write code to split the bill? 8 | 9 | Start with the simplest possible approach, with everyone paying the same. 10 | 11 | ## Create a new Clojure project 12 | 13 | [:fontawesome-solid-book-open: Pracitcalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) provides the `:project/create` alias to create projects using deps-new project. 14 | 15 | ```shell 16 | clojure -T:project/create :template app :name practicalli/split-the-bill 17 | ``` 18 | 19 | ```clojure 20 | (str "Create code to calculate the bill, including what each person should pay") 21 | ``` 22 | 23 | Tke a look at the [Who am I](/community-docs/docs/curriculum/who-am-i) section for ideas on how to model the bill. Also look at [More Than Average](/community-docs/docs/curriculum/more-than-average) for ideas on how to write code to work out how to pay the bill. 24 | 25 | ### Paying what was ordered 26 | 27 | As not everyone had eaten the same amount of food or arrived at the same time, then there was an ask for everyone to pay just what they ordered. 28 | 29 | So create a collection to capture what each person ordered and create an itemised bill so each person knows what they should pay. 30 | 31 | Define a detailed bill based on what each person ordered, then create an itemised bill based on each persons order 32 | 33 | Now it was realised that what everyone ordered is not what everyone ate. So now we need to take the order and create an itemised bill based on what everyone actually ate (lets suspend believe here a little and assume everyone knows exactly what they ate, and is honest about it). 34 | 35 | Define a detailed bill based on what each person ordered, then create an itemised bill based on each person actually ate 36 | 37 | ## Spliting the bill with a Social Group 38 | 39 | Extend the exercise by splitting bills over multiple events and activities with multiple people. 40 | -------------------------------------------------------------------------------- /docs/simple-projects/tdd-kata/index.md: -------------------------------------------------------------------------------- 1 | # Kata challenges 2 | 3 | A kata is a small challenge that you attempt to solve in different ways, so experiment with your solutions to these challenges. 4 | 5 | Kata are often coupled with Test Driven Development approach. 6 | 7 | | Project | Topics | Overview | 8 | |---------------------------------------------------|--------|--------------------------------------------------------| 9 | | [Recent song-list](recent-song-list.md) | TDD | Keep a list of recent songs played, without duplicates | 10 | | [Salary Slip Generator](salary-slip-generator.md) | TDD | Generate play slips for an employee | 11 | 12 | [Code Kata Website](http://codekata.com/){target=_blank .md-button} 13 | 14 | ![Kata TDD - lego](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/tdd-kata-lego.png) 15 | -------------------------------------------------------------------------------- /docs/testing/integration-testing/index.md: -------------------------------------------------------------------------------- 1 | # Integration Testing 2 | 3 | See the [continuous integration section](/continuous-integration/) 4 | -------------------------------------------------------------------------------- /docs/testing/test-runners/aero.md: -------------------------------------------------------------------------------- 1 | # Aero 2 | 3 | [juxt/aero](https://github.com/juxt/aero) is used to read the kaocha configuration, so reader literals such as #env, #merge, #ref, and #include can be used. 4 | 5 | Set up [profiles for different stages of the development workflow](https://juxt.pro/blog/aero.html), dev, test, prod, etc. Each profile has a different configuration making it very easy to switch 6 | 7 | ```clojure 8 | {:port 8000 9 | :database #profile {:prod "datomic:dev://localhost:4334/my-prod-db2" 10 | :test "datomic:dev://localhost:4334/my-test-db" 11 | :default "datomic:dev://localhost:4334/my-db"} 12 | :known-users [{:name "Alice"} {:name "Betty"}]} 13 | ``` 14 | 15 | Then in application startup function or a component lifecycle library (mount, component, integrant) read in a specific profile 16 | 17 | ```clojure 18 | (aero.core/read-config "config.edn" {:profile :prod}) 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/testing/test-runners/example-projects.md: -------------------------------------------------------------------------------- 1 | # Example projects 2 | 3 | * [TDD Kata: Recent Song-list](/simple-projects/tdd-kata/recent-song-list.md) - simple tests examples 4 | * [Codewars: Rock Paper Scissors (lizard spock) solution](https://github.com/practicalli/codewars-guides/tree/develop/rock-paper-scissors) - `and` examples 5 | * [practicalli/numbers-to-words](https://github.com/practicalli/numbers-to-words) - overly verbose example, ripe for refactor 6 | * [practicalli/codewars-guides](https://github.com/practicalli/codewars-guides) - deps.edn projects 7 | * [practicalli/exercism-clojure-guides](https://github.com/practicalli/exercism-clojure-guides) - Leiningen projects 8 | 9 | ## Sean Corfield - user manager 10 | 11 | [User manager](https://github.com/seancorfield/usermanager-example) has unit tests that also include an embedded database. Tests can run with the Cognitect Labs test runner. 12 | 13 | `:test` alias includes the test path and a dependency for the H2 database 14 | 15 | Cognitect Labs test runner included in the project `deps.edn` file as `:runner` 16 | 17 | `clojure -M:test:runner` will run the Cognitect Labs runner and include the dependency to run the in-memory database used for the tests. 18 | 19 | ### Using koacha with Sean Corfield user manager 20 | 21 | Adding a `test.edn` file is not sufficient for testing this project with lambdaisland/kaocha, as the H2 dependency is also needed. 22 | 23 | Create a `bin/koacha` script and add the extra alias 24 | 25 | ```shell 26 | #!/usr/bin/env bash 27 | clojure -M:test:test-runner-kaocha "$@" 28 | ``` 29 | 30 | 31 | 32 | 33 | ## Status Monitor 34 | 35 | [Status monitor](https://github.com/jr0cket/webapp-status-monitor) is a Leiningen project. 36 | 37 | Include a `:kaocha` profile in the `project.clj` file, adding the koacha dependency. The `:kaocha` alias sets the main namespace and uses the kaocha profile. 38 | 39 | ```clojure 40 | {:dev {:dependencies [[javax.servlet/servlet-api "2.5"] 41 | [ring/ring-mock "0.3.2"]]} 42 | :kaocha {:dependencies [[lambdaisland/kaocha "1.0.632"]]}} 43 | :aliases {"kaocha" ["with-profile" "+kaocha" "run" "-m" "kaocha.runner"]} 44 | ``` 45 | 46 | `lein kaocha` will run all the tests 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/testing/unit-testing/clojure-test-expectations.md: -------------------------------------------------------------------------------- 1 | # Clojure test Expectations 2 | 3 | 4 | 5 | `clojure.test.expectations` uses the same tooling as `clojure.test` and only depends on that library. 6 | 7 | ## Using a deps.edn alias 8 | 9 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) 10 | 11 | ## Add dependency 12 | 13 | Edit the deps.edn file for the current project 14 | -------------------------------------------------------------------------------- /docs/thinking-functionally/arity.md: -------------------------------------------------------------------------------- 1 | # Arity 2 | 3 | > **Fixme** work in progress 4 | -------------------------------------------------------------------------------- /docs/thinking-functionally/first-class-functions.md: -------------------------------------------------------------------------------- 1 | # First Class functions 2 | 3 | Idempotent - given the same input you get the same output 4 | 5 | > ####Note:: 6 | > Write an expression to add up the numbers from 1 to 10 and return the overall total. 7 | 8 | ```clojure 9 | (+ 1 2 3 4 5 6 7 8 9 10) 10 | ``` 11 | 12 | > ####Note:: 13 | > Create an expression to do the same calculation, but without having to write all the numbers. Hint: consider the functions called range and reduce. 14 | 15 | The `range` function generates a sequence of numbers and when given arguments it does so from a specific range. The second number is exclusive, so for 1 to 10 the second argument should be 11. 16 | 17 | ```clojure 18 | (range 1 11) 19 | ``` 20 | 21 | Unfortunately we cant just add the result of a range, because it returns a [lazy sequence](lazy-evaluation.html) So `(range)` by itself will create an error 22 | 23 | ```clojure 24 | (+ 1 (range 1 11)) 25 | ``` 26 | 27 | Using a function called `reduce` we can calculate a single total value from all the numbers in the collection. 28 | 29 | The reduce function take 2 arguments, the first is the function to apply to a data structure, the second is the data structure. 30 | 31 | ```clojure 32 | (reduce + (range 1 11)) 33 | 34 | (reduce + (1 2 3 4 5 6 7 8 9 10)) 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/thinking-functionally/function-composition.md: -------------------------------------------------------------------------------- 1 | # Function Composition 2 | 3 | We have discussed how functional programs are essentially a number of functions that work together, this is called composition (functional composition). 4 | 5 | ``` 6 | (let [calculated-value (* 10 (reduce + (map inc (range 5))))] 7 | calculated-value) 8 | ``` 9 | 10 | This expression is common in the Lisp & Clojure languages. Occasionally the created expressions can becomes challenging to read. To overcome this parsing complexity, developers often break down a more complex expression into its parts, extracting code into its own function. 11 | 12 | > **Note** Brake down the above example into each expression that gives a value 13 | 14 | 15 | 16 | ``` 17 | (range 5) 18 | 19 | (map inc (range 5)) 20 | 21 | (reduce + (map inc (range 5))) 22 | 23 | (* 10 (reduce + (map inc (range 5)))) 24 | 25 | 26 | ;; Additional examples 27 | 28 | ;; Use a let expression for code that is used more than once in a function 29 | 30 | (let [calculated-value (* 10 (reduce + (map inc (range 5))))] 31 | calculated-value) 32 | 33 | ;; Use defn to define a function for code that multiple functions will call 34 | ;; and generalise the function with arguments 35 | 36 | (defn common-data-calculation 37 | [certainty-factor scope] 38 | (* certainty-factor (reduce + (map inc (range scope))))) 39 | ``` 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/thinking-functionally/functors.md: -------------------------------------------------------------------------------- 1 | # Functors 2 | 3 | > **Fixme** work in progress 4 | 5 | Put simply, a function that takes a value and a function as its arguments, eg `map`. The argument pass as a value is most commonly a collection type (vector, map, string, list). 6 | 7 | > From Wikipedia 8 | 9 | > In mathematics, a functor is a type of mapping between categories which is applied in category theory. Functors can be thought of as homomorphisms between categories. In the category of small categories, functors can be thought of more generally as morphisms. 10 | 11 | A functor applies the given function to each element in the the collection by unpacking and each element from the collection and passing it to the function as an argument. The result from each application of the function from the element of the collection is put into a new collection. This new collection is returned once all elements of the original collection have been processed. 12 | 13 | The function, eg. + is applied in turn to each value and returns a structured value as a result, 14 | eg. a list or vector 15 | 16 | ``` 17 | (map inc [1 2 3 4 5]) 18 | 19 | (inc 1 ) 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutability.md: -------------------------------------------------------------------------------- 1 | # Immutability 2 | 3 | There is a strong emphasis on immutability in Clojure. Rather than create variables that change, Clojure uses values that do not change. 4 | 5 | Values in Clojure include numbers, characters, strings. 6 | 7 | When functions act on values, a new value is created and returned, rather than modifying the existing value. 8 | 9 | > **TODO** include a diagram to visualise this... 10 | 11 | # Immutabile data structures 12 | 13 | List, Map, Vector and Set are all immutable data structures in Clojure. 14 | 15 | So when you use these data structures with a function, a new data structure is returned. 16 | 17 | > **Hint** When a new data structure is created from an existing data structure, then under the covers the two data structures actually share memory use for any elements that are common. This keeps copies very cheap to create in terms of memory used. 18 | 19 | > See the section on [data structures](/data-structures/) for more details. 20 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-collections.md: -------------------------------------------------------------------------------- 1 | # Immutable collections 2 | 3 | As we have discussed, immutable data structures cannot be changed. So when you run a function over a collection a copy of that collection is returned. Lets see this by running some code in the REPL. 4 | 5 | > **Note** Define a data structure called `numbers` using a vector. Then write a function that uses the `map` and `inc` function to increment all the numbers in a vector. 6 | 7 | > Then check the current value of the `numbers` data structure by evaluating its name. 8 | 9 | 10 | ```clojure 11 | ;; define the data structure 12 | (defn numbers [1 2 3 4 5]) 13 | 14 | ;; increment the numbers 15 | (map inc numbers) 16 | 17 | ;; see the current value of numbers 18 | numbers 19 | ``` 20 | 21 | 22 | 23 | > **Note** Use the `conj` function to first add the number `5` to the `numbers` vector from the previous exercise and check the value of `numbers`. Then add the number `6` to the `numbers` vector and check the value of `numbers`. 24 | 25 | > Finally, use the `conj` function to add both `5` and `6` to the `numbers` vector and check the value of `numbers` 26 | 27 | 28 | 29 | ``` 30 | (def numbers [1 2 3 4]) 31 | 32 | ;; add 5 to the numbers vector 33 | (conj numbers 5) 34 | 35 | ;; check the value of numbers 36 | numbers 37 | ;; => [1 2 3 4] 38 | 39 | ;; add 6 to the numbers vector 40 | (conj numbers 6) 41 | 42 | ;; check the value of numbers 43 | numbers 44 | ;; => [1 2 3 4] 45 | 46 | ;; add 5 and 6 to the numbers vector 47 | (conj numbers 5 6) 48 | 49 | ;; Alternatively, you can use the threading macro to chain two conj function calls 50 | (-> numbers 51 | (conj 5) 52 | (conj 6)) 53 | 54 | ;; check the value of numbers 55 | numbers 56 | ;; => [1 2 3 4] 57 | ``` 58 | 59 | So even though we have applied several functions on the `numbers` data structure it still has the same value. 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-local-bindings.md: -------------------------------------------------------------------------------- 1 | # Immutable Local Bindings 2 | 3 | Names can be bound to values & and data structures with either the `def` or `let` function. The `def` binding is global to the namespace, however the `let` function is local to its use. 4 | 5 | > **Hint** The `let` function is typically used to define names within a function definition, or in snippets of code created during repl driven development. 6 | 7 | ```clojure 8 | 9 | (let [five 5] 10 | (str "Within the let expression the value is " five)) 11 | ;; => Within the let expression the value is 5 12 | 13 | ;; evaluating the name five outside the let expression returns an error 14 | five 15 | ;; => Unable to resolve symbol: five in this context 16 | ``` 17 | 18 | > **Note** Create a local binding called number that represents the value 5 using the `let` function. Increment the number, then print out the value of number. 19 | 20 | 21 | ```clojure 22 | (let [number 5] 23 | (inc number) 24 | (str "The number is still " number)) 25 | ``` 26 | 27 | So the value that any local binding points to is immutable too. 28 | 29 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-values.md: -------------------------------------------------------------------------------- 1 | # Immutable values 2 | 3 | > **Fixme** work in progress 4 | 5 | Values in Clojure include numbers, characters and strings. When you use functions on these values they do not change, instead a new value is returned. 6 | 7 | Lets look at a simple example with a number: 8 | 9 | ```clojure 10 | (def two-little-ducks 22) 11 | 12 | (inc two-little-ducks) 13 | ;; => 23 14 | 15 | two-little-ducks 16 | ;; => 22 17 | ``` 18 | 19 | Another example with a string: 20 | 21 | ```clojure 22 | (def message "Strings are immutable") 23 | 24 | (str message "," " " "you cant change them") 25 | ;; => "Strings are immutable, you cant change them" 26 | 27 | message 28 | ;; => "Strings are immutable" 29 | ``` 30 | 31 | > **Fixme** Add an exercise 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/thinking-functionally/index.md: -------------------------------------------------------------------------------- 1 | # Thinking Functionally 2 | 3 | In this section I cover some simple examples of Clojure code to help you think about the concepts involved in functional programming. 4 | 5 | An overview of thinking functionally is also covered in the presentation entitled [Getting into Functional Programming with Clojure](http://www.slideshare.net/jr0cket/cebit-get-into-functional-programming-with-clojure) on slideshare and its accompanying [youtube video](https://www.youtube.com/watch?v=mEfqULqChZs) 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | [![Join the conversation on Clojurians Slack](/images/practicalli-slack-channel.png)](https://clojurians.slack.com/messages/practicalli) 14 | 15 | Get a [free Clojurians slack community account](https://clojurians.net/) 16 | -------------------------------------------------------------------------------- /docs/thinking-functionally/iterate-over-values.md: -------------------------------------------------------------------------------- 1 | # iterate Over Values 2 | 3 | This 4 | 5 | > #### Hint::Work in progress 6 | 7 | * loop recur 8 | * reducing functions 9 | * map apply reduce 10 | * partition group-by sort-by 11 | 12 | ## loop recur 13 | 14 | loop recur is a very detailed way of defining a way to iterate over values. map, reduce and apply are commonly used abstractions for iterating over values. They simplify the code (once you are comfortable with them) 15 | 16 | Functions that iterate over values usually treat a string as a sequence of characters. 17 | -------------------------------------------------------------------------------- /docs/thinking-functionally/managing-state-changes.md: -------------------------------------------------------------------------------- 1 | # Managing state changes 2 | -------------------------------------------------------------------------------- /docs/thinking-functionally/pattern-matching.md: -------------------------------------------------------------------------------- 1 | # Pattern matching 2 | 3 | > **Fixme** work in progress 4 | 5 | # Regular Expression 6 | 7 | # Destructuring 8 | -------------------------------------------------------------------------------- /docs/thinking-functionally/persistent-data-structures.md: -------------------------------------------------------------------------------- 1 | # Persistent data structures 2 | -------------------------------------------------------------------------------- /docs/thinking-functionally/polymorphism.md: -------------------------------------------------------------------------------- 1 | # Polymorphic function definitions 2 | 3 | Polymorphic means many forms. 4 | 5 | The simplest example of polymorphism in Clojure is a function definition that acts differently based on the number of arguments passed. 6 | 7 | Usually you define a function with one set of arguments, either none `[]`, one `[one]` or many `[any number of args]`, using the basic syntax 8 | 9 | ```clojure 10 | (defn name 11 | "I am the doc string to describe the function" 12 | [args] 13 | (str "define your behaviour here")) 14 | ``` 15 | 16 | Instead of writing multiple functions with the same name that each take different numbers of arguments, you can use the following polymorphic syntax in Clojure 17 | 18 | ```clojure 19 | (defn name 20 | "I am the doc string to describe the function" 21 | ([] 22 | (str "behaviour with no args")) 23 | ([one] 24 | (str "behaviour with one arg")) 25 | ([one two & args] 26 | (str "behaviour with multiple args"))) 27 | ``` 28 | 29 | > **Note** Write a simple function called `i-am-polly` that returns a default message when given no arguments and a custom message when given a custom string as an argument 30 | 31 | 32 | 33 | ```clojure 34 | (defn i-am-polly 35 | ([] (i-am-polly "My name is polly")) 36 | ([message] (str message))) 37 | 38 | (i-am-polly) 39 | (i-am-polly "I call different behaviour depending on arguments sent") 40 | ``` 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/thinking-functionally/pure-functions.md: -------------------------------------------------------------------------------- 1 | # Pure functions 2 | 3 | A function is considered pure if does not side effects or is affected by side causes. A pure function does not change any other part of the system and is not affected by any other part of the system. 4 | 5 | When you pass arguments to a function and that function returns a value without interacting with any other part of the system, then that function is considered pure. 6 | 7 | Should something from outside a function be allowed to affect the result of evaluating a function, or if that function be allowed to affect the outside world, then its an impure function. 8 | 9 | ![Pure function basic concept](/images/functional-programming-concepts-pure-function.png) 10 | 11 | So lets look at a simple code example 12 | 13 | > ####Note::Write a pure function that adds two numbers together ? 14 | 15 | ```clojure 16 | (defn add-numbers [number1 number2] 17 | (+ number1 number2)) 18 | 19 | (add-numbers 1 2) 20 | ``` 21 | 22 | Lets look at each line of this suggested answer 23 | 24 | ```clojure 25 | ;; function takes 2 arguments 26 | ;; function uses both arguments for result 27 | (defn add-numbers [number1 number2] 28 | (+ number1 number2)) 29 | 30 | ;; specific values are passed as arguments 31 | (add-numbers 1 2) 32 | ``` 33 | 34 | # An example with map 35 | 36 | > **Note** Define a collection called numbers and write a named function that increments each number of the numbers collection. 37 | > Is your function pure or impure ? 38 | 39 | ```clojure 40 | (def numbers '(5 4 3 2 1)) 41 | 42 | (defn increment-numbers [] 43 | (map inc numbers)) 44 | 45 | (increment-numbers) 46 | ``` 47 | 48 | The function takes no arguments and is pulling in a value from outside the function. This is a trivial example, but if all your code is like this it would be more complex. If the value pointed to by `numbers` is mutable and changes before the `increment-numbers` function is called then you will get different results. 49 | 50 | Here is a Pure function example 51 | 52 | ```clojure 53 | (def numbers '(5 4 3 2 1)) 54 | 55 | (defn increment-numbers [number-collection] 56 | (map inc number-collection)) 57 | 58 | (increment-numbers numbers) 59 | ``` 60 | 61 | In this example we are explicitly passing the `numbers` collection to the function. The function works on passed value and returns a predictable result. 62 | -------------------------------------------------------------------------------- /docs/thinking-functionally/recursion-polymorphism.md: -------------------------------------------------------------------------------- 1 | # Recursion & Polymorphism 2 | 3 | > **Fixme** work in progress 4 | 5 | The following `sum` function will calculate the value of adding all the elements in a collection. You can alter the results by adding a starting value to the calculation as a second argument when calling `sum` 6 | 7 | ```clojure 8 | (defn sum 9 | ([vals] (sum vals 0)) 10 | ([vals accumulating-total] 11 | (if (empty? vals) 12 | accumulating-total 13 | (sum (rest vals) (+ (first vals) accumulating-total))))) 14 | 15 | (sum [2 7 9 11 13]) 16 | (sum [1]) 17 | (sum [2 7 9 11 13] 9) 18 | ``` 19 | 20 | Rather than duplicate the calculation, the behaviour of calling `sum` with just a collection simply calls `sum` again, this time passing a starting value of zero. 21 | -------------------------------------------------------------------------------- /docs/thinking-functionally/recursion.md: -------------------------------------------------------------------------------- 1 | # Recursion 2 | 3 | > **Fixme** work in progress 4 | 5 | Recursion is used greatly in Clojure to iterate through data and as anything can be treated as data in Clojure you can understand why. 6 | 7 | The constructs available in Clojure for recursion include 8 | 9 | * `loop` and `recur` 10 | * Named function that calls itself 11 | * `map`, `reduce`, `filter`, `remove`, etc. 12 | * `for` 13 | 14 | # Recursively calling the same function 15 | 16 | Lets iterate though a collection using recursion by writing a function that calls itself 17 | 18 | ```clojure 19 | (defn recursively-use-a-collection [collection] 20 | (println (first collection)) 21 | (if (empty? collection) 22 | (print-str "no more values to process") 23 | (recursively-use-a-collection (rest collection)))) 24 | 25 | (recursively-use-a-collection [1 2 3]) 26 | ``` 27 | 28 | Lets take this recursive approach to create a function that can tell us the length of a collection (list or vector) 29 | 30 | We define a function that takes a collection of an argument. The collection is tested to see if it is empty and if so a zero value is returned. If the collection is not empty, then we 31 | 32 | ```clojure 33 | (defn length [collection] 34 | (if (empty? collection) 35 | 0 36 | (+ 1 (length (rest collection))))) 37 | ;; => #'clojure-through-code.01-basics/length 38 | ``` 39 | 40 | If we call the `length` function with an empty collection, then the `empty?` condition will return true and the `if` expression will evaluate the first expression, 0, returning 0. 41 | 42 | ```clojure 43 | (length []) 44 | ;; => 0 45 | 46 | ``` 47 | 48 | If we call the `length` function with a collection containing 3 values, then the `empty?` function will return `false` and the `if` function will evaluate the second expression. 49 | 50 | The second expression starts with a simple counter, using the `+` function and the value one 51 | 52 | ```clojure 53 | (length [0 1 2]) 54 | ;; => 3 55 | 56 | ``` 57 | 58 | ```clojure 59 | (+ 1 (length [1 2])) 60 | (+ 1 (+ 1 (length [2]))) 61 | (+ 1 (+ 1 (+ 1 (length [])))) 62 | (+ 1 (+ 1 (+ 1 0))) 63 | 64 | (length (range 24)) 65 | ;; => 24 66 | 67 | ``` 68 | 69 | (defn length [collection] 70 | (kk)) 71 | 72 | # Further recursion examples 73 | 74 | Other functions to consider 75 | 76 | * every 77 | * accumulating / accumulative 78 | * keep 79 | -------------------------------------------------------------------------------- /docs/thinking-functionally/sequence-abstractions.md: -------------------------------------------------------------------------------- 1 | # Sequence abstraction 2 | 3 | > **Fixme** work in progress 4 | 5 | ```clojure 6 | (first '(1 2 3 4 5)) 7 | (rest '(1 2 3 4 5)) 8 | (last '(1 2 3 4 5)) 9 | ``` 10 | 11 | ```clojure 12 | (defn nth [items n] 13 | (if (= n 0) 14 | (first items) 15 | (recur (rest items) (- n 1)))) 16 | 17 | (define squares '(0 1 4 9 16 25)) 18 | 19 | (nth squares 3) 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/thinking-functionally/sequences.md: -------------------------------------------------------------------------------- 1 | # Sequences 2 | 3 | > **Fixme** work in progress 4 | 5 | Data structures can be built by combining functions 6 | 7 | ```clojure 8 | (cons 1 (cons 2 (cons 3 (cons 4 nil)))) 9 | ``` 10 | 11 | ```clojure 12 | (->> 13 | nil 14 | (cons 4) 15 | (cons 3) 16 | (cons 2) 17 | (cons 1)) 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/thinking-functionally/side-effects.md: -------------------------------------------------------------------------------- 1 | # Side effects 2 | 3 | A side effect is something that creates a change outside of the current code scope, or something external that affects the behaviour or result of executing the code in the current scope. 4 | 5 | ## Nondeterministic - the complexity iceberg 6 | 7 | When you have side effects, you cannot reason accurately about a piece of the code. 8 | 9 | In order to understand a piece of code you must look at all possible side effects created in all lines of code to ensure you fully understand the result of executing your code. 10 | 11 | With side effects in your system, complexity is hidden, causing a far greater risk of a dangerous situation. 12 | 13 | ## Side causes - side effects 14 | 15 | You can think about these effects is in two specific areas, **Side Causes** and **Side Effects** 16 | 17 | * **Side Causes** - are where other pieces of code (function) or state change affects the behaviour of a function. 18 | 19 | * **Side Effects** - are where the current code (function) affects the rest of the system 20 | 21 | [![Side Causes & Side Effects - Kris Jenkins](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/side-causes-side-effects.png)](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/side-causes-side-effects.png) 22 | 23 | > #### Hint::Side Causes term 24 | > 25 | > The term of side causes was coined by [Kris Jenkins](https://twitter.com/krisajenkins) in the superb article [What is Functional Programming?](http://blog.jenkster.com/2015/12/what-is-functional-programming.html) 26 | -------------------------------------------------------------------------------- /docs/thinking-functionally/tail-recursion.md: -------------------------------------------------------------------------------- 1 | # Tail recursion 2 | 3 | > **Fixme** work in progress 4 | 5 | If we generate a very large collection we run the risk of blowing our heap space. For example we could use range to generate a very large collection, say a vector containing 10 billion values 6 | 7 | Don't try this example below 8 | 9 | ```clojure 10 | (vec (range 0 9999999999)) 11 | ;; this will crash after a short while as it will use up all your heap space 12 | ``` 13 | 14 | Using tail call optimisation (tail recursion) allows us to reuse a memory location when we call a function recursively. This tail recursion is not part of the underlying Java Virtual Machine (JVM), so instead Clojure has a specific function called `recur` 15 | 16 | The `recur` function allows the processing of a very large data set without blowing the heap space because the memory space will be reused. 17 | 18 | The `recur` function must be the last expression in order to work. 19 | 20 | ```clojure 21 | (defn sum 22 | ([vals] (sum vals 0)) 23 | ([vals accumulating-total] 24 | (if (empty? vals) 25 | accumulating-total 26 | (recur (rest vals) (+ (first vals) accumulating-total))))) 27 | 28 | (sum (vec (range 0 9999999))) 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/using-data-structures/applying-functions.md: -------------------------------------------------------------------------------- 1 | # Applying functions to data structures 2 | 3 | Applying a functions behaviour to the elements of a data structure 4 | -------------------------------------------------------------------------------- /docs/using-data-structures/destructuring.md: -------------------------------------------------------------------------------- 1 | # Destructuring 2 | 3 | Destructuring is a form of pattern matching that is common in Clojure. Destructuring allow you to pull out the specific elements from a collection. 4 | 5 | Destructuring is commonly used with the `let` method for creating local bindings (locally scoped names). 6 | 7 | ```clojure 8 | (let [[a b c & d :as e] [1 2 3 4 5 6 7]] 9 | [a b c d e]) 10 | 11 | (let [[[x1 y1][x2 y2]] [[1 2] [3 4]]] 12 | [x1 y1 x2 y2]) 13 | 14 | ;; with strings 15 | (let [[a b & c :as str] "asdjhhfdas"] 16 | [a b c str]) 17 | 18 | ;; with maps 19 | (let [{a :a, b :b, c :c, :as m :or {a 2 b 3}} {:a 5 :c 6}] 20 | [a b c m]) 21 | ``` 22 | 23 | It is often the case that you will want to bind same-named symbols to the map keys. The :keys directive allows you to avoid the redundancy: 24 | 25 | ```clojure 26 | (let [{fred :fred ethel :ethel lucy :lucy} m] ) 27 | ``` 28 | 29 | This can be written in a shorter form as follows: 30 | 31 | ```clojure 32 | (let [{:keys [fred ethel lucy]} m] ) 33 | ``` 34 | 35 | As of Clojure 1.6, you can also use prefixed map keys in the map destructuring form: 36 | 37 | ```clojure 38 | (let [m {:x/a 1, :y/b 2} 39 | {:keys [x/a y/b]} m] 40 | (+ a b)) 41 | ``` 42 | 43 | As shown above, in the case of using prefixed keys, the bound symbol name will be the same as the right-hand side of the prefixed key. You can also use auto-resolved keyword forms in the :keys directive: 44 | 45 | ```clojure 46 | (let [m {::x 42} 47 | {:keys [::x]} m] 48 | x) 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/using-data-structures/lazy-sequences.md: -------------------------------------------------------------------------------- 1 | # Lazy Sequences 2 | 3 | Sequences are an interface for logical lists, which can be lazy. "Lazy" means that a sequence can define an infinite series, like so: 4 | 5 | ``` 6 | (range 4) 7 | ``` 8 | 9 | If you evaluate `(range)` just by itself it will return an infinite number of integers, well at least until your computers memory space fills up. 10 | 11 | So we dont blow up our memory and just get the values we want we can use `range` in conjunction with other functions that define how many numbers we actually want. 12 | 13 | For example, if we just wanted the first four numbers from the infinite sequence of `range` we could specify that with the `take` function 14 | 15 | ``` 16 | (take 4 (range)) ; (0 1 2 3) 17 | ``` 18 | 19 | Here the range function is being lazy, because it will only generate the first 4 numbers in its sequence. 20 | 21 | Clojure (and Lisps in general) often evaluate at the last possible moment, usually when they have been given more specific content. 22 | -------------------------------------------------------------------------------- /docs/using-data-structures/mapping-data-structures.md: -------------------------------------------------------------------------------- 1 | # Mapping functions over data structures 2 | 3 | Map allows you to work over one or more data sets, applying the function to each element of each of the data structures. 4 | 5 | When the data structures are of equal size, then the same sized data structure is returned. 6 | 7 | ```clojure 8 | (map + [1 2 3] [1 2 3]) 9 | ;; => (2 4 6) 10 | ``` 11 | 12 | If one data structure is smaller, then the function is only applied up to the last element of the smallest data structure. 13 | 14 | ```clojure 15 | (map + [1 2 3] [1 2]) 16 | ;; => (2 4) 17 | 18 | (map + [1 2 3] [1]) 19 | ;; => (2) 20 | 21 | (map + [1 2 3] []) 22 | ;; => () 23 | 24 | (map + [1 2 3]) 25 | ;; => (1 2 3) 26 | ``` 27 | 28 | Lets look at another example. Here we have a pre-defined Fibonacci sequence up to the first 12 values. 29 | 30 | ```clojure 31 | (def fibonacci-sequence [1 2 3 5 8 13 21 34 55 89 144 278]) 32 | ``` 33 | 34 | If we just want the first 10 values of the sequence, we can use the `take` function. 35 | 36 | ```clojure 37 | (take 10 fibonacci-sequence) 38 | ;; => (1 2 3 5 8 13 21 34 55 89) 39 | ``` 40 | 41 | If we want a calculation using the values of the fibonacci-sequence then we can use `map` with a function. In this case we are going to generate a range of Integer numbers from 0-9 using the function `range`. That range of numbers is then multiplied element by element with the corresponding element in the fibonacci-sequence. 42 | 43 | ```clojure 44 | (map * (range 10) fibonacci-sequence) 45 | ;; => (0 2 6 15 32 65 126 238 440 801) 46 | ``` 47 | 48 | So, 49 | 50 | - 0 times 1 is 0, 51 | - 1, times 2 is 2, 52 | - 2 times 3 is 6, etc. 53 | 54 | If we evaluate the previous expression part by part, its easier to see what is going on. First lets evaluate the `fibonacci-sequence` 55 | 56 | ```clojure 57 | (map * (range 10) [1 2 3 5 8 13 21 34 55 89 144 278]) 58 | ;; => (0 2 6 15 32 65 126 238 440 801) 59 | ``` 60 | 61 | Now lets evaluate the `(range 10)` function call 62 | 63 | ```clojure 64 | (map * (0 1 2 3 4 5 6 7 8 9) [1 2 3 5 8 13 21 34 55 89 144 278]) 65 | ;; => (0 2 6 15 32 65 126 238 440 801) 66 | ``` 67 | 68 | We can see the answer is the same, however by evaluating each part of the expression we get an exact view of what is going to happen with the `map` function. 69 | -------------------------------------------------------------------------------- /overrides/404.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | {% extends "main.html" %} 7 | 8 | 9 | {% block content %} 10 |

This is not the page you are looking for

11 | 12 |

13 | Sorry we have arrived at a page that does not exist... 14 |

15 | 16 |

17 | Practicalli website are published using Material for MkDocs 18 |

19 | 20 |

21 | Use the Search bar at the top of the page or left navigation to find the relevant content. 22 |

23 | 24 |

25 | 26 | Practicalli Clojure book cover 28 | 29 |

30 | 31 |

32 | 33 | Practicalli Clojure book cover 35 | 36 |

37 | 38 | 39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /overrides/main.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | {% extends "base.html" %} 10 | 11 | 12 | {% block announce %} 13 | 14 | Practicalli Clojure has migrated to MkDocs 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /overrides/partials/palette.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | {% for option in config.theme.palette %} 7 | {% set scheme = option.scheme | d("default", true) %} 8 | {% set primary = option.primary | d("indigo", true) %} 9 | {% set accent = option.accent | d("indigo", true) %} 10 | 25 | {% if option.toggle %} 26 | 34 | {% endif %} 35 | {% endfor %} 36 |
37 | -------------------------------------------------------------------------------- /overrides/partials/source.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | {% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %} 4 | {% include ".icons/" ~ icon ~ ".svg" %} 5 |
6 |
7 | {{ config.repo_name }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /use-cases.org: -------------------------------------------------------------------------------- 1 | * Complex use cases 2 | ** Cycle race management and predictions 3 | Design and build a system to calculate and manage the rankings of cyclists. 4 | 5 | Include prediction system for winners of races based on ranking. Also for Champion of the mountains predictions 6 | 7 | System should report on its own accuracy of predictions based on results of cycling events 8 | --------------------------------------------------------------------------------