37 |
38 |
39 | {% endblock %}
40 |
--------------------------------------------------------------------------------
/overrides/partials/palette.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
37 |
--------------------------------------------------------------------------------
/.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/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 |
--------------------------------------------------------------------------------
/.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/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 |
--------------------------------------------------------------------------------
/docs/install/index.md:
--------------------------------------------------------------------------------
1 | # Install Clojure
2 |
3 | [Clojure CLI](clojure-cli.md) provides the foundation for Clojure development, providing a declarative approach to:
4 |
5 | * Run Clojure programs and tools
6 | * Run a REPL process (Read-Eval-Print Loop) and provides a basic interactive terminal UI
7 | * Manage packaged dependencies from Maven (jars) and use Git repositories as dependencies
8 |
9 | !!! INFO "Practicalli Clojure Config community tools"
10 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](clojure-cli/#practicalli-clojure-cli-config) is a user configuration providing aliases for a wide range of community tools which extends the features of Clojure CLI. The aliases include tools to create, develop, build and deploy Clojure code. Aliases are used heavily in the Practicalli books.
11 |
12 | If the Practicalli Clojure CLI config is not used, review the [:fontawesome-brands-github: `deps.edn` file](https://github.com/practicalli/clojure-cli-config/blob/live/deps.edn){target=_blank} from the GitHub repository and add relevant aliases definitions to your own Clojure CLI configuration.
13 |
14 | ## "Pre-requisites"
15 |
16 | A [Java Virtual Machine](java.md) hosts Clojure. Java 21 is the current Long Term Support version providing a stable platform to run Clojure
17 |
18 |
19 | ## Additional tools
20 |
21 | !!! INFO "Clojure connected editor"
22 | A [Clojure connected editor](/clojure/clojure-editors/) provides the most effective way to write and maintain Clojure projects. The editor connects to (or starts) a Clojure REPL and code can be evaluated as its typed, showing the results instantly in line with the code.
23 |
24 | [Clojure LSP server](/clojure/clojure-editors/clojure-lsp/) generates static analysis of code which editors can surface as code diagnostics. Analysis supports effective code navigate and refactor tools. [:fontawesome-solid-book-open: Practicalli Clojure LSP config](/clojure/clojure-editors/clojure-lsp/) configures
25 |
26 | !!! INFO "Data Inspectors"
27 | [Data inspectors](/clojure/data-inspectors/) visualize results of Clojure code evaluation and allow navigation of nested data or paging through large data sets.
28 |
29 | [Portal](/clojure/data-inspector/portal/) is highly recommended data inspector and included in projects generated with [Practicalli Project Templates](/clojure/clojure-cli/projects/templates/practicalli/).
30 |
31 |
32 | ??? INFO "Alternative development tools"
33 | [Leiningen](https://leiningen.org){target=_blank} is the long-standing development tool for Clojure. All the code examples in this book should work with Leiningen when a correctly configured `project.clj` file is created which includes all the necessary library dependencies. Libraries included via aliases should be added as either `:dev-dependencies` or `:aliases` in the Leiningen `project.clj` file.
34 |
35 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # ------------------------------------------
2 | # Practicalli: Makefile
3 | #
4 | # Consistent set of targets to support local book development
5 | # ------------------------------------------
6 |
7 | # .PHONY: ensures target used rather than matching file name
8 | # https://makefiletutorial.com/#phony
9 | .PHONY: all clean docs lint pre-commit-check test
10 |
11 | # ------- Makefile Variables --------- #
12 | # run help if no target specified
13 | .DEFAULT_GOAL := help
14 | SHELL := /usr/bin/zsh
15 |
16 | # Column the target description is printed from
17 | HELP-DESCRIPTION-SPACING := 24
18 |
19 | # Tool Commands
20 | MEGALINTER_RUNNER := npx mega-linter-runner --flavor documentation --env "'MEGALINTER_CONFIG=.github/config/megalinter.yaml'" --env "'VALIDATE_ALL_CODEBASE=true'" --remove-container
21 | MKDOCS_SERVER := mkdocs serve --dev-addr localhost:7777
22 |
23 | # Makefile file and directory name wildcard
24 | EDN-FILES := $(wildcard *.edn)
25 | # ------------------------------------ #
26 |
27 | # ------ Quality Checks ------------ #
28 | pre-commit-check: lint
29 |
30 | lint: ## Run MegaLinter with custom configuration (node.js required)
31 | $(info --------- MegaLinter Runner ---------)
32 | $(MEGALINTER_RUNNER)
33 |
34 | lint-fix: ## Run MegaLinter with custom configuration (node.js required)
35 | $(info --------- MegaLinter Runner ---------)
36 | $(MEGALINTER_RUNNER) --fix
37 |
38 | lint-clean: ## Clean MegaLinter report information
39 | $(info --------- MegaLinter Clean Reports ---------)
40 | - rm -rf ./megalinter-reports
41 |
42 | megalinter-upgrade: ## Upgrade MegaLinter config to latest version
43 | $(info --------- MegaLinter Upgrade Config ---------)
44 | npx mega-linter-runner@latest --upgrade
45 | # ------------------------------------ #
46 |
47 | # --- Documentation Generation ------ #
48 | python-venv: ## Enable Python Virtual Environment for MkDocs
49 | $(info --------- Mkdocs Local Server ---------)
50 | source ~/.local/venv/bin/activate
51 |
52 | docs: ## Build and run mkdocs in local server (python venv)
53 | $(info --------- Mkdocs Local Server ---------)
54 | source ~/.local/venv/bin/activate && $(MKDOCS_SERVER)
55 |
56 | docs-changed: ## Build only changed files and run mkdocs in local server (python venv)
57 | $(info --------- Mkdocs Local Server ---------)
58 | source ~/.local/venv/bin/activate && $(MKDOCS_SERVER) --dirtyreload
59 |
60 | docs-build: ## Build mkdocs (python venv)
61 | $(info --------- Mkdocs Local Server ---------)
62 | source ~/.local/venv/bin/activate && mkdocs build
63 | # ------------------------------------ #
64 |
65 | # ------------ Help ------------------ #
66 | # Source: https://nedbatchelder.com/blog/201804/makefile_help_target.html
67 |
68 | help: ## Describe available tasks in Makefile
69 | @grep '^[a-zA-Z]' $(MAKEFILE_LIST) | \
70 | sort | \
71 | awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-$(HELP-DESCRIPTION-SPACING)s\033[0m %s\n", $$1, $$2}'
72 | # ------------------------------------ #
73 |
--------------------------------------------------------------------------------
/overrides/partials/header.html:
--------------------------------------------------------------------------------
1 |
2 | {% set class = "md-header" %} {% if "navigation.tabs.sticky" in features %} {%
3 | set class = class ~ " md-header--shadow md-header--lifted" %} {% elif
4 | "navigation.tabs" not in features %} {% set class = class ~ " md-header--shadow"
5 | %} {% endif %}
6 |
7 |
8 |
9 |
74 |
75 |
76 | {% if "navigation.tabs.sticky" in features %} {% if "navigation.tabs" in
77 | features %} {% include "partials/tabs.html" %} {% endif %} {% endif %}
78 |
79 |
--------------------------------------------------------------------------------
/.github/config/megalinter.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Configuration file for MegaLinter
3 | #
4 | # General configuration:
5 | # https://megalinter.io/latest/configuration/
6 | #
7 | # Specific Linters:
8 | # https://megalinter.io/latest/supported-linters/
9 |
10 | # ------------------------
11 | # Validate all files if true
12 | # or new / edited files if false
13 | VALIDATE_ALL_CODEBASE: false
14 |
15 | # ------------------------
16 | # Linters
17 |
18 | # Run linters in parallel
19 | PARALLEL: true
20 |
21 | # ENABLE specific linters, all other linters automatically disabled
22 | ENABLE:
23 | # - CLOJURE
24 | - CREDENTIALS
25 | - DOCKERFILE
26 | - MAKEFILE
27 | - MARKDOWN
28 | - GIT
29 | - SPELL
30 | - YAML
31 | - REPOSITORY
32 |
33 | # Linter specific configuration
34 |
35 | # CLOJURE_CLJ_KONDO_CONFIG_FILE: ".github/config/clj-kondo-ci-config.edn"
36 | # CLOJURE_CLJ_KONDO_ARGUMENTS: "--lint deps.edn"
37 | # CLOJURE_CLJ_KONDO_FILTER_REGEX_EXCLUDE: "dev|develop"
38 | # CLOJURE_CLJ_KONDO_FILTER_REGEX_EXCLUDE: "resources"
39 |
40 | # CREDENTIALS_SECRETLINT_DISABLE_ERRORS: true
41 | CREDENTIALS_SECRETLINT_CONFIG_FILE: ".github/config/secretlintrc.json"
42 |
43 | MARKDOWN_MARKDOWNLINT_CONFIG_FILE: ".github/config/markdown-lint.jsonc"
44 | MARKDOWN_MARKDOWNLINT_FILTER_REGEX_EXCLUDE: ".github/pull_request_template.md|CHANGELOG.md|README.md|GLOSSARY.md|java-17-flags.md|abbreviations.md"
45 | # MARKDOWN_MARKDOWNLINT_DISABLE_ERRORS: true
46 | MARKDOWN_MARKDOWN_LINK_CHECK_CONFIG_FILE: ".github/config/markdown-link-check.json"
47 | # MARKDOWN_MARKDOWN_LINK_CHECK_CLI_LINT_MODE: "project"
48 | # MARKDOWN_MARKDOWN_LINK_CHECK_DISABLE_ERRORS: false
49 | MARKDOWN_REMARK_LINT_DISABLE_ERRORS: true
50 | # MARKDOWN_MARKDOWN_TABLE_FORMATTER_DISABLE_ERRORS: false
51 |
52 | REPOSITORY_GITLEAKS_CONFIG_FILE: ".github/config/gitleaks.toml"
53 | REPOSITORY_TRUFFLEHOG_DISABLE_ERRORS: true # Errors only as warnings
54 |
55 | # SPELL_CSPELL_DISABLE_ERRORS: true
56 | SPELL_MISSPELL_DISABLE_ERRORS: true
57 | SPELL_LYCHEE_CONFIG_FILE: ".github/config/lychee.toml"
58 | SPELL_LYCHEE_DISABLE_ERRORS: true # Errors are only warnings
59 |
60 | # YAML_PRETTIER_FILTER_REGEX_EXCLUDE: (docs/)
61 | # YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: (docs/)
62 |
63 | # Explicitly disable linters to ensure they are never run
64 | # DISABLE:
65 | # - COPYPASTE # checks for excessive copy-pastes
66 | # - SPELL # spell checking - often creates many false positives
67 | # - CSS #
68 |
69 | # Disable linter features
70 | DISABLE_LINTERS:
71 | - YAML_PRETTIER # draconian format rules
72 | - SPELL_CSPELL # many clojure references causing false positives
73 | - YAML_YAMLLINT # vague error mesages, investigation required
74 | - REPOSITORY_GIT_DIFF # warnings about LF to CRLF
75 | - REPOSITORY_SECRETLINT # reporting errors in its own config file
76 | # - REPOSITORY_DEVSKIM # unnecessary URL TLS checks
77 | - REPOSITORY_CHECKOV # fails on root user in Dockerfile
78 | - REPOSITORY_SECRETLINT
79 |
80 | # Ignore all errors and return without error status
81 | # DISABLE_ERRORS: true
82 |
83 | # ------------------------
84 |
85 | # ------------------------
86 | # Reporting
87 |
88 | # Activate sources reporter
89 | UPDATED_SOURCES_REPORTER: false
90 |
91 | # Show Linter timings in summary table at end of run
92 | SHOW_ELAPSED_TIME: true
93 |
94 | # Upload reports to file.io
95 | FILEIO_REPORTER: false
96 | # ------------------------
97 |
98 | # ------------------------
99 | # Over-ride errors
100 |
101 | # detect errors but do not block CI passing
102 | # DISABLE_ERRORS: true
103 | # ------------------------
104 |
--------------------------------------------------------------------------------
/docs/assets/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | [data-md-color-scheme="default"] {
2 | --md-default-bg-color: hsla(208, 100%, 96%, 0.94);
3 | --md-code-bg-color: hsla(208, 80%, 88%, 0.64);
4 | --md-code-hl-color: hsla(208, 88%, 80%, 0.92);
5 | --md-admonition-bg-color: hsla(208, 80%, 92%, 0.92);
6 | --md-typeset-kbd-color: hsla(208, 100%, 98%, 0.98);
7 | }
8 |
9 | /* Custom Admonitions */
10 |
11 |
12 | :root {
13 | /* Clojure Idiom*/
14 | --md-admonition-icon--clojure-idiom: url(https://raw.githubusercontent.com/practicalli/graphic-design/c40cc063cc5bb07525b524d8a3d638e2f42bc38a/logos/clojure-logo-bullet.svg);
15 |
16 | /* Round corners */
17 | --base-border-radius: 0.5rem;
18 | }
19 |
20 | /*Admonitions colors*/
21 | .md-typeset .admonition.clojure-idiom,
22 | .md-typeset details.clojure-idiom {
23 | border-color: rgb(43, 155, 70);
24 | }
25 | .md-typeset .clojure-idiom > .admonition-title,
26 | .md-typeset .clojure-idiom > summary {
27 | background-color: rgba(43, 155, 70, 0.1);
28 | }
29 | .md-typeset .clojure-idiom > .admonition-title::before,
30 | .md-typeset .clojure-idiom > summary::before {
31 | background-color: rgb(250, 250, 250);
32 | background-image: var(--md-admonition-icon--clojure-idiom);
33 | -webkit-mask-image: var(--md-admonition-icon--clojure-idiom);
34 | mask-image: var(--md-admonition-icon--clojure-idiom);
35 | }
36 |
37 |
38 | /* Change font family of filename present on top of code block. */
39 | .highlight span.filename {
40 | border-bottom: none;
41 | border-radius: var(--base-border-radius);
42 | display: inline;
43 | font-family: var(--md-code-font-family);
44 | border-bottom-left-radius: 0;
45 | border-bottom-right-radius: 0;
46 | margin-bottom: 5px;
47 | text-align: center;
48 | }
49 | .highlight span.filename + pre > code {
50 | border-radius: var(--base-border-radius);
51 | border-top-left-radius: 0;
52 | }
53 | .md-typeset pre > code {
54 | border-radius: var(--base-border-radius);
55 | }
56 |
57 | /* Grid Cards */
58 | .md-typeset .grid.cards > ul > li {
59 | border-radius: var(--base-border-radius);
60 | }
61 | .md-typeset .grid.cards > ul > li:hover {
62 | box-shadow: 0 0 0.2rem #ffffff40;
63 | }
64 |
65 | /* Markdown Button */
66 | .md-typeset .md-button {
67 | border-radius: var(--base-border-radius);
68 | }
69 |
70 | /* Critic, Mark */
71 | ins.critic,
72 | del.critic {
73 | text-decoration: none;
74 | }
75 |
76 | .md-typeset .critic,
77 | .md-typeset mark {
78 | border-radius: 0.2rem;
79 | padding: 0 0.2rem;
80 | }
81 |
82 | .md-typeset mark {
83 | box-shadow: 0 0 0 0.1rem var(--md-typeset-mark-color);
84 | }
85 |
86 | .md-typeset ins.critic {
87 | box-shadow: 0 0 0 0.1rem var(--md-typeset-ins-color);
88 | }
89 |
90 | .md-typeset del.critic {
91 | box-shadow: 0 0 0 0.1rem var(--md-typeset-del-color);
92 | }
93 |
94 | /* Forms */
95 | .md-search__form {
96 | border-radius: var(--base-border-radius);
97 | }
98 |
99 | [data-md-toggle="search"]:checked ~ .md-header .md-search__form {
100 | border-top-right-radius: var(--base-border-radius);
101 | border-top-left-radius: var(--base-border-radius);
102 | }
103 |
104 | [dir="ltr"] .md-search__output {
105 | border-bottom-right-radius: var(--base-border-radius);
106 | border-bottom-left-radius: var(--base-border-radius);
107 | }
108 |
109 | /* Blog - index.md */
110 | .md-post--excerpt {
111 | background-color: var(--md-accent-fg-color--transparent);
112 | box-shadow: 0 0 0 1rem var(--md-accent-fg-color--transparent);
113 | border-radius: var(--base-border-radius);
114 | }
115 |
116 | /* Table */
117 | .md-typeset table:not([class]) {
118 | border-radius: var(--base-border-radius);
119 | }
120 |
--------------------------------------------------------------------------------
/docs/introduction/concepts/index.md:
--------------------------------------------------------------------------------
1 | # Clojure concepts
2 |
3 | Clojure is an elegant language for a more civilized development experience.
4 |
5 | Clojure supports the creation of simple software systems using immutable values and encouraging a pragmatic approach to pure functional design.
6 |
7 | A simple syntax means Clojure is quick to learn and a wide range of open source libraries provides a rapid way to build any kind of software. Designed as a hosted language, Clojure runs on many platforms including the Java Virtual Machine, GraalVM, Microsoft.Net, JavaScript engines. Simple host language interoperability provides access to libraries from a wide range of programming languages, further extending the reach of Clojure.
8 |
9 | !!! HINT "Experiment with the Clojure language to help understand concepts"
10 | Spend some time eevaluating code in the REPL and then revisit this section to get a deeper understanding of the design and philosophy of the Clojure approach to functional programming.
11 |
12 | Clojure concepts are easier to relate to whist practicing with Clojure and building Clojure software solutions.
13 |
14 | ## Ten Big Ideas plus one
15 |
16 | The key to understanding Clojure is ideas, not language constructs but the concepts that shape the language.
17 |
18 | Each of these ideas is valuable by itself, not only in Clojure. Taken together, however, they Begin to fill in the picture of why Clojure is changing the way many programmers think about software development.
19 |
20 | 1. [Extensible Data Notation](https://github.com/edn-format/edn){target=_blank}
21 | 2. [Persistent Data Structures](https://clojure.org/reference/data_structures){target=_blank}
22 | 3. [Sequences](https://clojure.org/reference/sequences){target=_blank}
23 | 4. [Transducers](https://clojure.org/reference/transducers){target=_blank}
24 | 5. [Specification](https://clojure.org/about/spec){target=_blank}
25 | 6. [Dynamic Development](https://clojure.org/about/dynamic){target=_blank}
26 | 7. [Async Programming](http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html){target=_blank}
27 | 8. [Protocols](https://clojure.org/reference/protocols){target=_blank}
28 | 9. [ClojureScript](https://clojurescript.org/){target=_blank}
29 | 10. [Logic query](http://docs.datomic.com/query.html){target=_blank} / [Logic Programming](https://github.com/clojure/core.logic){target=_blank}
30 | 11. [Atomic Succession Model](https://clojure.org/about/concurrent_programming){target=_blank}
31 |
32 | Stuart Halloway presents [Clojure in 10 big ideas (plus one)](https://vimeo.com/223240720){target=_blank} in the following video, also see [presentation Content](https://github.com/stuarthalloway/presentations/wiki/Clojure-in-10-Big-Ideas){target=_blank}
33 |
34 |
35 |
36 |
37 |
38 | * 2013 [RuPy slides](https://github.com/stuarthalloway/presentations/blob/master/Barnstorming_2013/ClojureInTenBigIdeas.pdf?raw=true){target=_blank}
39 | * 2017 [Chicago JUG slides](https://github.com/stuarthalloway/presentations/blob/master/ClojureInTenBigIdeas-Jun-2017.pdf?raw=true){target=_blank}
40 |
41 | ## Antithesis of Clojure and simple software design
42 |
43 | In Narcissistic Design by Stuart Halloway, the antithesis of the Clojure view of software development is presented as a description of how unproductive and valueless much of the software industry has been in the past.
44 |
45 | Its essentially a guide on what to avoid if you are a responsible and professional software developer.
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Practicalli Clojure AI Tools
2 |
3 | A hands-on guide to using using Artificial Intelligence tools that support software engineering with Clojure
4 |
5 | !!! QUOTE - "John Stevenson, Practical.li"
6 | Future engineering teams will use highly effective Artificial Intelligence tools to make software development more effecive and rewarding. How much of that future has already arrived?
7 |
8 | [:fontawesome-solid-book-open: Why Clojure - Concepts of the Clojure Language](introduction/concepts/index.md){.md-button}
9 |
10 |
11 | ## Clojure REPL Driven Development
12 |
13 | {loading=lazy}
14 |
15 | Adopting AI tools should complement the highly effective feedback from a REPL connected editor.
16 |
17 | ### Clojure Language
18 |
19 | Clojure programming language has a strong dynamic type system and a [:fontawesome-solid-book-open: simple syntax](introduction/clojure-in-15-minutes/) that is a joy to work with. Immutable values and a pragmatic approach to pure functional programming makes it easier to create simple and highly maintainable systems. A [:fontawesome-solid-book-open: specification library](clojure-spec/) ensures values are of the correct shape, especially valuable when receiving data from outside of Clojure.
20 |
21 | Clojure has an open source license and a large number of open source libraries and tools. Simple host interoperability allows a even more libraries to be leveraged.
22 |
23 | !!! QUOTE "Adrian Cockcroft - as Cloud Architect at Netflix"
24 | The most productive programmers I know are writing everything in Clojure ... producing ridiculously sophisticated things in a very short time. And that programmer productivity matters.
25 |
26 | [:fontawesome-solid-book-open: Clojure REPL Workflow overview](introduction/repl-workflow.md){.md-button}
27 | [:fontawesome-solid-book-open: Clojure REPL](/clojure/clojure-cli/repl/){.md-button}
28 |
29 |
30 | ## Practicalli Resources
31 |
32 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config - additional tools via aliases](/clojure/clojure-cli/practicalli-config/){target=_blank .md-button}
33 |
34 | [:fontawesome-solid-book-open: Clojure Aware Editors](/clojure/clojure-editors){target=_blank .md-button}
35 | [:fontawesome-brands-youtube: Practicalli YouTube channel](https://youtube.co/practicalli){target=_blank .md-button}
36 |
37 |
38 | ## Navigate the book
39 |
40 | Use the mouse or built-in key bindings to navigate the pages of the book
41 |
42 | - ++p++ , ++comma++ : go to previous page
43 | - ++n++ , ++period++ : go to next page
44 |
45 | Use the search box to quickly find a specific topic
46 |
47 | - ++f++ , ++s++ , ++slash++ : open search dialog
48 | - ++arrow-down++ , ++arrow-up++ : select next / previous result
49 | - ++esc++ , ++tab++ : close search dialog
50 | - ++enter++ : follow selected result
51 |
52 |
53 | ## Sponsor Practicalli
54 |
55 | [{ align=left loading=lazy }](https://github.com/sponsors/practicalli-johnny/)
56 |
57 | All sponsorship funds are used to support the continued development of [:fontawesome-solid-book-open: Practicalli series of books and videos](https://practical.li/){target=_blank}, although most work is done at personal cost and time.
58 |
59 | Thanks to [:globe_with_meridians: Cognitect](https://www.cognitect.com/){target=_blank}, [:globe_with_meridians: Nubank](https://nubank.com.br/){target=_blank} and a wide range of other [:fontawesome-brands-github: sponsors](https://github.com/sponsors/practicalli-johnny#sponsors){target=_blank} from the Clojure community for your continued support
60 |
61 |
62 | ## Creative commons license
63 |
64 |
65 |
66 | This work is licensed under a Creative Commons Attribution 4.0 ShareAlike License (including images & stylesheets).
67 |
52 |
53 | This work is licensed under a Creative Commons Attribution 4.0 ShareAlike License (including images & stylesheets).
54 |
55 |
56 | ## Contributing
57 |
58 | Issues and pull requests are most welcome although it is the maintainers discression as to if they are applicable. Please detail issues as much as you can. Pull requests are simpler to work with when they are specific to a page or at most a section. The smaller the change the quicker it is to review and merge.
59 |
60 | Please read the [detailed Practicalli contributing page](https://practical.li/contributing/) before raising an issue or pull request to avoid disapointment.
61 |
62 | * [Current Issues](https://github.com/practicalli/clojure/issues)
63 | * [Current pull requests](https://github.com/practicalli/clojure/pulls)
64 |
65 | [Practicalli Clojure CLI Config](clojure/clojure-cli/practicalli-config.md) provides a user level configuration providing aliases for community tools used throughout this guide. Issues and pull requests can also be made via its GitHub repository.
66 |
67 | By submitting content ideas and corrections you are agreeing they can be used in any work by Practicalli under the [Creative Commons Attribution ShareAlike 4.0 International license](https://creativecommons.org/licenses/by-sa/4.0/). Attribution will be detailed via [GitHub contributors](https://github.com/practicalli/clojure/graphs/contributors).
68 |
69 | ## Sponsor Practicalli
70 |
71 | [](https://github.com/sponsors/practicalli-johnny/)
72 |
73 | All sponsorship funds are used to support the continued development of [Practicalli series of books and videos](https://practical.li/), although most work is done at personal cost and time.
74 |
75 | Thanks to [Cognitect](https://www.cognitect.com/), [Nubank](https://nubank.com.br/) and a wide range of other [sponsors](https://github.com/sponsors/practicalli-johnny#sponsors) for your continued support
76 |
77 |
78 | ## Star History
79 |
80 | [](https://star-history.com/#practicalli/clojure&Date)
81 |
82 |
83 | ## GitHub Actions
84 |
85 | The megalinter GitHub actions will run when a pull request is created,checking basic markdown syntax.
86 |
87 | A review of the change will be carried out by the Practicalli team and the PR merged if the change is acceptable.
88 |
89 | The Publish Book GitHub action will run when PR's are merged into main (or the Practicalli team pushes changes to the default branch).
90 |
91 | Publish book workflow installs Material for MkDocs version 9
92 |
93 |
94 | ## Local development
95 |
96 | Install mkdocs version 9 using the Python pip package manager
97 |
98 | ```shell
99 | pip install mkdocs-material=="9.5"
100 | ```
101 |
102 | Install the plugins used by the Practicalli site using Pip (these are also installed in the GitHub Action workflow)
103 |
104 | ```shell
105 | pip3 install mkdocs-material mkdocs-callouts mkdocs-glightbox mkdocs-git-revision-date-localized-plugin mkdocs-redirects pillow cairosvg
106 | ```
107 |
108 | > pillow and cairosvg python packages are required for [Social Cards](https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/)
109 |
110 | Fork the GitHub repository and clone that fork to your computer,
111 |
112 | ```shell
113 | git clone https://github.com//.git
114 | ```
115 |
116 | Run a local server from the root of the cloned project
117 |
118 | ```shell
119 | make docs
120 | ```
121 |
122 | The website will open at
123 |
124 | If making smaller changes, then only rebuild the content that changes, speeding up the local development process
125 |
126 | ```shell
127 | make docs-changed
128 | ```
129 |
130 | > NOTE: navigation changes may not be correctly reflected without reloading the page in the web browser or carrying out a full `make docs` build
131 |
--------------------------------------------------------------------------------
/docs/reference/clojure-cli/jvm-options.md:
--------------------------------------------------------------------------------
1 | # Reference: Clojure CLI JVM Options
2 |
3 | !!! HINT "`JDK_JAVA_OPTIONS` Environment Variable"
4 | `JDK_JAVA_OPTIONS` is the official Environment Variable for setting options when calling `java`, `javac` and other Java commands to start running a Java Virtual Machine (Java version 9 onward).
5 |
6 | [Java Virtual Machine options can be passed using the Clojure CLI](https://clojure.org/reference/deps_and_cli#_prepare_jvm_environment), either via the `-J` command line flag or `:jvm-opts` in a `deps.edn` alias.
7 |
8 |
9 |
10 | !!! HINT "Java Virtual Machine configuration and reporting"
11 | [Java Virtual Machine section](/reference/jvm/index.md) covers commonly used options, reporting JVM metrics and optimisation of the JVM process.
12 |
13 | ## Clojure CLI command line options
14 |
15 | Clojure CLI `-J` flag passes configuration options to the JVM. When there are multiple, each must be prefixed with `-J`.
16 |
17 | ```shell
18 | clojure -J-XX:+UnlockDiagnosticVMOptions -J‑XX:NativeMemoryTracking=summary -J‑XX:+PrintNMTStatistics
19 | ```
20 |
21 | ## Clojure CLI deps.edn configuration
22 |
23 | `:jvm-opts` key in an alias adds JVM options to Clojure CLI deps.edn configuration. The `:jvm-opts` key has a value that is a collection of string JVM options `["-Xms2048m" "-Xmx4096"]`
24 |
25 | Alias to set a large heap size
26 |
27 | ```clojure
28 | :jvm/heap-max-2g {:jvm-opts ["-Xmx2G"]}
29 | ```
30 |
31 | Report a full breakdown of the HotSpot JVM’s memory usage upon exit using the following option combination:
32 |
33 | ```clojure
34 | :jvm/report {:jvm-opts ["-XX:+UnlockDiagnosticVMOptions"
35 | "‑XX:NativeMemoryTracking=summary"
36 | "‑XX:+PrintNMTStatistics"]}
37 | ```
38 |
39 | Add a Java module
40 |
41 | ```clojure
42 | :jvm/xml-bind {:jvm-opts ["–add-modules java.xml.bind"]}
43 | ```
44 |
45 | Ignoring unrecognised options
46 |
47 | ```clojure
48 | :jvm-opts ["-XX:+IgnoreUnrecognizedVMOptions"]
49 | ```
50 |
51 | The aliases can be used with the Clojure CLI execution options: `-A` (for built-in REPL invocation), `-X` and `-T` (for clojure.exec function execution), or `-M` (for clojure.main execution).
52 |
53 | > `-J` JVM options specified on the command line are concatenated after the alias options
54 |
55 |
56 |
57 |
58 |
59 | ## Calling A Clojure Uberjar
60 |
61 | JVM options must be specified when calling an uberjar with the `java` command, `:jvm-opts` in the project `deps.edn` are not used with the `java` command
62 |
63 | ```
64 | java -jar project-uberjar.jar -J...
65 | ```
66 |
67 | !!! HINT "Use `JDK_JAVA_OPTIONS` to define JVM options"
68 | `JDK_JAVA_OPTIONS` environment variable is used to define options that are used whenever the `java` command is called, greatly simplifying `java` commands.
69 |
70 | The `JDK_JAVA_OPTIONS` environment variable can be used with deployment systems and passed to container environments to simplify adjustment of resources used by the JVM process.
71 |
72 | ## Clojure related JVM options
73 |
74 | Specify options or system properties to set up the Clojure service
75 |
76 | `-Dclojure.compiler.disable-locals-clearing=true` - make more info available to debuggers
77 |
78 | `-Dclojure.main.report=stderr` - print stack traces to standard error instead of saving to file, useful if process failing on startup
79 |
80 | `-Dclojure.spec.skip-macros=false` - skip spec checks against macro forms
81 |
82 | ### Memory Management
83 |
84 | `-XX:CompressedClassSpaceSize=3G` - prevent a specific type of OOMs
85 |
86 | `-XX:MaxJavaStackTraceDepth=1000000` - prevents trivial Stack Overflow errors
87 |
88 | `-Xmx24G` - set high maximum heap, preventing certain types of Out Of Memory errors (ideally high memory usage should be profiled if cause not known)
89 |
90 | `-Xss6144k` - increase stack size x6 to prevent Stack Overflow errors
91 |
92 | > The current default can be found with `java -XX:+PrintFlagsFinal -version 2>/dev/null | grep "intx ThreadStackSize"`
93 |
94 | `-Xms6G` - Set minimum memory that is equal or greater than memory used by a running REPL, to improve performance
95 |
96 | `-Xmx1G` - limit maximum heap allocation so a process can never use more memory, useful for environments with limited memory resources
97 |
98 | ```clojure
99 | :jvm/mem-max1g {:jvm-opts ["-Xmx1G"]}
100 | ```
101 |
102 | ## Container Memory Management
103 |
104 | `JDK_JAVA_OPTIONS` environment variable should be used for setting JVM options within a container or in the provisioning service (e.g. Kubernettes / Argo CD) that deploys containers.
105 |
106 | Use JVM options that optimise running in a container
107 |
108 | * `-XshowSettings:system` to output the resources the JVM believes it has access too, a very simple diagnostic tool to include
109 |
110 | * `-XX:+UseContainerSupport` instruct the JVM that it is running in a container environment, disabling the checks the JVM would otherwise carry out to determine if it was running in a container. Can save a very small amount of start up time, though mainly used to ensure the JVM knows its in a container.
111 |
112 | * `-XX:MaxRAMPercentage=90` to set a relative maximum percentage of heap to use, based on the memory available from the host, e.g. `-XX:MaxRAMPercentage=80` will use a heap size of 80% of the available host memory
113 |
114 | ### Dockerfile example with JDK_JAVA_OPTIONS environment variable
115 |
116 | In this `Dockerfile` excerpt the `JDK_JAVA_OPTIONS` environment variable is used to print out the resources the JVM believes it has access to at startup. The JVM is instructed that it is running in a container environment and should use a maximum 90% heap size of the hosts memory resource.
117 |
118 | ```yaml
119 | ENV JDK_JAVA_OPTIONS "-XshowSettings:system -XX:+UseContainerSupport -XX:MaxRAMPercentage=90"
120 | CMD ["java", "-jar", "/opt/practicalli-service.jar"]
121 | ```
122 |
123 | ## Low latency systems
124 |
125 | For systems that require very low latency, use the Z Garbage collector
126 |
127 | ```
128 | "-XX:+UnlockExperimentalVMOptions -XX:+UseZGC"
129 | ```
130 |
131 | ### Stack traces
132 |
133 | `-XX:+TieredCompilation` - enable tiered compilation to support accurate bench-marking (increases startup time)
134 |
135 | `-XX:-OmitStackTraceInFastThrow` - don't elide stack traces
136 |
137 | ### Startup options
138 |
139 | `-Xverify:none` option reduces startup time of the JVM by skipping verification process
140 |
141 | ```shell
142 | "-Xverify:none"
143 | ```
144 |
145 | > The verification process is a valuable check, especially for code that has not been run before. So the code should be run through the verification process before deploying to production.
146 |
147 | ### Benchmark options
148 |
149 | Enable various optimizations, for guaranteeing accurate benchmarking (at the cost of slower startup):
150 |
151 | `"-server"`
152 |
153 | ### Graphical UI related options
154 |
155 | `-Djava.awt.headless=true` - disable all UI features for disabling the clipboard for personal security:
156 |
157 | `-Dapple.awt.UIElement=true` - remove icon from the MacOSX Dock
158 |
159 | `-Dclash.dev.expound=true` - ?
160 |
161 | ### Garbage Collection
162 |
163 | Setup GC with short STW pauses which can be relevant for very high web server workloads
164 |
165 | ```clojure
166 | :jvm/g1gc
167 | {:jvm-opts ["-XX:+UseG1GC"
168 | "-XX:MaxGCPauseMillis=200"
169 | "-XX:ParallelGCThreads=20"
170 | "-XX:ConcGCThreads=5"
171 | "-XX:InitiatingHeapOccupancyPercent=70"]}
172 | ```
173 |
174 | * Source: [Tuning Garbage Collection with Oracle JDK](https://docs.oracle.com/cd/E40972_01/doc.70/e40973/cnf_jvmgc.htm#autoId2)
175 |
176 | ### View JVM options of a running JVM process
177 |
178 | Use a JMX client, e.g. [VisualVM](https://visualvm.github.io/){target=_blank}
179 |
180 | `jcmd pid VM.system_properties` or `jcmd pid VM.flags` using `jcmd -l` to get the pid of the JVM process
181 |
182 | On Linux `ps -ef | grep java` which includes the options to run the JVM process, `ps -auxww` to show long arguments
183 |
184 | [Getting the parameters of a running JVM](https://stackoverflow.com/questions/5317152/getting-the-parameters-of-a-running-jvm){target=_blank .md-button}
185 |
186 | ### References
187 |
188 | [JVM Options cheatsheet - JRebel](https://www.jrebel.com/blog/jvm-options-cheat-sheet){target=_blank .md-button}
189 |
--------------------------------------------------------------------------------
/.github/config/markdown-lint.jsonc:
--------------------------------------------------------------------------------
1 | // Example markdownlint configuration with all properties set to their default value
2 | {
3 |
4 | // Default state for all rules
5 | "default": true,
6 |
7 | // Path to configuration file to extend
8 | "extends": null,
9 |
10 | // MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time
11 | "MD001": true,
12 |
13 | // MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading
14 | "MD002": {
15 | // Heading level
16 | "level": 1
17 | },
18 |
19 | // MD003/heading-style/header-style - Heading style
20 | "MD003": {
21 | // Heading style
22 | "style": "consistent"
23 | },
24 |
25 | // MD004/ul-style - Unordered list style
26 | "MD004": {
27 | // List style
28 | "style": "consistent"
29 | },
30 |
31 | // MD005/list-indent - Inconsistent indentation for list items at the same level
32 | "MD005": true,
33 |
34 | // MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line
35 | "MD006": true,
36 |
37 | // MD007/ul-indent - Unordered list indentation
38 | "MD007": {
39 | // Spaces for indent
40 | "indent": 2,
41 | // Whether to indent the first level of the list
42 | "start_indented": false,
43 | // Spaces for first level indent (when start_indented is set)
44 | "start_indent": 2
45 | },
46 |
47 | // MD009/no-trailing-spaces - Trailing spaces
48 | "MD009": {
49 | // Spaces for line break
50 | "br_spaces": 2,
51 | // Allow spaces for empty lines in list items
52 | "list_item_empty_lines": false,
53 | // Include unnecessary breaks
54 | "strict": true
55 | },
56 |
57 | // MD010/no-hard-tabs - Hard tabs
58 | "MD010": {
59 | // Include code blocks
60 | "code_blocks": true,
61 | // Fenced code languages to ignore
62 | "ignore_code_languages": [],
63 | // Number of spaces for each hard tab
64 | "spaces_per_tab": 1
65 | },
66 |
67 | // MD011/no-reversed-links - Reversed link syntax
68 | "MD011": true,
69 |
70 | // MD012/no-multiple-blanks - Multiple consecutive blank lines
71 | "MD012": {
72 | // Consecutive blank lines
73 | "maximum": 2
74 | },
75 |
76 | // MD013/line-length - Line length
77 | "MD013": {
78 | // Number of characters
79 | "line_length": 420,
80 | // Number of characters for headings
81 | "heading_line_length": 90,
82 | // Number of characters for code blocks
83 | "code_block_line_length": 420,
84 | // Include code blocks
85 | "code_blocks": true,
86 | // Include tables
87 | "tables": true,
88 | // Include headings
89 | "headings": true,
90 | // Include headings
91 | "headers": true,
92 | // Strict length checking
93 | "strict": false,
94 | // Stern length checking
95 | "stern": true
96 | },
97 |
98 | // MD014/commands-show-output - Dollar signs used before commands without showing output
99 | "MD014": true,
100 |
101 | // MD018/no-missing-space-atx - No space after hash on atx style heading
102 | "MD018": true,
103 |
104 | // MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading
105 | "MD019": true,
106 |
107 | // MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading
108 | "MD020": true,
109 |
110 | // MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading
111 | "MD021": true,
112 |
113 | // MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
114 | "MD022": {
115 | // Blank lines above heading
116 | "lines_above": 1,
117 | // Blank lines below heading
118 | "lines_below": 1
119 | },
120 |
121 | // MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line
122 | "MD023": true,
123 |
124 | // MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
125 | "MD024": {
126 | // Only check sibling headings
127 | "allow_different_nesting": false,
128 | // Only check sibling headings
129 | "siblings_only": false
130 | },
131 |
132 | // MD025/single-title/single-h1 - Multiple top-level headings in the same document
133 | "MD025": {
134 | // Heading level
135 | "level": 1,
136 | // RegExp for matching title in front matter
137 | "front_matter_title": "^\\s*title\\s*[:=]"
138 | },
139 |
140 | // MD026/no-trailing-punctuation - Trailing punctuation in heading
141 | "MD026": {
142 | // Punctuation characters not allowed at end of headings
143 | "punctuation": ".,;:!。,;:!"
144 | },
145 |
146 | // MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol
147 | "MD027": true,
148 |
149 | // MD028/no-blanks-blockquote - Blank line inside blockquote
150 | "MD028": true,
151 |
152 | // MD029/ol-prefix - Ordered list item prefix
153 | "MD029": {
154 | // List style
155 | "style": "one_or_ordered"
156 | },
157 |
158 | // MD030/list-marker-space - Spaces after list markers
159 | "MD030": {
160 | // Spaces for single-line unordered list items
161 | "ul_single": 1,
162 | // Spaces for single-line ordered list items
163 | "ol_single": 1,
164 | // Spaces for multi-line unordered list items
165 | "ul_multi": 1,
166 | // Spaces for multi-line ordered list items
167 | "ol_multi": 1
168 | },
169 |
170 | // MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
171 | "MD031": {
172 | // Include list items
173 | "list_items": true
174 | },
175 |
176 | // MD032/blanks-around-lists - Lists should be surrounded by blank lines
177 | "MD032": true,
178 |
179 | // MD033/no-inline-html - Inline HTML
180 | "MD033": {
181 | // Allowed elements
182 | "allowed_elements": ["a", "iframe", "img", "p", "div"]
183 | },
184 |
185 | // MD034/no-bare-urls - Bare URL used
186 | "MD034": true,
187 |
188 | // MD035/hr-style - Horizontal rule style
189 | "MD035": {
190 | // Horizontal rule style
191 | "style": "consistent"
192 | },
193 |
194 | // MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
195 | "MD036": {
196 | // Punctuation characters
197 | "punctuation": ".,;:!?。,;:!?"
198 | },
199 |
200 | // MD037/no-space-in-emphasis - Spaces inside emphasis markers
201 | "MD037": true,
202 |
203 | // MD038/no-space-in-code - Spaces inside code span elements
204 | "MD038": true,
205 |
206 | // MD039/no-space-in-links - Spaces inside link text
207 | "MD039": true,
208 |
209 | // MD040/fenced-code-language - Fenced code blocks should have a language specified
210 | "MD040": {
211 | // List of languages
212 | "allowed_languages": [],
213 | // Require language only
214 | "language_only": false
215 | },
216 |
217 | // MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
218 | "MD041": {
219 | // Heading level
220 | "level": 1,
221 | // RegExp for matching title in front matter
222 | "front_matter_title": "^\\s*title\\s*[:=]"
223 | },
224 |
225 | // MD042/no-empty-links - No empty links
226 | "MD042": true,
227 |
228 | // MD043/required-headings/required-headers - Required heading structure
229 | "MD043": {},
230 |
231 | // MD044/proper-names - Proper names should have the correct capitalization
232 | "MD044": {
233 | // List of proper names
234 | "names": [],
235 | // Include code blocks
236 | "code_blocks": true,
237 | // Include HTML elements
238 | "html_elements": true
239 | },
240 |
241 | // MD045/no-alt-text - Images should have alternate text (alt text)
242 | "MD045": true,
243 |
244 | // MD046/code-block-style - Code block style
245 | "MD046": {
246 | // Block style
247 | "style": "consistent"
248 | },
249 |
250 | // MD047/single-trailing-newline - Files should end with a single newline character
251 | "MD047": true,
252 |
253 | // MD048/code-fence-style - Code fence style
254 | "MD048": {
255 | // Code fence style
256 | "style": "consistent"
257 | },
258 |
259 | // MD049/emphasis-style - Emphasis style should be consistent
260 | "MD049": {
261 | // Emphasis style should be consistent
262 | "style": "consistent"
263 | },
264 |
265 | // MD050/strong-style - Strong style should be consistent
266 | "MD050": {
267 | // Strong style should be consistent
268 | "style": "consistent"
269 | },
270 |
271 | // MD051/link-fragments - Link fragments should be valid
272 | "MD051": true,
273 |
274 | // MD052/reference-links-images - Reference links and images should use a label that is defined
275 | "MD052": true,
276 |
277 | // MD053/link-image-reference-definitions - Link and image reference definitions should be needed
278 | "MD053": {
279 | // Ignored definitions
280 | "ignored_definitions": ["//"]
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/docs/introduction/first-taste-of-clojure.md:
--------------------------------------------------------------------------------
1 | # Clojure Quick Reference
2 |
3 | The basic Clojure syntax and a few common functions you should probably learn first.
4 |
5 | The examples are editable (using an embedded REPL) so feel free to experiment and watch as the return value changes as you change the code. Reload the page if you want to reset all the code back to the starting point.
6 |
7 | [Install Clojure](/clojure-cli/install/) on your computer if you want to experiment even further.
8 |
9 | !!! HINT "Want to go deeper already?"
10 | Watch [the Clojure language video series by Brian Will](https://www.youtube.com/playlist?list=PLAC43CFB134E85266) for a detailed introduction to key parts of the language. Or discover Clojure core functions by completing challenges on [4Clojure.org](https://4clojure.oxal.org/) and then [watching how Practicalli solved them](https://www.youtube.com/playlist?list=PLpr9V-R8ZxiDB_KGrbliCsCUrmcBvdW16).
11 |
12 | ## Calling functions
13 |
14 | The first element in a list, `()`, is a call to a function. Any other elements are passed to the function as arguments. The examples show how to call functions with multiple arguments.
15 |
16 | ```clojure
17 | (+ 1 2)
18 | ```
19 |
20 | ```clojure
21 | (+ 3 (* 2 (- 7 2) 4) (/ 16 4))
22 | ```
23 |
24 | ```clojure
25 | (str "Clojure is " (- 2021 2007) " years old")
26 | ```
27 |
28 | ```clojure
29 | (inc 1)
30 | ```
31 |
32 | ```clojure
33 | (map inc [1 2 3 4 5])
34 | ```
35 |
36 | ```clojure
37 | (filter odd? (range 11))
38 | ```
39 |
40 | !!! Hint "Prefix notation and parens"
41 | Hugging code with `()` is a simple syntax to define the scope of code expressions. No additional `;`, `,` or spaces are required.
42 |
43 | Treating the first element of a list as a function call is referred to as prefix notation, which greatly simplifies Clojure syntax. Prefix notation makes mathematical expressions completely deterministic, eliminating the need for [operator precedence](https://en.wikipedia.org/wiki/Order_of_operations).
44 |
45 | ## Understanding functions
46 |
47 | `clojure.repl/doc` function returns the doc-string of the given function. A doc-string should be part of all public function definitions.
48 |
49 | Clojure editors should provide commands to view doc-strings and the ability to jump to function definitions to view their source code
50 |
51 | ```clojure
52 | (clojure.repl/ddoc doc)
53 | ```
54 |
55 | ## Modeling data with Collection types
56 |
57 | Clojure has 4 main collection types, all immutable (cannot change once created) and can contain any Clojure types.
58 |
59 | A list, `()`, used for calling functions and representing sequences. A linked list for sequential access.
60 |
61 | ```clojure
62 | (str "lists used mainly " (* 2 2) " " :code)
63 | ```
64 |
65 | A vector, `[]`, used for simple collections of values. An indexed data structure for random access
66 |
67 | ```clojure
68 | [0 "indexed" :array (* 2 2) "random-access" 4 :data]
69 | ```
70 |
71 | A map, `{}`, use for descriptive data collections. An associative data structure for value lookup by unique keys (also known as a dictionary).
72 |
73 | ```clojure
74 | { :hash-map :associative-collection :pairs {:key "value"} :aka "dictionary"}
75 | ```
76 |
77 | A set, `#{}`, use as a unique set of values. Sets are used to test if a value is contained within, i.e. predicates.
78 |
79 | ```clojure
80 | #{1 2 3 4 "unique" "set" "of" "values" "unordered" (* 3 9)}
81 | ```
82 |
83 | !!! Hint "Persistent data types"
84 | Values are immutable so when a function changes a value a new immutable value is created. When creating new collection values, unchanged values are shared with the original collection. This [sharing model is called persistent data types]( /data-structures/shared-memory.md) and enables immutable data to be used efficiently.
85 |
86 | ## Using data structures
87 |
88 | Using the `map` and `inc` function, increment all the numbers in a vector
89 |
90 | ```clojure
91 | (map inc [1 2 3 4 5])
92 | ```
93 |
94 | The above `map` function is roughly equivalent to the following expression
95 |
96 | ```clojure
97 | (conj [] (inc 1) (inc 2) (inc 3) (inc 4) (inc 5))
98 | ```
99 |
100 | The `conj` function creates a new collection by combining a collection and one or more values.
101 |
102 | `map` `reduce` `filter` are common functions for iterating through a collection / sequence of values
103 |
104 | ```clojure
105 | (map * [1 3 5 8 13 21] [3 5 8 13 21 34])
106 | ```
107 |
108 | ```clojure
109 | (filter even? [1 3 5 8 13 21 34])
110 | ```
111 |
112 | ```clojure
113 | (reduce + [31 28 30 31 30 31])
114 | ```
115 |
116 | ```clojure
117 | (empty? [])
118 | ```
119 |
120 | !!! HINT "Many Clojure core functions for collections"
121 | `map`, `reduce`, `apply`, `filter`, `remove` are just a few examples of Clojure core functions that work with data structures.
122 |
123 | ## Defining custom functions
124 |
125 | ```clojure
126 | (defn square-of
127 | "Calculates the square of a given number"
128 | [number]
129 | (* number number))
130 |
131 | (square-of 9)
132 | ```
133 |
134 | Function definitions can also be used within other expressions, useful for mapping custom functions over a collection
135 |
136 | ```clojure
137 | (map (fn [number] (* number number)) [1 2 3 4 5])
138 | ```
139 |
140 | ## Defining local names
141 |
142 | Use the `let` function as a simple way to experiment with code designs
143 |
144 | ```clojure
145 | (let [data (range 24 188)
146 | total (reduce + data)
147 | values (count data)]
148 | (str "Average value: " (/ total values)))
149 | ```
150 |
151 | Define local names to remove duplication in function definitions, or to simplify algorithms
152 |
153 | ```clojure
154 | (defn square-of
155 | "Calculates the square of a given number"
156 | [number]
157 | (* number number))
158 |
159 | (square-of 9)
160 | ```
161 |
162 | ## Defining names for values (vars)
163 |
164 | A name bound to a value can be used to represent that value throughout the code. Names can be bound to simple values (numbers, strings, etc.), collections or even function calls.
165 |
166 | `def` binds a name to a value with the scope of the current namespace. `def` is useful for data that is passed to multiple functions within a namespace.
167 |
168 | Evaluating a name will return the value it is bound to.
169 |
170 | ```clojure
171 | (def public-health-data
172 | [{:date "2020-01-01" :confirmed-cases 23814 :recovery-percent 15}
173 | {:date "2020-01-02" :confirmed-cases 24329 :recovery-percent 14}
174 | {:date "2020-01-03" :confirmed-cases 25057 :recovery-percent 12}])
175 |
176 | public-health-data
177 | ```
178 |
179 | !!! HINT "def for shared values, let for locally scoped values"
180 | `let` function is used to bind names to values locally, such as within a function definition. Names bound with `def` have namespace scope so can be used with any code in that namespace.
181 |
182 | ## Iterating over collections
183 |
184 | `map` iterates a function over a collection of values, returning a new collection of values
185 |
186 | ```clojure
187 | (map inc (range 20))
188 | ```
189 |
190 | `reduce` iterates a function over the values of a collection to produce a new result
191 |
192 | ```
193 | (reduce + (range 101))
194 | ```
195 |
196 | Reducing functions are function definitions used by the `reduce` function over a collection
197 |
198 | ```clojure
199 | (reduce (fn [[numerator denominator] accumulator]
200 | [(+ numerator accumulator)
201 | (inc denominator)])
202 | [0 0]
203 | (range 1 20))
204 | ```
205 |
206 | Functions can call themselves to iterate over a collection. Using a lazy sequence means only the required numbers are generated, ensuring efficiency of operation and making the function usable in many different scenarios.
207 |
208 | ```clojure
209 | (defn fibonacci-sequence
210 | [current-number next-number]
211 | (lazy-seq
212 | (cons current-number
213 | (fibonacci-sequence next-number (+ current-number next-number)))))
214 |
215 | (take 10 (fibonacci-sequence 0 1))
216 | ```
217 |
218 | ## Host Interoperability
219 |
220 | The REPL in this web page is running inside a JavaScript engine, so JavaScript functions can be used from within ClojureScript code (ClojureScript is Clojure that runs in JavaScript environments).
221 |
222 | In the box below, replace `()` with `(js/alert "I am a pop-up alert")`
223 |
224 | ```clojure
225 | ()
226 | ```
227 |
228 | !!! HINT "Java libraries in Clojure"
229 | [java.lang library](https://docs.oracle.com/javase/8/docs/api/java/lang/compact2-package-summary.html) is available in Clojure by default and many other Java methods can be included by using their full name, e.g. `(java.lang.Date.)` will return the current date.
230 |
231 | ## Next steps
232 |
233 | [Install Clojure](/clojure-cli/install/) on your computer if you want to experiment even further or keep on reading more about Clojure.
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
--------------------------------------------------------------------------------
/docs/install/clojure-cli.md:
--------------------------------------------------------------------------------
1 | # Install Clojure CLI
2 |
3 | {align=right loading=lazy style="height:150px;width:150px"}
4 |
5 | Clojure CLI is a command line tool for running a Clojure REPL, project or tool.
6 |
7 | Clojure CLI automatically downloads required library dependencies, including the Clojure Standard library.
8 |
9 | ??? INFO "Clojure distributed as a library"
10 | Clojure is distributed as a library (`.jar` Java ARchive) via Maven Central.
11 |
12 | A `deps.edn` file specifies the version of Clojure to be used with a project.
13 |
14 | ```clojure
15 | :deps {org.clojure/clojure {:mvn/version "1.12.0"}}
16 | ```
17 |
18 | The Clojure CLI tool provides a default Clojure library version if not specified in the project or user `deps.edn` files.
19 |
20 | [Clojure releases](https://clojure.org/releases/downloads){target=_blank .md-button}
21 |
22 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](#practicalli-clojure-cli-config) extends the Clojure CLI with a range of development tools as well as configuration for Clojure LSP and cljstyle code format tool.
23 |
24 | === "Linux"
25 |
26 | Use the Linux script installer from [Clojure.org - Getting Started](https://clojure.org/guides/getting_started#_installation_on_linux) to install or update to the latest stable release
27 |
28 | ```shell
29 | curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh && \
30 | chmod +x linux-install.sh && \
31 | sudo ./linux-install.sh
32 | ```
33 |
34 | The installation creates `/usr/local/bin/clojure`, `/usr/local/bin/clj` wrapper and `/usr/local/lib/clojure` directory.
35 |
36 | ??? HINT "Use alternative location - unattended install"
37 | `--prefix` option specifies an alternative lolcation for the Clojure CLI install.
38 |
39 | When permissions are not available or for automating the install without password prompt, use a local user specific install, e.g.
40 | ```shell
41 | curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh && \
42 | chmod +x linux-install.sh && \
43 | ./linux-install.sh --prefix $HOME/.local/
44 | ```
45 |
46 | ??? INFO "Include version number for specific release"
47 | Each Clojure CLI version is a number that represents the version of Clojure used and the build version of the Clojure CLI tool, e.g. `1.11.1.1413`.
48 |
49 | [Clojure CLI Releases page](https://clojure.org/releases/tools){target=_blank .md-button}
50 |
51 | Include the version in the script name for repeatable environments, e.g. in Dockerfile configuration and Continuous Integraion workflows.
52 | ```shell title="Clojure CLI install specific version"
53 | curl -L -O https://github.com/clojure/brew-install/releases/1.11.1.1413/download/linux-install.sh && \
54 | chmod +x linux-install-1.11.1.1413.sh
55 | sudo ./linux-install-1.11.1.1413.sh
56 | ```
57 |
58 | === "Homebrew"
59 |
60 | Practically recommends setting `XDG_CONFIG_HOME` to the `.config` directory, to avoid creating another dot directory in the root of the user account. Add the following to `~/.bashrc` for the bash shell or `~/.zshenv` for Zsh.
61 | ```
62 | export XDG_CONFIG_HOME="$HOME/.config"
63 | ```
64 |
65 | Use the Homebrew command with the [clojure/tools tap](https://github.com/clojure/homebrew-tools), as defined in the [Clojure.org Getting started guide](https://clojure.org/guides/getting_started#_installation_on_linux)
66 |
67 | ```shell
68 | brew install clojure/tools/clojure
69 | ```
70 |
71 | Use Homebrew to update an install of Clojure CLI to the latest release
72 | ```shell
73 | brew upgrade clojure/tools/clojure
74 | ```
75 |
76 | > [Homebrew on Linux or Windows with WSL](https://docs.brew.sh/Homebrew-on-Linux)
77 |
78 | === "Windows"
79 |
80 | For Windows 10 use [Windows Subsystem for Linux and Windows Terminal are recommended](https://conan.is/blogging/clojure-on-windows.html) if you have administrative privileges and are comfortable using a Unix system on the command line.
81 |
82 | Alternatively install [scoop.sh](https://scoop.sh/), a command line installer for windows. [Powershell 5](https://aka.ms/wmf5download) or greater is required. Follow the [scoop-clojure getting started guide](https://github.com/littleli/scoop-clojure/wiki/Getting-started), summarized here:
83 |
84 | Open "Windows PowerShell" and enter the following commands to configure the shell:
85 |
86 | ```shell
87 | iwr -useb get.scoop.sh | iex
88 | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
89 | ```
90 | Then in the same PowerShell window, install the Clojure related tools using the following commands:
91 |
92 | ```shell
93 | scoop bucket add extras
94 | scoop bucket add java
95 | scoop bucket add scoop-clojure https://github.com/littleli/scoop-clojure
96 | scoop install git 7zip pshazz temurin-lts-jdk clj-deps leiningen clj-kondo vscode coreutils windows-terminal
97 | ```
98 |
99 | > Reference: [Clojure CLI Install - Clojure.org](https://clojure.org/guides/install_clojure){target=_blank} - official guide
100 |
101 |
102 | ## Practicalli Clojure CLI Config
103 |
104 | Add a wide range of community tools to extend the capabilities of Clojure CLI via the aliases.
105 |
106 | Clone [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](https://github.com/practicalli/clojure-cli-config){target=_blank} GitHub repository, first removing the `$XDG_CONFIG_HOME/clojure` and `$HOME/.clojure` directory if they exist.
107 |
108 | ??? INFO "User configuration locations"
109 | If `XDG_CONFIG_HOME` environment variable is set, then the user configuration is `$XDG_CONFIG_HOME/clojure/deps.edn`
110 |
111 | Otherwise the user configuration is `$HOME/.clojure/deps.edn`.
112 |
113 | `CLJ_CONFIG` environment variable can be used to set a custom location, overriding any other location.
114 |
115 | !!! HINT "Practicalli recommends FreeDesktop XDG location"
116 | Practically recommends setting `XDG_CONFIG_HOME` to the `.config` directory to simplify versioning of configuration.
117 |
118 | Configure `~/.bashrc` for the bash shell
119 | ```shell title="Bash .bashrc file"
120 | export XDG_CONFIG_HOME="$HOME/.config"
121 | ```
122 |
123 | Configure `~/.zshenv` for Zsh
124 | ```shell
125 | # Set XDG_CONFIG_HOME for clean management of configuration files
126 | export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}"
127 | export XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}"
128 | export XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}"
129 | export ZDOTDIR="${ZDOTDIR:=$XDG_CONFIG_HOME/zsh}"
130 | ```
131 |
132 | === "Free Desktop XDG CONFIG"
133 | If `XDG_CONFIG_HOME` environment variable is set, clone the repository to `$XDG_CONFIG_HOME/clojure`
134 |
135 | Via SSH
136 | ```shell
137 | git clone git@github.com:practicalli/clojure-cli-config.git $XDG_CONFIG_HOME/clojure
138 | ```
139 |
140 | Via HTTPS:
141 | ```shell
142 | git clone https://github.com/practicalli/clojure-cli-config.git $XDG_CONFIG_HOME/clojure
143 | ```
144 |
145 | === "Classic Config"
146 | Clojure CLI will look for its configuration in `$HOME/.clojure` directory if `$XDG_CONFIG_HOME` and `CLJ_CONFIG` environment variables not set.
147 | Via SSH
148 | ```shell
149 | git clone git@github.com:practicalli/clojure-cli-config.git $HOME/.clojure
150 | ```
151 |
152 | Via HTTPS
153 | ```shell
154 | git clone https://github.com/practicalli/clojure-cli-config.git $HOME/.clojure
155 | ```
156 |
157 | ## Check Configuration
158 |
159 | The `--version` flag shows prints the current version of Clojure CLI.
160 |
161 | ```shell
162 | clojure --version
163 | ```
164 |
165 | {loading=lazy}
166 |
167 |
168 | > NOTE: `clojure -Sdescribe` is now deprecated and will be removed. Use tools.build API if more details are required about the configuration of Clojure CLI.
169 |
170 |
171 | ## Optional rlwrap readline
172 |
173 | The `rlwrap` binary is a basic readline tool that provides a history of commands entered into a terminal UI when running a Clojure REPL with the `clj` wrapper script.
174 |
175 | Pressing the ++arrow-up++ and ++arrow-down++ keys will scroll through the code previously entered in the REPL.
176 |
177 | `rlwrap` is available with most Linux systems. Look for install instructions by searching for rlwrap in a web browser or build from source from the [:globe_with_meridians: rlwrap GitHub repository](https://github.com/hanslub42/rlwrap).
178 |
179 | !!! HINT "Use Rebel Readline for a rich terminal UI experience"
180 | [:fontawesome-solid-book-open: rebel readline](/clojure/clojure-cli/repl/) is an advanced readline tool providing auto-completion, documentation, signature help and multi-line editing, all within a terminal UI
181 |
182 | Rebel is a much richer experience than the `clj` wrapper with `rlwrap`. Rebel should not be used with `clj`.
183 |
184 | Rebel Readline is part of the [Practicalli Clojure CLI config](#practicalli-clojure-cli-config).
185 |
186 | ```shell
187 | clojure -M:repl/rebel
188 | ```
189 |
--------------------------------------------------------------------------------
/docs/introduction/study-guide.md:
--------------------------------------------------------------------------------
1 | # Clojure Study guide
2 |
3 | A suggested study guide for those new to functional programming with Clojure.
4 |
5 | > #### Hint::MeetAMentor Clojure Study Group - 3rd November 2018 onwards
6 | > [MeetAMentor Community](http://meetamentor.co.uk/) is hosting a Clojure Study group starting 3rd November, using this study guide. Sign up via the[ MaM Study Group form](http://bcrw.typeform.com/to/VkChxI).
7 | >
8 | > All broadcasts are available via the [jr0cket YouTube channel](https://www.youtube.com/channel/UCelY2sic3hsIiSeAhWt640g)
9 |
10 | ## Week1: Overview of Clojure
11 |
12 | [](https://www.youtube.com/watch?v=MZcuL4lRw5E&index=1&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
13 |
14 | Briefly discuss what the Clojure programming languages is good for, some example companies using Clojure and give suggestions on how to start learning Clojure.
15 |
16 | Cover [the syntax and a few common functions](https://repl.it/@jr0cket/Clojure-In-15-minutes),
17 |
18 | ### Homework
19 | * Simple Clojure exercises from [ClojureBridge London workshop](https://clojurebridgelondon.github.io/workshop/simple-values/)
20 | * Understand the principles behind the design of the Clojure language by watching [Simple made Easy](https://www.infoq.com/presentations/Simple-Made-Easy) by Rich Hickey (the author of Clojure)
21 |
22 | ## Week2: REPL Driven Development overview and ClojureBridge London exercise review
23 |
24 | [](https://www.youtube.com/watch?v=y5TAHwZc1JE&index=2&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
25 |
26 | Discuss REPL Driven Development ([my blog post on the topic](http://jr0cket.co.uk/2018/11/REPL-driven-development-with-Clojure.html))
27 |
28 | Review the ClojureBridge London exercises homework - [Github Gist](https://gist.github.com/jr0cket/6551c8ef224a63e891bc61665471bcd1) - [Repl.it project](https://repl.it/@jr0cket/ClojureBridge-London-exercises)
29 |
30 | ### Homework
31 | * [4Clojure exercises](http://www.4clojure.com/)
32 |
33 |
34 | ## Week 3: Using the REPL and Structural editing
35 |
36 | [](https://www.youtube.com/watch?v=PmSPKvlJk74&index=3&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
37 |
38 | #### Part 1: Structural Editing
39 | The first part of the video covers Structural Editing for Clojure development and the concept of code as symbolic expressions. Demonstrating the basic of structural editing using Spacemacs
40 |
41 | Briefly covers the different editors that give a good Clojure experience.
42 | http://practicalli.github.io/clojure/development-tools/
43 |
44 | Visit http://practicalli.github.io/spacemacs to learn more about Clojure development with Spacemacs (Emacs and CIDER).
45 |
46 | #### Part 2: 4Clojure exercises 1 to 15
47 | At [34.50 onwards we discuss solving the first fifteen challenges of 4Clojure](https://www.youtube.com/watch?v=PmSPKvlJk74&feature=youtu.be&t=2090), a website that gives you Clojure code snippets to complete. Entering the missing code and the website will run the code and tests, showing if you got the answer correct.
48 |
49 | [Solving the first 15 challenges - 34.50 onwards](https://www.youtube.com/watch?v=PmSPKvlJk74&feature=youtu.be&t=2090)
50 |
51 | ### Homework
52 | * [4Clojure exercises](http://www.4clojure.com/)
53 | * Install a Clojure development environment using these [install guides for several Clojure aware editors](https://practicalli.github.io/clojure/development-tools/)
54 |
55 |
56 | ## Clojure Loves Data: A 40 minute sessions at London Java Community conference
57 |
58 | [](https://www.youtube.com/watch?v=Ja63rOa2MFA)
59 |
60 | Live coding session covering the basics of using data structures (collections) in Clojure, mostly focusing on Vectors (arrays) and Maps (hash-map, dictionary).
61 | I will also attempt to broadcast live a lightning talk on how to broadcast live (because I like being quite meta some times).
62 |
63 |
64 |
65 | ## Week 4: Clojure maps, ascii code generator and clojure test
66 |
67 | [](https://www.youtube.com/watch?v=ikW6Qk73K1s)
68 |
69 | Working with Clojure maps (hash-map) and [writing a simple ascii code generator](https://youtu.be/ikW6Qk73K1s?t=1052).
70 |
71 | ### Homework
72 | * Test driven development for the Clacks messages converter. See [61 minutes onwards for ideas how to create the tests](https://www.youtube.com/watch?v=ikW6Qk73K1s&feature=youtu.be&t=3660).
73 |
74 |
75 | ## Week 5: Test Driven Development - Clacks messenger
76 |
77 | [](https://www.youtube.com/watch?v=LvissLmUNho&index=5&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
78 |
79 | This week we will look at Test Driven Development and Clojure.
80 |
81 | The [`clojure.test`](https://clojure.github.io/clojure/clojure.test-api.html) library is part of Clojure and is the simplest way to start with TDD and Clojure. We will take a small challenge and solve it by first writing a failing test, then writing some code to make the test pass, then refactor the code if necessary.
82 |
83 | We will start by creating a new project on the command line with Leiningen, which creates parallel source and test branches.
84 |
85 | ```shell
86 | lein new my-project
87 | ```
88 |
89 | Open the source code file from the project (`SPC f f`) and start the REPL - `, '` or `M-RET`.
90 |
91 | Toggle between source code and test code with `SPC p a`. Or open the other in a new window using `SPC p f`, highlight the filename and use `C-c o` to open in new window.
92 |
93 | Run all tests using `, t a` or `M-RET t a`.
94 |
95 | To automate running of tests, toggle cider-auto-test-mode using `, T t` and then evaluate either source or test buffer using `, e b` and tests will run for that namespace
96 |
97 | ### Source code examples
98 | * [Clacks encoder/decoder TDD style](https://github.com/practicalli/clacks)
99 |
100 | ### Homework
101 | * Try some of the [Advent of Code challenges](https://adventofcode.com/)
102 | * Solve [4Clojure challenges](http://www.4clojure.com/) from #16 onwards
103 |
104 |
105 | ## Week 6: Advent of Clojure
106 |
107 | [](https://www.youtube.com/watch?v=opM7fU7IAV8&index=6&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
108 |
109 | Solving day one of the [Advent of Code challenges](https://adventofcode.com/). The first part of the problem is solved with a simple `reduce` function. The second part we investigate several different approaches, becoming more functional and using higher abstractions in Clojure.
110 |
111 | If you understand the progression of the solutions, you are a good way on to creating very clean Clojure code at a good level of abstraction.
112 |
113 | > #### Note::Advent of Code
114 | > [jr0cket/advent-of-code-2018](https://github.com/jr0cket/advent-of-code-2018) repository contains documented descriptions of the solutions to Advent of Code created so far.
115 | >
116 | > [Advent of Code](https://adventofcode.com/) is a yearly coding challenge that sets one challenge per day, just like an advent calendar.
117 | >
118 | > Tim Pote will be live [streaming their solution to the advent of code](https://www.twitch.tv/timpote) each day at 12 noon (UTC-5 timezone) on Twitch.
119 | >
120 | > Borkdude also has a [Github repository where advent of code solutions will be published](https://github.com/borkdude/advent-of-spec), with all solutions checked with Clojure spec
121 |
122 |
123 |
124 | ## Week 7: Simple website with ClojureScript, reagent, bootstrap and SVG
125 |
126 | [](https://www.youtube.com/watch?v=WYaIy3E6nLk&index=7&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
127 |
128 | Creating a very simple front end website with ClojureScript. ClojureScript is Clojure that runs on JavaScript engines, such at the one in your browser or node.js. The project included [reagent](), a react.js style framework for ClojureScript. The web page is made up of one or more components that are managed by the reagent library, so if there are updates to the components the reagent library will refresh the web page.
129 |
130 | The project also used [Bootstrap](https://getbootstrap.com/) as a Cascading Style Sheet library, to help structure the layout of the page and add visual components. The reagent library also lets us define graphics using markup, creating [Scalable Vector graphics](https://clojurebridgelondon.github.io/workshop/introducing-clojure/clojure-svg-graphics.html).
131 |
132 | The project was created with the [Leiningen figwheel template](https://github.com/bhauman/lein-figwheel), to give instant feedback in the web page as we update the code.
133 |
134 | ```shell
135 | lein new figwheel simple-website -- --reagent
136 | ```
137 |
138 |
139 | ## Week 8: 4Clojure challenges 16 to 22
140 |
141 | [](https://www.youtube.com/watch?v=8u8y73zh0w0&index=8&list=PLy9I_IfUBzKJSgctCJaRYcnF6kZdiZ5ku)
142 |
143 | Solving the 4Clojure challenges from 16 to 32.
144 |
145 | > #### Hint::4Clojure challenges up to 15
146 | > We solve [the first 15 challenges in study group #3 from 34.50 onwards](https://www.youtube.com/watch?v=PmSPKvlJk74&feature=youtu.be&t=2090)
147 | >
148 | > [jr0cket/four-clojure](https://github.com/jr0cket/four-clojure) repository - discussions of 4Clojure solutions
149 |
150 | ## Week 9: Tic-Tac-Toe
151 |
152 | [](https://www.youtube.com/watch?v=ikW6Qk73K1s)
153 |
154 | Building a simple command line tic-tac-toe game in Clojure in a very functional style.
155 |
156 | No frameworks were harmed (or used) in the making of this broadcast :smile_cat:
157 |
158 | * [practicalli/tictactoe GitHub repository](https://github.com/practicalli/tictactoe-cli)
159 |
160 | > #### Hint::Joining the Live discussion
161 | > A hangout link will be posted to the MeetAMentor Slack channel and London Clojurians meetup page about 30 minutes before broadcast.
162 |
163 |
164 |
165 | ## Additional concepts to cover
166 | * Understanding how to model the world with immutable data (values)
167 | * Writing your own (pure) functions
168 | * using sequences
169 | * lisp comprehension
170 | * higher order functions
171 | * functional composition.
172 |
173 | Homework: various small challenges and 4Clojure exercises.
174 |
175 | ## Resources for practising Clojure
176 | * [4Clojure](http://www.4clojure.com/)
177 | * [Exorcism](https://exercism.io/)
178 | * [CodeWars](https://www.codewars.com/)
179 |
180 | Demonstrate how to use the REPL in [Spacemacs](http://practicalli.github.io/clojure/development-tools/#spacemacs) and [Atom.io with ProtoREPL](http://practicalli.github.io/clojure/development-tools/#atom-protorepl). Demonstrating evaluation of code, structural editing, refactoring, etc.
181 |
182 |
183 |
184 | > #### Hint::Finding your own learning path
185 | > As there are many ways to study, please feel free to carve out your own learning path and share what you found useful if you wish.
186 | > [Learning Clojure section](learning-clojure.md) describes important steps you should take on your journy into Clojure.
187 |
--------------------------------------------------------------------------------
/docs/reference/clojure-cli/example-alias-definitions.md:
--------------------------------------------------------------------------------
1 | # Common alias definitions
2 |
3 | ## Task: Run a simple terminal REPL
4 |
5 | `clojure` and `clj` (requires rlwrap) will run a REPL if given no other arguments.
6 |
7 | Running either command from the root directory of a project will merge the `deps.edn` configuration with `~/.clojure/deps.edn`.
8 |
9 | ## Task: Run a REPL with additional dependencies and paths
10 |
11 | `clojure -M:alias` will run a repl if the alias does not contain a main namespace defined in `:main-opts`, e.g. `:main-opts ["-m" "namespace.main"]`. The deps and path values are included from the alias.
12 |
13 | If the following alias is defined in the project `deps.edn` file
14 |
15 | ```clojure
16 | :env/dev
17 | {:extra-paths ["resources"]
18 | :extra-deps {com.h2database/h2 {:mvn/version "1.4.200"}}}
19 | ```
20 |
21 | `clojure -M:env/dev` will add `resources` directory to the path and the h2 database library to the dependencies, then runs a REPL.
22 |
23 | Including the `-r` option in the command line forces a REPL to run, even if a main namespace is provided via `:main-opts` or the command line.
24 |
25 | ```clojure
26 | clojure -r -M:alias1:alias2
27 | ```
28 |
29 | The dependencies and paths will be merged from the alias from left to right, with each successive alias over-riding the value of any matching keys in the dependencies.
30 |
31 | ## Task: Create a new project from template
32 |
33 | The `clj-new` community tool can be used to create a Clojure / ClojureScript project, using a template to provide a project structure and example source code and tests.
34 |
35 | Using the `:main-opts` approach, an alias for `clj-new` would be defined as follows
36 |
37 | ```clojure
38 | :project/new
39 | {:extra-deps {seancorfield/clj-new {:mvn/version "1.0.215"}}
40 | :main-opts ["-m" "clj-new.create"]}
41 | ```
42 |
43 | The `clj-new` tool can be run using the `-M` flag, passing the template and project names as arguments.
44 |
45 | `clojure -M:project/new template-name project-domain/application-name`
46 |
47 | To create a project as an application (to be run via the command line) for the practicalli domain with the application called banking-on-clojure
48 |
49 | ```shell
50 | clojure -M:new app practicalli/banking-on-clojure
51 | ```
52 |
53 | The latest version of the `clj-new` project also supports using the `-X` flag and default arguments.
54 |
55 | Adding the `:exec-fn` to the `clj-new` alias, the `-X` flag can be used instead of the `-M`. Arguments are supplied as key/value pairs
56 |
57 | ```clojure
58 | :project/new
59 | {:extra-deps {seancorfield/clj-new {:mvn/version "1.1.215"}}
60 | :exec-fn clj-new/create}
61 | ```
62 |
63 | Use this alias with the `-X` flag
64 |
65 | ```shell
66 | clojure -X:project/new :template template-name :name practicalli/banking-on-clojure
67 | ```
68 |
69 | Default values can be added using the `:exec-args` key to the alias
70 |
71 | ```clojure
72 | :project/new
73 | {:extra-deps {seancorfield/clj-new {:mvn/version "1.1.215"}}
74 | :exec-fn clj-new.create
75 | :exec-args {:template lib :name practicalli/playground}}
76 | ```
77 |
78 | `clojure -M:project/new :name practicalli/awesome-webapp` will create a new project using the `{:template lib :name practicalli/awesome-webapp}` argument.
79 |
80 | ## Task: Executing a specific function
81 |
82 | Clojure can run a specific function, useful for one off tasks or timed batch processing (via cron or similar tool) as well as complete applications.
83 |
84 | Arguments to the function are passed as a hash-map, defined in either an aliases `:exec-args` key or as key value pairs on the command line. Command line key value pairs are merged with the `:exec-arg` hash-map, replacing the values from the command line if there are matching keys.
85 |
86 | **Scenarios**
87 |
88 | `clojure -X namespace/fn` runs the function specified on the command line, passing an empty hash-map as an argument
89 |
90 | `clojure -X:alias fn` runs the function if the `:ns-default` is set to the namespace that contains the function, otherwise "Unqualified function can't be resolved: fn-name" error is returned.
91 |
92 | `clojure -X:alias` runs the function specified by `:exec-fn` in the alias. The function must include its namespace or have that namespace defined in `:ns-default`. If `:exec-args` is defined in the alias, its value is passed to the function, otherwise an empty hash-map is passed to the function as an argument.
93 |
94 | `clojure -X:alias namespace/fn` will run the function specified on the command line, over-riding `:exec-fn` if it is defined in the alias. `:exec-args` will be passed to the command line function if defined in the alias. Dependencies and paths will be used from the alias. Assumption: the command line namespace also overrides the `:ns-default` value if set.
95 |
96 | `clojure -X:alias :key1 val1 :key2 val2` will execute the function defined in `:exec-fn` and pass it the key value pairs from the command line as a hash map. If the alias has `:exec-args` defined, command line args are merged into the `:exec-fn` hash-map, replacing the default values in `:exec-args` where keys match.
97 |
98 | Assuming there is an alias called `database/migrate` defined in the project `deps.edn`
99 |
100 | ```clojure
101 | :database/migrate
102 | {:exec-fn practicalli.banking-on-clojure.database/migrate
103 | :exec-args {:db-type "h2" :database "banking-on-clojure"}}
104 | ```
105 |
106 | `clojure -X:database/migrate :database "specs-repository"` would merge the command line args with `:exec-args` to create the hash-map `{:db-type "h2" :database "specs-repository"}` which is passed to the `practicalli.banking-on-clojure.database/migrate` function as an argument.
107 |
108 | ## Task: Executing a range of functions
109 |
110 | `:ns-default` in an alias defines the namespace that contains the functions that could be executed.
111 |
112 | ```clojure
113 | {:aliases
114 | {:project/run
115 | {:ns-default practicalli/banking-on-clojure}}}
116 | ```
117 |
118 | Specific functions from the namespace can be called via the command line
119 |
120 | ```shell
121 | clojure -X:project/run migrate-db :db-type h2 :database banking-on-clojure
122 | clojure -X:project/run server-start :port 8080
123 | ```
124 |
125 | ## Task: Dry Run or Prepare for CI / Containers
126 |
127 | `clojure -P` will download the libraries defined in `:deps` in the project `deps.edn` and do nothing else. Standard out shows downloading of dependencies not already cached locally, including name and versions and repository downloaded from.
128 |
129 | 
130 |
131 | !!! HINT "Qualified namespaces required"
132 | If an unqualified library name is used, e.g. `compojure`, then a warning is sent to the standard out. Change the name of the library to be fully qualified e.g. `weavejester/compojure`. Use the same name if there is no official qualified domain, e.g. `http-kit/http-kit`
133 |
134 | The `-P` flag can be used to modify an existing command to ensure no execution takes place, ensuring a prepare only (dry run) action.
135 |
136 | `clojure -P -M:alias-name` downloads the dependencies for the specific aliases and multiple aliases can be chained together, e.g. `clojure -P -M:dev/env:test-runner/kaocha`
137 |
138 | The `-P` flag uses everything from an alias not related to execution.
139 |
140 | > The classic way to download deps was to run `clojure -A:aliases -Spath`, where `-Spath` prevented execution of repl or main.
141 |
142 | ## Run a Clojure application
143 |
144 | `clojure -m full.namespace.to.dash-main` calls the `-main` function from the given namespace. Arguments to the function are simply added to the end of the command line and passed to the `-main` function in the given namespace.
145 |
146 | > The `-m` flag in the CLI tools pre-release returns a warning that `-M` should be used.
147 |
148 | Using `-M` and `-m` works, but seems redundant. Using `-M` by itself runs the REPL.
149 |
150 | ```shell
151 | clojure -M -m full.namespace.to.dash-main
152 | ```
153 |
154 | `-M` seems useful when including an alias with extra configuration (eg. `:extra-deps`, `:extra-paths`, `:main-opts`). As `:main-opts` is no different to the `-m` option, creating an alias just to avoid the warning seems excessive.
155 |
156 | ## Task: Executing a project - using Edn style args
157 |
158 | Clojure CLI tools is encouraging a move to functions that take a hash-map for their arguments. Passing arguments in as an edn data structure has more rigor than options and strings on the command line.
159 |
160 | The simplest form is to define an alias to run the project, specifying just the function to execute using `:exec-fn`
161 |
162 | ```clojure
163 | :aliases
164 | {:project/run
165 | {:exec-fn practicalli.banking-on-clojure/server-start}
166 | } ;; End of Aliases
167 | ```
168 |
169 | Then the project can be run using this alias.
170 |
171 | ```shell
172 | clojure -X:project/run
173 | ```
174 |
175 | Arguments can be passed to the function as key/value pairs on the command line.
176 |
177 | ```shell
178 | clojure -X:project/run :port 8080 :host "localhost"
179 | ```
180 |
181 | `:exec-args` provides a way to define default arguments for the function, regardless of if it is defined in `;:exec-fn` or passed via the command line.
182 |
183 | `:exec-args` defines a hash-map of arguments so the function must support taking a hash-map as an argument.
184 |
185 | > A function may take variable args, especially if it is supporting both hash-maps and strings as options.
186 |
187 | ```clojure
188 | :aliases
189 | {:project/run
190 | {:exec-fn fully.qualified/namespace
191 | :exec-args {:default "arguments" :can-be-over-ridden-by "command-line-args"} }
192 | } ;; End of Aliases
193 | ```
194 |
195 | Adding `:exec-args` to the `:run-project`
196 |
197 | ```clojure
198 | :aliases
199 | {:project/run
200 | {:exec-fn practicalli.banking-on-clojure/server-start
201 | :exec-args {:port 8888 :host "localhost"}}
202 | } ;; End of Aliases
203 | ```
204 |
205 | #### Example of running a Clojure project - hello-world
206 |
207 | In this example I use the hello-world example from
208 | A project `deps.edn` file was created containing the dependency for clojure.java-time and the source code from that page copied into `src/hello.clj`
209 |
210 | `clojure -m` hello runs the project and returns the time from running the -main function.
211 | However this gives a warning:
212 |
213 | ```shell
214 | WARNING: When invoking clojure.main, use -M
215 | ```
216 |
217 | `clojure -M` runs a REPL
218 |
219 | `clojure -M -m hello` runs the project and returns the time. But then I ask myself what is the purpose of -M
220 |
221 | Creating an alias to run the project seems an interesting idea, as I could also set default arguments.
222 |
223 | Adding an `:project-run` alias to the project `deps.edn` works when calling with clojure `-M:project-run`
224 |
225 | ```clojure
226 | :aliases
227 | {:project-run {:main-opts ["-m" "hello"]}}
228 | ```
229 |
230 | Changing the `:project-run` alias to use `:exec-fn` and a fully qualified function (-main by default) should work when calling with `clojure -X:project-run`.
231 | :aliases
232 | {:run-project {:exec-fn hello]}}
233 |
234 | However, the `hello-world` project has an unqualified function and cannot be resolved.
235 |
236 | Moving the source code to `src/practicalli/hello.clj` and calling `clojure -X:run-project` gives an execution error, `(ArityException)` as the `-main` function does not take any arguments, `(defn -main [] ,,,)`.
237 |
238 | Changing the `-main` function to `(defn -main [& args] ,,,)` fixes the arity exception and calling `clojure -X:run-project` works.
239 |
240 | ## Local Maven install
241 |
242 | Install a jar into the local Maven cache, typically `~/.m2/repository/` directory, organised by groupId
243 |
244 | ```clojure
245 | clojure -X:deps mvn-install :jar '"/path/to.jar"'
246 | ```
247 |
248 | > edn strings must be in double quotes, and then single-quoted for the shell
249 |
250 | `mvn-install` uses the `.pom` file contained in the jar (if it exists) to determine the _groupId_, _artifactId_, and _version coordinates_ to use when the jar is installed.
251 |
252 | The `.pom` file can also be specified using the `:pom` argument.
253 |
254 | The install argmap takes the following options:
255 |
256 | | key | Required | Description |
257 | |---------------|----------|--------------------------------------------------------|
258 | | `:jar` | required | path to the jar file to install |
259 | | `:pom` | optional | path to .pom file (if .jar file does not contain .pom) |
260 | | `:lib` | optional | qualified symbol e.g `my.org/lib` |
261 | | `:version` | optional | Version number of library (string type) |
262 | | `:classifier` | optional | (string type) |
263 | | `:local-repo` | optional | path to local repo (default = ~/.m2/repository) |
264 |
--------------------------------------------------------------------------------
/docs/introduction/repl-workflow.md:
--------------------------------------------------------------------------------
1 | # REPL Driven Development
2 |
3 | {loading=lazy}
4 |
5 | !!! Quote "Always be REPL'ing"
6 | Coding without a REPL feels limiting. The REPL provides fast feedback from code as its crafted, testing assumptions and design choices every step of the journey to a solution
7 | - John Stevenson, Practical.li
8 |
9 | Clojure is a powerful, fun and highly productive language for developing applications and services.
10 | The clear language design is supported by a powerful development environment known as the REPL (read, evaluate, print, loop). The REPL gives you instant feedback on what your code does and enables you to test either a single expression or run the whole application (including tests).
11 |
12 | **REPL driven development is the foundation of working with Clojure effectively**
13 |
14 | An effective Clojure workflow begins by running a REPL process. Clojure expressions are written and evaluated immediately to provide instant feedback. The REPL feedback helps test the assumptions that are driving the design choices.
15 |
16 | * Read - code is read by the Clojure reader, passing any macros to the macro reader which converts those macros into Clojure code.
17 | * Evaluate - code is compiled into the host language (e.g. Java bytecode) and executed
18 | * Print - results of the code are displayed, either in the REPL or as part of the application.
19 | * Loop - the REPL is a continuous process that evaluates code, either a single expression or the whole application.
20 |
21 | Design decisions and valuable data from REPL experiments can be codified as [specifications](#data-and-function-specifications) and [unit tests](#test-driven-development-and-repl-driven-development)
22 |
23 | !!! HINT "Practicalli REPL Reloaded Workflow"
24 | The principles of REPL driven development are implemented in practice using the [Practicalli REPL Reloaded Workflow and supporting tooling](https://practical.li/clojure/clojure-cli/repl-reloaded/){target=_blank}. This workflow uses Portal to inspect all evaluation results and log events, hot-load libraries into the running REPL process and reloads namespaces to support major refactor changes.
25 |
26 | ## Evaluating source code
27 |
28 | {align=right loading=lazy}
29 |
30 | A REPL connected editor is the primary tool for evaluating Clojure code from source code files, displaying the results inline.
31 |
32 | Source code is automatically evaluated in its respective namespace, removing the need to change namespaces in the REPL with (`in-ns`) or use fully qualified names to call functions.
33 |
34 |
35 |
36 |
37 |
38 | ??? HINT "Evaluate Clojure in a Terminal UI REPL"
39 | Entering expressions at the REPL prompt evaluates the expression immediately, returning the result directly underneath
40 | {loading=lazy}
41 | {loading=lazy}
42 |
43 | ## Rich Comment blocks - living documentation
44 |
45 | The `(comment ,,,)` function wraps code that is only run directly by the developer using a [Clojure aware editor](https://practical.li/clojure/clojure-editors/){target=_blank}.
46 |
47 | Expressions in rich comment blocks can represent how to use the functions that make up the namespace API. For example, starting/restarting the system, updating the database, etc. Expressions provide examples of calling functions with typical arguments and make a project more accessible and easier to work with.
48 |
49 | !!! EXAMPLE "Clojure Rich Comment to manage a service"
50 | ```clojure
51 | (ns practicalli.gameboard.service)
52 |
53 | (defn app-server-start [port] ,,,)
54 | (defn app-server-start [] ,,,)
55 | (defn app-server-restart [] ,,,)
56 |
57 | (defn -main
58 | "Start the service using system components"
59 | [& options] ,,,)
60 |
61 | (comment
62 | (-main)
63 | (app-server-start 8888)
64 | (app-server-stop)
65 | (app-server-restart 8888)
66 |
67 | (System/getenv "PORT")
68 | (def environment (System/getenv))
69 | (def system-properties (System/getProperties))
70 | ) ; End of rich comment block
71 | ```
72 |
73 | Rich comment blocks are very useful for rapidly iterating over different design decisions by including the same function but with different implementations. Hide [clj-kondo linter](https://practical.li/clojure/clojure-cli/install/code-analysis.html){target=_blank} warnings for redefined vars (`def`, `defn`) when using this approach.
74 |
75 | ```clojure
76 | ;; Rich comment block with redefined vars ignored
77 | #_{:clj-kondo/ignore [:redefined-var]}
78 | (comment
79 | (defn value-added-tax []
80 | ;; algorithm design - first idea)
81 |
82 | (defn value-added-tax []
83 | ;; algorithm design - second idea)
84 |
85 | ) ;; End of rich comment block
86 | ```
87 |
88 | The "Rich" in the name is an honourary mention to Rich Hickey, the author and benevolent dictator of Clojure design.
89 |
90 | ## Design Journal
91 |
92 | A journal of design decisions makes the code easier to understand and maintain. Code examples of design decisions and alternative design discussions are captured, reducing the time spent revisiting those discussions.
93 |
94 | Journals simplify the developer on-boarding processes as the journey through design decisions are already documented.
95 |
96 | A Design Journal is usually created in a separate namespace, although it may start as a rich comment at the bottom of a namespace.
97 |
98 | A journal should cover the following aspects
99 |
100 | * Relevant expressions use to test assumptions about design options.
101 | * Examples of design choices not taken and discussions why (saves repeating the same design discussions)
102 | * Expressions that can be evaluated to explain how a function or parts of a function work
103 |
104 | The design journal can be used to create meaningful documentation for the project very easily and should prevent time spent on repeating the same conversations.
105 |
106 | !!! HINT "Example design journal"
107 | [Design journal for TicTacToe game using Reagent, ClojureScript and Scalable Vector Graphics](https://github.com/practicalli-john/tictactoe-reagent/blob/master/src/tictactoe_reagent/core.cljs#L124){target=_blank}
108 |
109 | ## Viewing data structures
110 |
111 | Pretty print shows the structure of results from function calls in a human-friendly form, making it easier for a developer to parse and more likely to notice incorrect results.
112 |
113 | Tools to view and navigate code
114 |
115 | * [:fontawesome-solid-book-open: Cider inspector](https://practical.li/spacemacs/evaluating-clojure/inspect/){target=_blank} is an effective way to navigate nested data and page through large data sets.
116 | * [:fontawesome-solid-book-open: Portal Inspector](https://practical.li/clojure/clojure-tools/data-inspector/portal){target=_blank} to visualise many kinds of data in many different forms.
117 |
118 | 
119 |
120 | ## Code Style and idiomatic Clojure
121 |
122 | Clojure aware editors should automatically apply formatting that follows the [:globe_with_meridians: Clojure Style guide](https://github.com/bbatsov/clojure-style-guide){target=_blank}.
123 |
124 | Live linting with [:fontawesome-brands-github: clj-kondo](https://github.com/borkdude/clj-kondo){target=_blank} suggests common idioms and highlights a wide range of syntax errors as code is written, minimizing bugs and therefore speeding up the development process.
125 |
126 | 
127 | 
128 |
129 | !!! INFO "Clojure LSP is build on top of clj-kondo"
130 | [:fontawesome-solid-book-open: Clojure LSP](https://practical.li/clojure/clojure-editors/clojure-lsp/){target=_blank} uses clj-kondo static analysis to provide a standard set of development tools (format, refactor, auto-complete, syntax highlighting, syntax & idiom warnings, code navigation, etc).
131 |
132 | Clojure LSP can be used with any Clojure aware editor that provides an LSP client, e.g. [:fontawesome-solid-book-open: Spacemacs](https://practical.li/spacemacs/install-spacemacs/clojure-lsp/){target=_blank}, [:fontawesome-solid-book-open: Doom Emacs](https://practical.li/doom-emacs/install/clojure-configuration/#clojure-cli){target=_blank}, [:fontawesome-solid-book-open: Neovim](https://practical.li/neovim/repl-driven-development/){target=_blank}, VSCode.
133 |
134 | !!! INFO "Clojure Style Guide"
135 | The [:globe_with_meridians: Clojure Style guide](https://github.com/bbatsov/clojure-style-guide){target=_blank} provides examples of common formatting approaches, although the development team should decide which of these to adopt. Emacs `clojure-mode` will automatically format code and so will Clojure LSP (via cljfmt). These tools are configurable and should be tailored to the teams standard.
136 |
137 | ## Data and Function specifications
138 |
139 | [:fontawesome-solid-book-open: Clojure spec](https://practical.li/clojure/clojure-spec/){target=_blank} is used to define a contract on incoming and outgoing data, to ensure it is of the correct form.
140 |
141 | As data structures are identified in REPL experiments, create data specification to validate the keys and value types of that data.
142 |
143 | ```clojure
144 | ;; ---------------------------------------------------
145 | ;; Address specifications
146 | (spec/def ::house-number string?)
147 | (spec/def ::street string?)
148 | (spec/def ::postal-code string?)
149 | (spec/def ::city string?)
150 | (spec/def ::country string?)
151 | (spec/def ::additional string?)
152 |
153 | (spec/def ::address ; Composite data specification
154 | (spec/keys
155 | :req-un [::street ::postal-code ::city ::country]
156 | :opt-un [::house-number ::additional]))
157 | ;; ---------------------------------------------------
158 | ```
159 |
160 | As the public API is designed, specifications for each functions arguments are added to validate the correct data is used when calling those functions.
161 |
162 | [:fontawesome-solid-book-open: Generative testing](https://practical.li/clojure/clojure-spec/generative-testing/){target=_blank} provides a far greater scope of test values used incorporated into unit tests. Data uses clojure.spec to randomly generate data for testing on each test run.
163 |
164 | ## Test Driven Development and REPL Driven Development
165 |
166 | {align=right loading=lazy}
167 |
168 | Test Driven Development (TDD) and REPL Driven Development (RDD) complement each other as they both encourage incremental changes and continuous feedback.
169 |
170 | > Test Driven Development fits well with Hammock Time, as good design comes from deep thought
171 |
172 | * RDD enables rapid design experiments so different approaches can easily and quickly be evaluated .
173 | * TDD focuses the results of the REPL experiments into design decisions, codified as unit tests. These tests guide the correctness of specific implementations and provide critical feedback when changes break that design.
174 |
175 | [:fontawesome-solid-book-open: Unit tests](https://practical.li/clojure/testing/unit-testing/){target=_blank} should support the public API of each namespace in a project to help prevent regressions in the code. Its far more efficient in terms of thinking time to define unit tests as the design starts to stabilize than as an after thought.
176 |
177 | `clojure.test` library is part of the Clojure standard library that provides a simple way to start writing unit tests.
178 |
179 | [:fontawesome-solid-book-open: Clojure spec](https://practical.li/clojure/clojure-spec/){target=_blank} can also be used for generative testing, providing far greater scope in values used when running unit tests. Specifications can be defined for values and functions.
180 |
181 | Clojure has a number of [:fontawesome-solid-book-open: test runners](https://practical.li/clojure/testing/test-runners/){target=_blank} available. Kaocha is a test runner that will run unit tests and function specification checks.
182 |
183 | !!! Hint "Automate local test runner"
184 | Use [:fontawesome-solid-book-open: kaocha test runner](https://practical.li/clojure/testing/test-runners/kaocha-test-runner/){target=_blank} in watch mode to run tests and specification check automatically (when changes are saved)
185 | ```shell
186 | clojure -X:test/watch
187 | ```
188 |
189 | ## Continuous Integration and Deployment
190 |
191 | Add a [:fontawesome-solid-book-open: continuous integration service](https://practical.li/clojure/continuous-integration/){target=_blank} to run tests and builds code on every shared commit. Spin up testable review deployments when commits pushed to a pull request branch, before pushing commits to the main deployment branch, creating an effective pipeline to gain further feedback.
192 |
193 | - [:globe_with_meridians: CircleCI](https://practical.li/clojure/continuous-integration/circle-ci/){target=_blank} provides a simple to use service that supports Clojure projects.
194 | - [:globe_with_meridians: GitHub Workflows](https://docs.github.com/en/actions/using-workflows){target=_blank} and [GitHub actions marketplace](https://github.com/marketplace?type=actions){target=_blank} to quickly build a tailored continuous integration service, e.g. [Setup Clojure GitHub Action](https://github.com/marketplace/actions/setup-clojure){target=_blank}.
195 | - [:globe_with_meridians: GitLab CI](https://docs.gitlab.com/ee/ci/introduction/index.html){target=_blank}
196 |
197 | 
198 |
199 | ## Live Coding with Data - Stuart Halloway
200 |
201 | There are few novel features of programming languages, but each combination has different properties. The combination of dynamic, hosted, functional and extended Lisp in Clojure gives developers the tools for making effective programs. The ways in which Clojure's unique combination of features can yield a highly effective development process.
202 |
203 | Over more than a decade we have developed an effective approach to writing code in Clojure whose power comes from composing many of its key features. As different as Clojure programs are from e.g. Java programs, so to can and should be the development experience. You are not in Kansas anymore!
204 |
205 | This talk presents a demonstration of the leverage you can get when writing programs in Clojure, with examples, based on my experiences as a core developer of Clojure and Datomic.
206 |
207 |
208 |
209 |
210 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Attribution-ShareAlike 4.0 International
2 |
3 | =======================================================================
4 |
5 | Creative Commons Corporation ("Creative Commons") is not a law firm and
6 | does not provide legal services or legal advice. Distribution of
7 | Creative Commons public licenses does not create a lawyer-client or
8 | other relationship. Creative Commons makes its licenses and related
9 | information available on an "as-is" basis. Creative Commons gives no
10 | warranties regarding its licenses, any material licensed under their
11 | terms and conditions, or any related information. Creative Commons
12 | disclaims all liability for damages resulting from their use to the
13 | fullest extent possible.
14 |
15 | Using Creative Commons Public Licenses
16 |
17 | Creative Commons public licenses provide a standard set of terms and
18 | conditions that creators and other rights holders may use to share
19 | original works of authorship and other material subject to copyright
20 | and certain other rights specified in the public license below. The
21 | following considerations are for informational purposes only, are not
22 | exhaustive, and do not form part of our licenses.
23 |
24 | Considerations for licensors: Our public licenses are
25 | intended for use by those authorized to give the public
26 | permission to use material in ways otherwise restricted by
27 | copyright and certain other rights. Our licenses are
28 | irrevocable. Licensors should read and understand the terms
29 | and conditions of the license they choose before applying it.
30 | Licensors should also secure all rights necessary before
31 | applying our licenses so that the public can reuse the
32 | material as expected. Licensors should clearly mark any
33 | material not subject to the license. This includes other CC-
34 | licensed material, or material used under an exception or
35 | limitation to copyright. More considerations for licensors:
36 | wiki.creativecommons.org/Considerations_for_licensors
37 |
38 | Considerations for the public: By using one of our public
39 | licenses, a licensor grants the public permission to use the
40 | licensed material under specified terms and conditions. If
41 | the licensor's permission is not necessary for any reason--for
42 | example, because of any applicable exception or limitation to
43 | copyright--then that use is not regulated by the license. Our
44 | licenses grant only permissions under copyright and certain
45 | other rights that a licensor has authority to grant. Use of
46 | the licensed material may still be restricted for other
47 | reasons, including because others have copyright or other
48 | rights in the material. A licensor may make special requests,
49 | such as asking that all changes be marked or described.
50 | Although not required by our licenses, you are encouraged to
51 | respect those requests where reasonable. More_considerations
52 | for the public:
53 | wiki.creativecommons.org/Considerations_for_licensees
54 |
55 | =======================================================================
56 |
57 | Creative Commons Attribution-ShareAlike 4.0 International Public
58 | License
59 |
60 | By exercising the Licensed Rights (defined below), You accept and agree
61 | to be bound by the terms and conditions of this Creative Commons
62 | Attribution-ShareAlike 4.0 International Public License ("Public
63 | License"). To the extent this Public License may be interpreted as a
64 | contract, You are granted the Licensed Rights in consideration of Your
65 | acceptance of these terms and conditions, and the Licensor grants You
66 | such rights in consideration of benefits the Licensor receives from
67 | making the Licensed Material available under these terms and
68 | conditions.
69 |
70 |
71 | Section 1 -- Definitions.
72 |
73 | a. Adapted Material means material subject to Copyright and Similar
74 | Rights that is derived from or based upon the Licensed Material
75 | and in which the Licensed Material is translated, altered,
76 | arranged, transformed, or otherwise modified in a manner requiring
77 | permission under the Copyright and Similar Rights held by the
78 | Licensor. For purposes of this Public License, where the Licensed
79 | Material is a musical work, performance, or sound recording,
80 | Adapted Material is always produced where the Licensed Material is
81 | synched in timed relation with a moving image.
82 |
83 | b. Adapter's License means the license You apply to Your Copyright
84 | and Similar Rights in Your contributions to Adapted Material in
85 | accordance with the terms and conditions of this Public License.
86 |
87 | c. BY-SA Compatible License means a license listed at
88 | creativecommons.org/compatiblelicenses, approved by Creative
89 | Commons as essentially the equivalent of this Public License.
90 |
91 | d. Copyright and Similar Rights means copyright and/or similar rights
92 | closely related to copyright including, without limitation,
93 | performance, broadcast, sound recording, and Sui Generis Database
94 | Rights, without regard to how the rights are labeled or
95 | categorized. For purposes of this Public License, the rights
96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
97 | Rights.
98 |
99 | e. Effective Technological Measures means those measures that, in the
100 | absence of proper authority, may not be circumvented under laws
101 | fulfilling obligations under Article 11 of the WIPO Copyright
102 | Treaty adopted on December 20, 1996, and/or similar international
103 | agreements.
104 |
105 | f. Exceptions and Limitations means fair use, fair dealing, and/or
106 | any other exception or limitation to Copyright and Similar Rights
107 | that applies to Your use of the Licensed Material.
108 |
109 | g. License Elements means the license attributes listed in the name
110 | of a Creative Commons Public License. The License Elements of this
111 | Public License are Attribution and ShareAlike.
112 |
113 | h. Licensed Material means the artistic or literary work, database,
114 | or other material to which the Licensor applied this Public
115 | License.
116 |
117 | i. Licensed Rights means the rights granted to You subject to the
118 | terms and conditions of this Public License, which are limited to
119 | all Copyright and Similar Rights that apply to Your use of the
120 | Licensed Material and that the Licensor has authority to license.
121 |
122 | j. Licensor means the individual(s) or entity(ies) granting rights
123 | under this Public License.
124 |
125 | k. Share means to provide material to the public by any means or
126 | process that requires permission under the Licensed Rights, such
127 | as reproduction, public display, public performance, distribution,
128 | dissemination, communication, or importation, and to make material
129 | available to the public including in ways that members of the
130 | public may access the material from a place and at a time
131 | individually chosen by them.
132 |
133 | l. Sui Generis Database Rights means rights other than copyright
134 | resulting from Directive 96/9/EC of the European Parliament and of
135 | the Council of 11 March 1996 on the legal protection of databases,
136 | as amended and/or succeeded, as well as other essentially
137 | equivalent rights anywhere in the world.
138 |
139 | m. You means the individual or entity exercising the Licensed Rights
140 | under this Public License. Your has a corresponding meaning.
141 |
142 |
143 | Section 2 -- Scope.
144 |
145 | a. License grant.
146 |
147 | 1. Subject to the terms and conditions of this Public License,
148 | the Licensor hereby grants You a worldwide, royalty-free,
149 | non-sublicensable, non-exclusive, irrevocable license to
150 | exercise the Licensed Rights in the Licensed Material to:
151 |
152 | a. reproduce and Share the Licensed Material, in whole or
153 | in part; and
154 |
155 | b. produce, reproduce, and Share Adapted Material.
156 |
157 | 2. Exceptions and Limitations. For the avoidance of doubt, where
158 | Exceptions and Limitations apply to Your use, this Public
159 | License does not apply, and You do not need to comply with
160 | its terms and conditions.
161 |
162 | 3. Term. The term of this Public License is specified in Section
163 | 6(a).
164 |
165 | 4. Media and formats; technical modifications allowed. The
166 | Licensor authorizes You to exercise the Licensed Rights in
167 | all media and formats whether now known or hereafter created,
168 | and to make technical modifications necessary to do so. The
169 | Licensor waives and/or agrees not to assert any right or
170 | authority to forbid You from making technical modifications
171 | necessary to exercise the Licensed Rights, including
172 | technical modifications necessary to circumvent Effective
173 | Technological Measures. For purposes of this Public License,
174 | simply making modifications authorized by this Section 2(a)
175 | (4) never produces Adapted Material.
176 |
177 | 5. Downstream recipients.
178 |
179 | a. Offer from the Licensor -- Licensed Material. Every
180 | recipient of the Licensed Material automatically
181 | receives an offer from the Licensor to exercise the
182 | Licensed Rights under the terms and conditions of this
183 | Public License.
184 |
185 | b. Additional offer from the Licensor -- Adapted Material.
186 | Every recipient of Adapted Material from You
187 | automatically receives an offer from the Licensor to
188 | exercise the Licensed Rights in the Adapted Material
189 | under the conditions of the Adapter's License You apply.
190 |
191 | c. No downstream restrictions. You may not offer or impose
192 | any additional or different terms or conditions on, or
193 | apply any Effective Technological Measures to, the
194 | Licensed Material if doing so restricts exercise of the
195 | Licensed Rights by any recipient of the Licensed
196 | Material.
197 |
198 | 6. No endorsement. Nothing in this Public License constitutes or
199 | may be construed as permission to assert or imply that You
200 | are, or that Your use of the Licensed Material is, connected
201 | with, or sponsored, endorsed, or granted official status by,
202 | the Licensor or others designated to receive attribution as
203 | provided in Section 3(a)(1)(A)(i).
204 |
205 | b. Other rights.
206 |
207 | 1. Moral rights, such as the right of integrity, are not
208 | licensed under this Public License, nor are publicity,
209 | privacy, and/or other similar personality rights; however, to
210 | the extent possible, the Licensor waives and/or agrees not to
211 | assert any such rights held by the Licensor to the limited
212 | extent necessary to allow You to exercise the Licensed
213 | Rights, but not otherwise.
214 |
215 | 2. Patent and trademark rights are not licensed under this
216 | Public License.
217 |
218 | 3. To the extent possible, the Licensor waives any right to
219 | collect royalties from You for the exercise of the Licensed
220 | Rights, whether directly or through a collecting society
221 | under any voluntary or waivable statutory or compulsory
222 | licensing scheme. In all other cases the Licensor expressly
223 | reserves any right to collect such royalties.
224 |
225 |
226 | Section 3 -- License Conditions.
227 |
228 | Your exercise of the Licensed Rights is expressly made subject to the
229 | following conditions.
230 |
231 | a. Attribution.
232 |
233 | 1. If You Share the Licensed Material (including in modified
234 | form), You must:
235 |
236 | a. retain the following if it is supplied by the Licensor
237 | with the Licensed Material:
238 |
239 | i. identification of the creator(s) of the Licensed
240 | Material and any others designated to receive
241 | attribution, in any reasonable manner requested by
242 | the Licensor (including by pseudonym if
243 | designated);
244 |
245 | ii. a copyright notice;
246 |
247 | iii. a notice that refers to this Public License;
248 |
249 | iv. a notice that refers to the disclaimer of
250 | warranties;
251 |
252 | v. a URI or hyperlink to the Licensed Material to the
253 | extent reasonably practicable;
254 |
255 | b. indicate if You modified the Licensed Material and
256 | retain an indication of any previous modifications; and
257 |
258 | c. indicate the Licensed Material is licensed under this
259 | Public License, and include the text of, or the URI or
260 | hyperlink to, this Public License.
261 |
262 | 2. You may satisfy the conditions in Section 3(a)(1) in any
263 | reasonable manner based on the medium, means, and context in
264 | which You Share the Licensed Material. For example, it may be
265 | reasonable to satisfy the conditions by providing a URI or
266 | hyperlink to a resource that includes the required
267 | information.
268 |
269 | 3. If requested by the Licensor, You must remove any of the
270 | information required by Section 3(a)(1)(A) to the extent
271 | reasonably practicable.
272 |
273 | b. ShareAlike.
274 |
275 | In addition to the conditions in Section 3(a), if You Share
276 | Adapted Material You produce, the following conditions also apply.
277 |
278 | 1. The Adapter's License You apply must be a Creative Commons
279 | license with the same License Elements, this version or
280 | later, or a BY-SA Compatible License.
281 |
282 | 2. You must include the text of, or the URI or hyperlink to, the
283 | Adapter's License You apply. You may satisfy this condition
284 | in any reasonable manner based on the medium, means, and
285 | context in which You Share Adapted Material.
286 |
287 | 3. You may not offer or impose any additional or different terms
288 | or conditions on, or apply any Effective Technological
289 | Measures to, Adapted Material that restrict exercise of the
290 | rights granted under the Adapter's License You apply.
291 |
292 |
293 | Section 4 -- Sui Generis Database Rights.
294 |
295 | Where the Licensed Rights include Sui Generis Database Rights that
296 | apply to Your use of the Licensed Material:
297 |
298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
299 | to extract, reuse, reproduce, and Share all or a substantial
300 | portion of the contents of the database;
301 |
302 | b. if You include all or a substantial portion of the database
303 | contents in a database in which You have Sui Generis Database
304 | Rights, then the database in which You have Sui Generis Database
305 | Rights (but not its individual contents) is Adapted Material,
306 |
307 | including for purposes of Section 3(b); and
308 | c. You must comply with the conditions in Section 3(a) if You Share
309 | all or a substantial portion of the contents of the database.
310 |
311 | For the avoidance of doubt, this Section 4 supplements and does not
312 | replace Your obligations under this Public License where the Licensed
313 | Rights include other Copyright and Similar Rights.
314 |
315 |
316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
317 |
318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
328 |
329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
338 |
339 | c. The disclaimer of warranties and limitation of liability provided
340 | above shall be interpreted in a manner that, to the extent
341 | possible, most closely approximates an absolute disclaimer and
342 | waiver of all liability.
343 |
344 |
345 | Section 6 -- Term and Termination.
346 |
347 | a. This Public License applies for the term of the Copyright and
348 | Similar Rights licensed here. However, if You fail to comply with
349 | this Public License, then Your rights under this Public License
350 | terminate automatically.
351 |
352 | b. Where Your right to use the Licensed Material has terminated under
353 | Section 6(a), it reinstates:
354 |
355 | 1. automatically as of the date the violation is cured, provided
356 | it is cured within 30 days of Your discovery of the
357 | violation; or
358 |
359 | 2. upon express reinstatement by the Licensor.
360 |
361 | For the avoidance of doubt, this Section 6(b) does not affect any
362 | right the Licensor may have to seek remedies for Your violations
363 | of this Public License.
364 |
365 | c. For the avoidance of doubt, the Licensor may also offer the
366 | Licensed Material under separate terms or conditions or stop
367 | distributing the Licensed Material at any time; however, doing so
368 | will not terminate this Public License.
369 |
370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
371 | License.
372 |
373 |
374 | Section 7 -- Other Terms and Conditions.
375 |
376 | a. The Licensor shall not be bound by any additional or different
377 | terms or conditions communicated by You unless expressly agreed.
378 |
379 | b. Any arrangements, understandings, or agreements regarding the
380 | Licensed Material not stated herein are separate from and
381 | independent of the terms and conditions of this Public License.
382 |
383 |
384 | Section 8 -- Interpretation.
385 |
386 | a. For the avoidance of doubt, this Public License does not, and
387 | shall not be interpreted to, reduce, limit, restrict, or impose
388 | conditions on any use of the Licensed Material that could lawfully
389 | be made without permission under this Public License.
390 |
391 | b. To the extent possible, if any provision of this Public License is
392 | deemed unenforceable, it shall be automatically reformed to the
393 | minimum extent necessary to make it enforceable. If the provision
394 | cannot be reformed, it shall be severed from this Public License
395 | without affecting the enforceability of the remaining terms and
396 | conditions.
397 |
398 | c. No term or condition of this Public License will be waived and no
399 | failure to comply consented to unless expressly agreed to by the
400 | Licensor.
401 |
402 | d. Nothing in this Public License constitutes or may be interpreted
403 | as a limitation upon, or waiver of, any privileges and immunities
404 | that apply to the Licensor or You, including from the legal
405 | processes of any jurisdiction or authority.
406 |
407 |
408 | =======================================================================
409 |
410 | Creative Commons is not a party to its public
411 | licenses. Notwithstanding, Creative Commons may elect to apply one of
412 | its public licenses to material it publishes and in those instances
413 | will be considered the “Licensor.” The text of the Creative Commons
414 | public licenses is dedicated to the public domain under the CC0 Public
415 | Domain Dedication. Except for the limited purpose of indicating that
416 | material is shared under a Creative Commons public license or as
417 | otherwise permitted by the Creative Commons policies published at
418 | creativecommons.org/policies, Creative Commons does not authorize the
419 | use of the trademark "Creative Commons" or any other trademark or logo
420 | of Creative Commons without its prior written consent including,
421 | without limitation, in connection with any unauthorized modifications
422 | to any of its public licenses or any other arrangements,
423 | understandings, or agreements concerning use of licensed material. For
424 | the avoidance of doubt, this paragraph does not form part of the
425 | public licenses.
426 |
427 | Creative Commons may be contacted at creativecommons.org.
428 |
--------------------------------------------------------------------------------
/docs/introduction/clojure-in-15-minutes.md:
--------------------------------------------------------------------------------
1 | # Clojure in 15 minutes
2 |
3 | A quick tour of the Clojure syntax and common functions. The syntax is quite minimal so this should take around 15 minutest to read through (it may take longer to get comfortable with).
4 |
5 | !!! HINT "Try the code out in the REPL"
6 | [:fontawesome-solid-book-open: Start a Clojure REPL](/clojure/clojure-cli/repl/) or use a [:fontawesome-solid-book-open: Clojure aware editor](/clojure/clojure-editors/) connected to a REPL and experiment with these code examples.
7 |
8 | Using the REPL provides instant feedback on each expression as they are evaluated, greatly increasing your understanding.
9 |
10 |
11 | ## Clojure expressions
12 |
13 | Clojure is written with "expressions", a lists of elements inside parentheses, `()`, separated by space characters.
14 |
15 | An expression is made of one or more forms, a form is a general term for anything that legally evaluates in Clojure, e.g. a number, string function definition, etc.
16 |
17 | Clojure evaluates the first element in an expression as a function call. Additional elements in the expression are passed as value arguments to the called function.
18 |
19 | !!! EXAMPLE "Function call with value and expression as arguments"
20 | ```clojure
21 | (+ 2007 (* 1 16))
22 | ```
23 |
24 | !!! EXAMPLE "Functions can be passed as an argument"
25 | ```clojure
26 | (map inc (range 0 99))
27 | ```
28 |
29 |
30 | ## Comment
31 |
32 | `;;` two semi-colons for a line comment, `;` single semi-colon to comment the rest of the line
33 |
34 | `#_` comment reader macro to skip specific parts of code, e.g. `(+ 1 2 #_(* 3 4))` is read as `(+ 1 2)`
35 |
36 | `(comment ,,,)` form to comment all the containing forms, useful to [:fontawesome-solid-book-open: separate experimental and established code](/clojure/introduction/repl-workflow/#rich-comment-blocks-living-documentation) in a namespace. Note that `(comment )` returns `nil` when evaluated so shouldn't be used inside other code.
37 |
38 |
39 | ## Organising Clojure
40 |
41 | Clojure code is organised into one or more namespaces. The namespace represents the directory path and file name that contains the code of the particular namespace.
42 |
43 | A company name or community repository name is often used making the namespace unique and easier to share & reuse.
44 |
45 | ??? INFO "ns form returns nil value"
46 | The `(ns namespace.,,,)` expression returns a `nil` value, as its work is done behind the scenes.
47 |
48 | All Clojure functions must return a value and `nil` is a value that means 'no value'.
49 |
50 | !!! EXAMPLE "Define a namespace"
51 | ```clojure title="src/practicalli/game_board.clj"
52 | (ns practicalli.game-board)
53 | ```
54 |
55 | !!! EXAMPLE "Define a longer namespace"
56 | ```clojure title="src/com/company/product/component_name.clj"
57 | (ns com.company.product.component-name)
58 | ```
59 |
60 | ??? WARNING "Namespaces use dash, directory and file names use underscore"
61 | Clojure uses `kebab-case` for names (common in Lisp dialects)
62 |
63 | Unfortunately the Java Virtual Machine that hosts Clojure does not support dash, `-`, in file and directory names, so an underscore, `-`, character is used
64 |
65 |
66 | ## String manipulation
67 |
68 | The `str` function creates a new string from all the arguments passed
69 |
70 | !!! EXAMPLE "Combine strings into a single string value"
71 | ```clojure
72 | (str "Hello" " " "World")
73 | ```
74 |
75 | `"Hello World"` is returned from evaluating the expression.
76 |
77 | !!! HINT "clojure.string library for manipulating strings"
78 | `clojure.string` library functions manipulate values and return string values (other clojure.core functions my return characters as results, e.g. `map`)
79 |
80 |
81 | ## Math, Truth & prefix notation
82 |
83 | Functions use prefix notation, so you can do math with multiple values very easily
84 |
85 | !!! EXAMPLE "Prefix syntax takes multiple arguments"
86 | ```clojure
87 | (+ 1 2 3 5 7 9 12) ; => 40
88 | ```
89 |
90 | Math in Clojure is very precise, no need for operator precedence rules (as there are no operators)
91 |
92 | Nesting forms defined a very precise calculation
93 |
94 | !!! EXAMPLE "Parentheses used instead of operator preceedence rules"
95 | ```clojure
96 | (* 1 2 (- 24 (* 7 3)))
97 | ```
98 |
99 | `6` is returned as the value. Nested expressions are typically read inside out. `(* 7 3)` is `21`, giving `(- 24 21)` expression resulting in `3`. Finally the expression becomes `(* 1 2 3)`, resulting in a value of `6`
100 |
101 |
102 | Maintain precision for calculations using a Ratio type in Clojure
103 |
104 | !!! EXAMPLE "Clojure Ratio value"
105 | ```clojure
106 | (/ 27 7) ; => 27/7
107 | ```
108 |
109 | `22/7` is returned as the value, rather than a floating point value (double) which may loose some precision due to rounding.
110 |
111 |
112 | ### Equality
113 |
114 | `=` function provides a test for equality
115 |
116 | !!! EXAMPLE "Equal values return a boolean true"
117 | ```clojure
118 | (= 1 1) ; => true
119 | ```
120 |
121 | !!! EXAMPLE "Unequals values return a boolean false"
122 | ```clojure
123 | (= 2 1) ; => false
124 | ```
125 |
126 | `true` and `false` are Boolean values and can be used literally in Clojure.
127 |
128 |
129 | ### Predicates
130 |
131 | A predicate is a function that returns a boolean `true` or `false` value and by convention the function name ends in `?`, e.g. `true?`, `false?`, `seq?`, `even?`, `uuid?`.
132 |
133 | `and` & `or` functions can be used to chain the results of predicates together for more interesting conditional tests.
134 |
135 | !!! EXAMPLE "All predicates are true, returning true"
136 | ```clojure
137 | (and (true? true) (not false)) ; => true
138 | ```
139 |
140 | !!! EXAMPLE "One of the predicates or values is true"
141 | ```clojure
142 | (or nil (not= true false) (true? (complement true?)) ) ; => true
143 | ```
144 |
145 | !!! HINT "Truthy and Falsy values in Clojure"
146 | `false` boolean value and `nil` value are considered false in Clojure.
147 |
148 | All other values are consider true.
149 |
150 |
151 | [:fontawesome-solid-book-open: Clojure Standard Library Predicate Functions](https://practical.li/clojure/reference/standard-library/predicate-functions/){.md-button}
152 |
153 |
154 | ## Collections & Sequences
155 |
156 | The most common data collections in Clojure:
157 |
158 | * List literal `(1 2 "three")` or function call `(list 1 2 "three")` - a list of values read from start to end (sequential access)
159 | * Vector literal `[1 2 "three"]` or function call `(vector 1 2 "three")` - a vector of values with index (random access)
160 | * Hash-map literal `{:key "value"}` or function call `(hash-map :key "value")` - a hash-map with zero or more key value pairs (associative relation)
161 | * Set literal `#{1 2 "three"}` or function call `(set 1 2 "three")` - a unique set of values
162 |
163 | A literal list `()` expression is evaluated as a function call. The first element in the list is the name of the function to call and additional values are arguments to the function (assuming the function takes arguments).
164 |
165 | The `'` is syntax short-cut for the `quote` function which informs the Clojure reader to treat a list as data only. A quoted list is not evaluated as a function.
166 |
167 | !!! EXAMPLE "Evaluating a quoted list returns that list as data"
168 | ```clojure
169 | '(1 2 3) ; => (1 2 3)
170 | ```
171 |
172 | !!! EXAMPLE "Lists and vectors are collections"
173 | ```clojure
174 | (and (coll? '(1 2 3)) (coll? [1 2 3])) ; => true
175 | ```
176 |
177 | Only lists are sequences
178 |
179 | ```clojure
180 | (seq? '(1 2 3)) ; => true
181 | (seq? [1 2 3]) ; => false
182 | ```
183 |
184 | Sequences are an interface for logical lists, which can be lazy. "Lazy" means that a sequence of values are not evaluated until accessed.
185 |
186 | The `range` function generates an 'infinite' series of numbers, e.g. `(range) ; => (0 1 2 3 4 ...)`
187 |
188 | Lazily evaluating a sequence enables the use of large or even an infinite series without consuming all the computer memory, like so:
189 |
190 | !!! EXAMPLE "Lazy sequences"
191 | ```clojure
192 | (take 4 (range)) ; (0 1 2 3) - lazyily evaluate range and stop when enough values are taken
193 | ```
194 |
195 | Use cons to add an item to the beginning of a list or vector
196 |
197 | ```clojure
198 | (cons 4 [1 2 3]) ; => (4 1 2 3)
199 | (cons 4 '(1 2 3)) ; => (4 1 2 3)
200 | ```
201 |
202 | Use conj to add an item relative to the type of collection, to the beginning of a list or the end of a vector
203 |
204 | ```clojure
205 | (conj [1 2 3] 4) ; => [1 2 3 4]
206 | (conj '(1 2 3) 4) ; => (4 1 2 3)
207 | ```
208 |
209 | Use `concat` (concatenate) to add sequences (lists or vectors) together
210 |
211 | ```clojure
212 | (concat [1 2] '(3 4)) ; => (1 2 3 4)
213 | ```
214 |
215 | `filter` maps another function over a collection of values, returning the values that returned true from the function used by filter
216 |
217 | ```clojure
218 | (map inc [1 2 3]) ; => (2 3 4)
219 | (filter even? [1 2 3]) ; => (2)
220 | ```
221 |
222 | `reduce` uses a function over a collection of values to return a combined result
223 |
224 | ```clojure
225 | (reduce + [1 2 3 4])
226 | ; = (+ (+ (+ 1 2) 3) 4)
227 | ; => 10
228 | ```
229 |
230 | Reduce can take an initial-value argument too
231 |
232 | ```clojure
233 | (reduce conj [] '(3 2 1))
234 | ; => [3 2 1]
235 | ```
236 |
237 | The above is the equivalent of `(conj (conj (conj [] 3) 2) 1)`
238 |
239 |
240 | ## Anonymous Functions
241 |
242 | Use `fn` to create new functions that defines some behaviour. `fn` is referred to as an anonymous function as it has no external name to be referenced by and must be called within a list form.
243 |
244 | ```clojure
245 | (fn hello [] "Hello World")
246 | ```
247 |
248 | Wrap an anonymous function `(fn ,,,)` expression in another list to call it and return the result.
249 |
250 | !!! EXAMPLE "Call an anonymous function"
251 | ```clojure
252 | ((fn hello [] "Hello World")) ; => "Hello World"
253 | ```
254 |
255 | Normally the anonymous function is used inline with other code
256 |
257 | !!! EXAMPLE "Use anonymous function within other code"
258 | ```clojure
259 | (map (fn [x] (* x 2)) [1 2 3 4 [1 2 3 4 5]5])
260 | ```
261 |
262 | Make the anonymous function reusable by binding it to a shared name (`var`) using `def`.
263 |
264 | The `var` name bound to the function can now be called anywhere in the namespace.
265 |
266 | > As `def` creates a `var` (variable) name, the developer can changed the expression the name is bound to and re-evaluated to use the changed behaviour.
267 |
268 | !!! EXAMPLE "Bind a name to the anonymous function"
269 | ```clojure
270 | (def hello-world
271 | (fn hello [] "Hello World"))
272 | ```
273 |
274 | !!! EXAMPLE "Evaluate anonymous function by evaluating its name"
275 | ```clojure
276 | hello-world
277 | ```
278 |
279 | > NOTE: `hello-world` is a name and not a function call, so parentheses are not required.
280 |
281 |
282 | ## Shared Functions
283 |
284 | It is more common to use the `defn` macro to define a function. This is the same as defining the `fn` function and the `def` name within the same expression
285 |
286 | !!! EXAMPLE "Define a function with defn macro"
287 | ```clojure
288 | (defn hello-world
289 | "I am a humble doc-string, please describe the function purpose"
290 | []
291 | "Hello World")
292 | ```
293 |
294 | `#'user/hello-world` is the value returned from evaluating the expression, showing the fully qualified name of the function. Note: the fully qualified name will be different when defined in a different namespace than `user`.
295 |
296 |
297 | > A `defn` function has the scope of the current namespace, so can be called anywhere in the namespace or in a namepace that has used `require` to include this namespace.
298 |
299 | !!! EXAMPLE "Call a function"
300 | ```clojure
301 | (hello-world)
302 | ```
303 |
304 | The `[]` vector is used to define the argument names for the function. There can be zero or more arguments.
305 |
306 | !!! EXAMPLE "Call function with arguments"
307 | ```clojure
308 | (defn hello [name]
309 | (str "Hello " name))
310 | ```
311 |
312 | The correct number of arguments must be used when calling a function, or an error will be returned.
313 |
314 | !!! EXAMPLE "Call function with arguments"
315 | ```clojure
316 | (hello "Steve") ; => "Hello Steve"
317 | ```
318 |
319 | ??? HINT "Pass a hash-map as an argument"
320 | Simplify the design of a function signature by passing all arguments as a hash-map.
321 | ```clojure
322 | (defn data-processing
323 | [data]
324 | (let [body (get data :body)])
325 | (transform body))
326 | ```
327 | [:globe_with_meridians: Associative Destructuring](https://clojure.org/guides/destructuring#_associative_destructuring){target=_blank} can be used to automatically create local variables from the desired keys contained in the map, giving access to the value of each key.
328 | ```clojure
329 | (defn data-processing
330 | [{:keys [body]}]
331 | (transform body))
332 | ```
333 |
334 |
335 | Clojure supports multi-variadic functions, allowing one function definition to respond to a function call with different number of arguments. This provides a simple form of polymorphism based on the number of arguments.
336 |
337 | ```clojure
338 | (defn hello-polly
339 | ([] "Hello World") ; (1)!
340 | ([name] (str "Hello " name))) ; (2)!
341 | ```
342 |
343 | 1. Call `hello-polly` with one argument
344 | ```clojure
345 | (hello-polly "Jake") ; => "Hello Jake"
346 | ```
347 |
348 | 2. Call `hello-polly` with zero arguments
349 | ```clojure
350 | (hello-polly) ; => "Hello World"
351 | ```
352 |
353 | Functions can pack extra arguments up in a seq for you
354 |
355 | ```clojure
356 | (defn count-args [& args]
357 | (str "You passed " (count args) " args: " args))
358 | (count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
359 | ```
360 |
361 | You can mix regular and packed arguments
362 |
363 | ```clojure
364 | (defn hello-count [name & args]
365 | (str "Hello " name ", you passed " (count args) " extra args"))
366 | (hello-count "Finn" 1 2 3)
367 | ; => "Hello Finn, you passed 3 extra args"
368 | ```
369 |
370 | ## Hash-map collections
371 |
372 | ```clojure
373 | (class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
374 | ```
375 |
376 | Keywords are like strings with some efficiency bonuses
377 |
378 | ```clojure
379 | (class :a) ; => clojure.lang.Keyword
380 | ```
381 |
382 | Maps can use any type as a key, but usually keywords are best
383 |
384 | ```clojure
385 | (def stringmap (hash-map "a" 1, "b" 2, "c" 3))
386 | stringmap ; => {"a" 1, "b" 2, "c" 3}
387 |
388 | (def keymap (hash-map :a 1 :b 2 :c 3))
389 | keymap ; => {:a 1, :c 3, :b 2} (order is not guaranteed)
390 | ```
391 |
392 | ??? INFO "Commas are whitespace"
393 | commas are always treated as whitespace and are ignored by the Clojure reader
394 |
395 | Retrieve a value from a map by calling it as a function
396 |
397 | ```clojure
398 | (stringmap "a") ; => 1
399 | (keymap :a) ; => 1
400 | ```
401 |
402 | Keywords can be used to retrieve their value from a map. Strings cannot be used.
403 |
404 | ```clojure
405 | (:b keymap) ; => 2
406 |
407 | ("a" stringmap)
408 | ; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
409 | ```
410 |
411 | Retrieving a non-present value returns nil
412 |
413 | ```clojure
414 | (stringmap "d") ; => nil
415 | ```
416 |
417 | Use assoc to add new keys to hash-maps
418 |
419 | ```clojure
420 | (assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}
421 | ```
422 |
423 | But remember, Clojure types are immutable!
424 |
425 | ```clojure
426 | keymap ; => {:a 1, :b 2, :c 3}
427 | ```
428 |
429 | Use `dissoc` function to remove keys from a hash-map
430 |
431 | ```clojure
432 | (dissoc keymap :a :b) ; => {:c 3}
433 | ```
434 |
435 | ## Sets
436 |
437 | ```clojure
438 | (class #{1 2 3}) ; => clojure.lang.PersistentHashSet
439 | (set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
440 | ```
441 |
442 | Add a value to a set with `conj` (conjoin)
443 |
444 | ```clojure
445 | (conj #{1 2 3} 4) ; => #{1 2 3 4}
446 | ```
447 |
448 | Remove one value from a set with `disj` (disjoin)
449 |
450 | ```clojure
451 | (disj #{1 2 3} 1) ; => #{2 3}
452 | ````
453 |
454 | Test for existence by using the set as a function:
455 |
456 | ```clojure
457 | (#{1 2 3} 1) ; => 1
458 | (#{1 2 3} 4) ; => nil
459 | ```
460 |
461 | There are more functions in the [clojure.sets namespace](https://clojure.github.io/clojure/clojure.set-api.html).
462 |
463 | ## Useful forms
464 |
465 | Logic constructs in clojure are just macros, and look like everything else
466 |
467 | ```clojure
468 | (if false "a" "b") ; => "b"
469 | (if false "a") ; => nil
470 | ```
471 |
472 | Use let to create temporary bindings
473 |
474 | ```clojure
475 | (let [a 1 b 2]
476 | (> a b)) ; => false
477 | ```
478 |
479 | Group statements together with do
480 |
481 | ```clojure
482 | (do
483 | (print "Hello")
484 | "World") ; => "World" (prints "Hello")
485 | ```
486 |
487 | Functions have an implicit `do` function that will call every expression within a function definition.
488 |
489 | ```clojure
490 | (defn print-and-say-hello [name]
491 | (print "Saying hello to " name)
492 | (str "Hello " name))
493 | (print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
494 | ```
495 |
496 | The `let` function also has an implicit `do` function.
497 |
498 | ```clojure
499 | (let [name "Urkel"]
500 | (print "Saying hello to " name)
501 | (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
502 | ```
503 |
504 | ## Namespaces and Libraries
505 |
506 | Namespaces are used to organise code into logical groups. The top of each Clojure file has an `ns` form that defines the namespace name. The domain part of the namespace name is typically the organisation or community name (e.g. GitHub organisation or user account name)
507 |
508 | ```clojure
509 | (ns domain.namespace-name)
510 | ```
511 |
512 | Practicalli projects use a namespace domain of `practicalli` followed by the project name
513 |
514 | ```clojure
515 | (ns practicalli.service-name)
516 | ```
517 |
518 | `require` allows code from one namespace to be accessed from another namespace, either from a the same Clojure project or from a library added to the project classpath.
519 |
520 | The `:as` directive with `require` is used to specify an alias name, a short-hand for the full library name
521 |
522 | Or `:refer [function-name var-name]` can be used to specify specific functions and data (vars) that are available directly
523 |
524 | A required directive is typically added to a namespace form
525 |
526 | ```clojure
527 | (ns practicalli.service-name
528 | (require [clojure.set :as set]))
529 | ```
530 |
531 | Functions from `clojure.set` can be used via the alias name, `set/intersection`, rather than the fully qualified name, `clojure.set/intersection`
532 |
533 | ```clojure
534 | (set/intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
535 | (set/difference #{1 2 3} #{2 3 4}) ; => #{1}
536 | ```
537 |
538 | `:require` directive can be used to include multiple library namespaces
539 |
540 | ```clojure
541 | (ns test
542 | (:require
543 | [clojure.string :as string]
544 | [clojure.set :as set]))
545 | ```
546 |
547 | `require` can be used as a top level expression (not in an `ns` expression). Requires are often used within a rich code block for experimenting with code.
548 |
549 | ```clojure
550 | (comment
551 | (require 'clojure.set :as set))
552 | ```
553 |
554 |
555 | ## Strong Dynamic Types
556 |
557 | Clojure is strongly typed, so everything is a type in Clojure.
558 |
559 | Clojure is dynamically typed, so Clojure infers the type. An explicit type name does not need to be specified in the code, making the code simpler and more concise.
560 |
561 | Clojure is a hosted language and uses the type system of the platform it runs upon. For example, Clojure uses Java object types for booleans, strings and numbers under the covers.
562 |
563 | Use `class` or `type` function to inspect the type of some code in Clojure.
564 |
565 | ```clojure
566 | (type 1) ; Integer literals are java.lang.Long by default
567 | (type 1.); Float literals are java.lang.Double
568 | (type ""); Strings always double-quoted, and are java.lang.String
569 | (type false) ; Booleans are java.lang.Boolean
570 | (type nil); The "null" value is called nil
571 | ```
572 |
573 | Collections in Clojure have their own type too.
574 |
575 | ```
576 | (type [1 2 3]); => clojure.lang.PersistentVector
577 | (type '(1 2 3)); => clojure.lang.PersistentList
578 | ```
579 |
580 | !!! INFO "Type hints"
581 | Type hints can be used to avoid reflection look-ups where performace critical issues have been identified. Type hints are not required in general.
582 | [Clojure Type Hints](https://clojure.org/reference/java_interop#typehints){target=_blank .md-button}
583 |
584 |
585 | ## Java Interop
586 |
587 | Java has a large and very useful standard library which is easily accessible from Clojure. The `java.lang` library is available by default.
588 |
589 | Use import to load a java package
590 |
591 | ```clojure
592 | (import java.util.*)
593 | ```
594 |
595 | Import specific Java classes
596 |
597 | ```clojure
598 | (ns test
599 | (:import
600 | java.util.Date
601 | java.util.Calendar))
602 | ```
603 |
604 | Use the class name with a "." at the end to make a new instance
605 |
606 | ```clojure
607 | (Date.) ;
608 | ```
609 |
610 | Use `.` to call methods. Or, use the ".method" shortcut
611 |
612 | ```clojure
613 | (. (Date.) getTime) ;
614 | (.getTime (Date.)) ; exactly the same thing.
615 | ```
616 |
617 | Use / to call static methods
618 |
619 | ```clojure
620 | (System/currentTimeMillis) ; (system is always present)
621 | ```
622 |
623 | Use doto to make dealing with (mutable) classes more tolerable
624 |
625 | ```clojure
626 | (import java.util.Calendar)
627 | (doto (Calendar/getInstance)
628 | (.set 2000 1 1 0 0 0)
629 | .getTime) ; => A Date. set to 2000-01-01 00:00:00
630 | ```
631 |
--------------------------------------------------------------------------------