├── docs ├── .nojekyll ├── assets │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── images │ │ ├── practicalli-logo.png │ │ └── social │ │ │ └── README.md │ ├── stylesheets │ │ ├── extra.css │ │ └── practicalli-light.css │ ├── favicon.svg │ └── practicalli-logo.svg ├── reference │ ├── clojure-cli │ │ ├── index.md │ │ ├── jvm-options.md │ │ └── example-alias-definitions.md │ └── index.md ├── introduction │ ├── five-steps-to-clojure.md │ ├── concepts │ │ └── index.md │ ├── learning-clojure.md │ ├── who-uses-clojure.md │ ├── first-taste-of-clojure.md │ ├── study-guide.md │ ├── repl-workflow.md │ └── clojure-in-15-minutes.md ├── install │ ├── index.md │ ├── java.md │ └── clojure-cli.md └── index.md ├── CHANGELOG.md ├── .github ├── CODEOWNERS ├── config │ ├── secretlintrc.json │ ├── markdown-link-check.json │ ├── gitleaks.toml │ ├── lychee.toml │ ├── megalinter.yaml │ └── markdown-lint.jsonc ├── pull_request_template.md └── workflows │ ├── changelog-check.yaml │ ├── scheduled-version-check.yaml │ ├── publish-book.yaml │ ├── scheduled-stale-check.yaml │ └── megalinter.yaml ├── .gitattributes ├── overrides ├── main.html ├── partials │ ├── source.html │ ├── palette.html │ └── header.html └── 404.html ├── .gitignore ├── Makefile ├── mkdocs.yml ├── README.md └── LICENSE /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/favicon-16x16.png -------------------------------------------------------------------------------- /docs/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/assets/images/practicalli-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojure-ai-tools/main/docs/assets/images/practicalli-logo.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ### Added 6 | 7 | - project: new clojure ai tools mkdocs book 8 | 9 | ### Changed 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Codeowners 2 | 3 | # Default owner accounts for the current repository 4 | # Automatically added as a reviewr to all pull requests (not including drafts) 5 | 6 | * @practicalli-johnny 7 | -------------------------------------------------------------------------------- /docs/reference/clojure-cli/index.md: -------------------------------------------------------------------------------- 1 | # Reference: Clojure CLI 2 | 3 | A reference on using Clojure CLI and using community tools effectively. 4 | 5 | * structure of the deps.edn configuration 6 | * execution options 7 | * Java Virtual Machine options 8 | * defining custom aliases 9 | * common aliases from practicalli/clojure-deps-edn project 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Configure Languages for GitHub repository using Linguist 2 | 3 | # Markdown & Make detection, 4 | # exclude HTML, CSS & JavaScript 5 | docs/** linguist-detectable 6 | *.md linguist-detectable=true 7 | make linguist-detectable=true 8 | *.css linguist-detectable=false 9 | *.js linguist-detectable=false 10 | *.html linguist-detectable=false 11 | -------------------------------------------------------------------------------- /overrides/main.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | {% extends "base.html" %} 10 | 11 | 12 | {% block announce %} 13 | 14 | Practicalli Clojure AI Tools mini-guide to navigate the complex and confusing array of Artificial Intelligence services and tools 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /.github/config/secretlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "id": "@secretlint/secretlint-rule-basicauth", 5 | "options": { 6 | "allows": [ 7 | "hostname.domain.com", 8 | "jdbc:postgresql://:port/?user=&password=", 9 | "postgres://postgres://username:password@hostname.domain.com:1234/database-name" 10 | ] 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /docs/reference/index.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | Useful information about Clojure presented as a reference, rather than guides as the rest of the book presents. 4 | 5 | 6 | ## Talk Transcripts 7 | 8 | [:globe_with_meridians: Talk transcripts](https://github.com/matthiasn/talk-transcripts) repository contains slides and text from many different speakers and their presentations, including Rich Hickey, Stuart Halloway, David Nolen and Alex Miller. 9 | -------------------------------------------------------------------------------- /docs/assets/images/social/README.md: -------------------------------------------------------------------------------- 1 | # Social Cards 2 | 3 | Social Cards are visual previews of the website that are included when sending links via social media platforms. 4 | 5 | Material for MkDocs is [configured to generate beautiful social cards automatically](https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/), using the colors, fonts and logos defined in `mkdocs.yml` 6 | 7 | Generated images are stored in this directory. 8 | -------------------------------------------------------------------------------- /overrides/partials/source.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | {% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %} 4 | {% include ".icons/" ~ icon ~ ".svg" %} 5 |
6 |
7 | {{ config.repo_name }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude all files from root directory 2 | /* 3 | 4 | # ------------------------ 5 | # Common project files 6 | !CHANGELOG.md 7 | !README.md 8 | !LICENSE 9 | 10 | # ------------------------ 11 | # Include MkDocs files 12 | !docs/ 13 | !includes/ 14 | !overrides/ 15 | !mkdocs.yml 16 | 17 | # ------------------------ 18 | # Project automation 19 | !Makefile 20 | 21 | # ------------------------ 22 | # Version Control 23 | !.gitignore 24 | !.gitattributes 25 | !.github/ 26 | 27 | -------------------------------------------------------------------------------- /.github/config/markdown-link-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^http://localhost" 5 | }, 6 | { 7 | "pattern": "^mailto:*" 8 | }, 9 | { 10 | "pattern": "^#*" 11 | }, 12 | { 13 | "pattern": "^https://127.0.0.0/" 14 | } 15 | ], 16 | "timeout": "20s", 17 | "retryOn429": true, 18 | "retryCount": 5, 19 | "fallbackRetryDelay": "30s", 20 | "aliveStatusCodes": [ 21 | 200, 22 | 206 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/config/gitleaks.toml: -------------------------------------------------------------------------------- 1 | title = "gitleaks config" 2 | 3 | [allowlist] 4 | description = "global allow lists" 5 | paths = [ 6 | '''gitleaks.toml''', 7 | '''(.*?)(jpg|gif|doc|docx|zip|xls|pdf|bin|svg|socket)$''', 8 | '''(go.mod|go.sum)$''', 9 | '''gradle.lockfile''', 10 | '''node_modules''', 11 | '''package-lock.json''', 12 | '''pnpm-lock.yaml''', 13 | '''Database.refactorlog''', 14 | '''vendor''', 15 | ] 16 | 17 | [[rules]] 18 | description = "AWS Example API Key" 19 | id = "aws-example-api-key" 20 | regex = '''AKIAIOSFODNN7EXAMPLE''' 21 | keywords = [ 22 | "awstoken", 23 | ] 24 | -------------------------------------------------------------------------------- /docs/introduction/five-steps-to-clojure.md: -------------------------------------------------------------------------------- 1 | # 5 Steps to Clojure 2 | 3 | ## Set up your environment 4 | 5 | Install Clojure and a build tool 6 | 7 | Setup a Clojure aware editor 8 | 9 | * Emacs & CIDER - Spacemacs, Doom, Prelude 10 | * Neovim & Conjure 11 | * VSCode & Clover or Calva 12 | * Sublime Text & SublimedClojure 13 | 14 | ## Learn the syntax 15 | 16 | ## Practice the core functions 17 | 18 | * 4clojure.org 19 | * Exercism.io 20 | 21 | ### def / defn / let 22 | 23 | ### map / reduce / apply 24 | 25 | ### for / while / loop / recur 26 | 27 | ## Adopt functional programming practices 28 | 29 | ## Learn the commonly used libraries 30 | 31 | ### Server-side websites 32 | 33 | #### Ring / Compojure / Reitit / Hiccup | Selma 34 | 35 | ### React client-side single page apps 36 | 37 | #### React.js / Om-next / Reagent / Re-frame 38 | 39 | #### core.async 40 | 41 | ### Full Stack apps 42 | 43 | #### Kit Framework 44 | -------------------------------------------------------------------------------- /.github/config/lychee.toml: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # Base URL or website root directory to check relative URLs. 4 | base = "https://practical.li/clojure/" 5 | 6 | # Only test links with the given schemes (e.g. https). 7 | # Omit to check links with any other scheme. 8 | # At the moment, we support http, https, file, and mailto. 9 | scheme = ["https"] 10 | 11 | # ---------------------------------------- 12 | # Exclusions 13 | 14 | # Exclude URLs and mail addresses from checking (supports regex). 15 | exclude = ['^https://127.0.0.0', '^https://www\.linkedin\.com', '^https://web\.archive\.org/web/'] 16 | 17 | # Exclude these filesystem paths from getting checked. 18 | exclude_path = ["mkdocs.yml", "overrides", "includes", ".github", ".git"] 19 | 20 | # Exclude all private IPs from checking. 21 | # Equivalent to setting `exclude_private`, `exclude_link_local`, and 22 | # `exclude_loopback` to true. 23 | exclude_all_private = true 24 | 25 | # Check mail addresses 26 | include_mail = false 27 | # ---------------------------------------- 28 | -------------------------------------------------------------------------------- /overrides/404.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | {% extends "main.html" %} 7 | 8 | 9 | {% block content %} 10 |

This is not the page you are looking for

11 | 12 |

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

15 | 16 |

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

19 | 20 |

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

23 | 24 |

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

30 | 31 |

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

37 | 38 | 39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /overrides/partials/palette.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | {% for option in config.theme.palette %} 7 | {% set scheme = option.scheme | d("default", true) %} 8 | {% set primary = option.primary | d("indigo", true) %} 9 | {% set accent = option.accent | d("indigo", true) %} 10 | 25 | {% if option.toggle %} 26 | 34 | {% endif %} 35 | {% endfor %} 36 |
37 | -------------------------------------------------------------------------------- /.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 | ![Clojure Repl workflow - concept](https://github.com/practicalli/graphic-design/blob/live/clojure/clojure-repl-workflow-concept.png?raw=true){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 | [![Sponsor practicalli-johnny](https://raw.githubusercontent.com/practicalli/graphic-design/live/buttons/practicalli-github-sponsors-button.png){ 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 | Creative Commons License 66 | This work is licensed under a Creative Commons Attribution 4.0 ShareAlike License (including images & stylesheets). 67 |
68 | -------------------------------------------------------------------------------- /docs/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | image/svg+xml 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | I 19 | P 20 | 9 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/assets/stylesheets/practicalli-light.css: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Rules 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Color variables 6 | :root { 7 | @extend %root; 8 | 9 | // Primary color shades 10 | --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1); 11 | --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-400)}, 1); 12 | --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1); 13 | --md-primary-bg-color: hsla(0, 0%, 100%, 1); 14 | --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7); 15 | 16 | // Accent color shades 17 | --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1); 18 | --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1); 19 | --md-accent-bg-color: hsla(0, 0%, 100%, 1); 20 | --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7); 21 | } 22 | 23 | // ---------------------------------------------------------------------------- 24 | 25 | // Allow to explicitly use color schemes in nested content 26 | [data-md-color-scheme="practicalli"] { 27 | @extend %root; 28 | } 29 | 30 | // ---------------------------------------------------------------------------- 31 | // Placeholders 32 | // ---------------------------------------------------------------------------- 33 | 34 | // Default theme, i.e. light mode 35 | %root { 36 | 37 | // Default color shades 38 | --md-default-fg-color: hsla(0, 0%, 0%, 0.87); 39 | --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54); 40 | --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32); 41 | --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07); 42 | --md-default-bg-color: hsla(53, 90%, 90%, 1); 43 | --md-default-bg-color--light: hsla(53, 90%, 90%, 0.7); 44 | --md-default-bg-color--lighter: hsla(53, 90%, 90%, 0.3); 45 | --md-default-bg-color--lightest: hsla(53, 90%, 90%, 0.12); 46 | 47 | // Code color shades 48 | --md-code-fg-color: hsla(200, 18%, 26%, 1); 49 | --md-code-bg-color: hsla(0, 0%, 96%, 1); 50 | 51 | // Code highlighting color shades 52 | --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5); 53 | --md-code-hl-number-color: hsla(0, 67%, 50%, 1); 54 | --md-code-hl-special-color: hsla(340, 83%, 47%, 1); 55 | --md-code-hl-function-color: hsla(291, 45%, 50%, 1); 56 | --md-code-hl-constant-color: hsla(250, 63%, 60%, 1); 57 | --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1); 58 | --md-code-hl-string-color: hsla(150, 63%, 30%, 1); 59 | --md-code-hl-name-color: var(--md-code-fg-color); 60 | --md-code-hl-operator-color: var(--md-default-fg-color--light); 61 | --md-code-hl-punctuation-color: var(--md-default-fg-color--light); 62 | --md-code-hl-comment-color: var(--md-default-fg-color--light); 63 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 64 | --md-code-hl-variable-color: var(--md-default-fg-color--light); 65 | 66 | // Typeset color shades 67 | --md-typeset-color: var(--md-default-fg-color); 68 | 69 | // Typeset `a` color shades 70 | --md-typeset-a-color: var(--md-primary-fg-color); 71 | 72 | // Typeset `mark` color shades 73 | --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5); 74 | 75 | // Typeset `del` and `ins` color shades 76 | --md-typeset-del-color: hsla(6, 90%, 60%, 0.15); 77 | --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15); 78 | 79 | // Typeset `kbd` color shades 80 | --md-typeset-kbd-color: hsla(0, 0%, 98%, 1); 81 | --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1); 82 | --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1); 83 | 84 | // Typeset `table` color shades 85 | --md-typeset-table-color: hsla(0, 0%, 0%, 0.12); 86 | 87 | // Admonition color shades 88 | --md-admonition-fg-color: var(--md-default-fg-color); 89 | --md-admonition-bg-color: var(--md-default-bg-color); 90 | 91 | // Footer color shades 92 | --md-footer-fg-color: hsla(0, 0%, 100%, 1); 93 | --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7); 94 | --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3); 95 | --md-footer-bg-color: hsla(0, 0%, 0%, 0.87); 96 | --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32); 97 | 98 | // Shadow depth 1 99 | --md-shadow-z1: 100 | 0 #{px2rem(4px)} #{px2rem(10px)} hsla(0, 0%, 0%, 0.05), 101 | 0 0 #{px2rem(1px)} hsla(0, 0%, 0%, 0.1); 102 | 103 | // Shadow depth 2 104 | --md-shadow-z2: 105 | 0 #{px2rem(4px)} #{px2rem(10px)} hsla(0, 0%, 0%, 0.1), 106 | 0 0 #{px2rem(1px)} hsla(0, 0%, 0%, 0.25); 107 | 108 | // Shadow depth 3 109 | --md-shadow-z3: 110 | 0 #{px2rem(4px)} #{px2rem(10px)} hsla(0, 0%, 0%, 0.2), 111 | 0 0 #{px2rem(1px)} hsla(0, 0%, 0%, 0.35); 112 | } 113 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Practicalli Clojure AI Tools 3 | site_name: Practicalli Clojure AI Tools 4 | site_url: https://practical.li/clojure-ai-tools 5 | site_description: Practical guide to applying AI tools to support the Clojure programming language and Clojure REPL driven development 6 | site_author: Practicalli 7 | site_org: https://practical.li/ 8 | copyright: Copyright © 2023 Practicali CC BY-SA 4.0 9 | repo_url: https://github.com/practicalli/clojure-ai-tools/ 10 | edit_uri: https://github.com/practicalli/clojure-ai-tools/edit/main/docs/ 11 | 12 | # Deployment 13 | # remote_name: origin 14 | remote_branch: gh-pages # deployment branch 15 | 16 | # Theme and styling 17 | theme: 18 | name: material 19 | logo: assets/images/practicalli-logo.png 20 | favicon: assets/favicon.svg 21 | features: 22 | - announce.dismiss 23 | - content.action.edit 24 | - content.action.view 25 | - content.code.annotate 26 | - content.code.copy 27 | - content.tabs.link 28 | - navigation.footer 29 | - navigation.indexes # Nav sections can have files 30 | - navigation.instant # Avoid page reloading for internal links 31 | - navigation.top 32 | - navigation.tracking # Update URL with sub-heading anchor names 33 | palette: 34 | # Palette toggle for automatic mode 35 | - media: "(prefers-color-scheme)" 36 | toggle: 37 | icon: material/brightness-auto 38 | name: Switch to light mode 39 | # Palette toggle for light mode 40 | - media: "(prefers-color-scheme: light)" 41 | scheme: default 42 | primary: blue 43 | accent: teal 44 | toggle: 45 | icon: material/brightness-7 46 | name: Switch to dark mode 47 | # Palette toggle for dark mode 48 | - media: "(prefers-color-scheme: dark)" 49 | scheme: slate 50 | primary: blue 51 | accent: teal 52 | toggle: 53 | icon: material/brightness-4 54 | name: Switch to light mode 55 | # Override theme 56 | custom_dir: overrides 57 | 58 | extra_css: 59 | - assets/stylesheets/extra.css 60 | 61 | ## Additional styling 62 | markdown_extensions: 63 | - admonition 64 | - pymdownx.details 65 | - pymdownx.superfences 66 | - attr_list 67 | - md_in_html # Grids 68 | - footnotes # footnotes and abbreviations 69 | - pymdownx.emoji: 70 | emoji_index: !!python/name:material.extensions.emoji.twemoji 71 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 72 | - pymdownx.highlight: 73 | anchor_linenums: true 74 | - pymdownx.inlinehilite 75 | - pymdownx.snippets: 76 | url_download: true 77 | - pymdownx.superfences: 78 | custom_fences: 79 | - name: mermaid 80 | class: mermaid 81 | format: !!python/name:pymdownx.superfences.fence_code_format 82 | - pymdownx.tabbed: 83 | alternate_style: true 84 | - pymdownx.keys # keyboard keys 85 | - pymdownx.magiclink 86 | - def_list # lists 87 | - pymdownx.tasklist: 88 | custom_checkbox: true # checkboxes 89 | - toc: 90 | permalink: λ︎ 91 | 92 | ## Plugins 93 | plugins: 94 | # Explicitly add search plugin when defining plugins in this configuration file 95 | - search 96 | - callouts 97 | - glightbox # Image aligning 98 | - git-revision-date-localized: # Update and Creation date of each page 99 | # enable_creation_date: true 100 | fallback_to_build_date: true 101 | 102 | # Generate Social Cards via CI only 103 | # in assets/images/social 104 | - social: 105 | cards: !ENV [MKDOCS_SOCIAL_CARDS_GENERATE, true] 106 | 107 | # Redirect pages when moved or changed 108 | # - redirects: 109 | # redirect_maps: 110 | # repl-driven-development.md: introduction/repl-workflow.md 111 | 112 | # Footer / Social Media 113 | extra: 114 | analytics: 115 | provider: google 116 | property: G-N2Y85K2YMM 117 | social: 118 | - icon: material/web 119 | link: https://practical.li/ 120 | - icon: fontawesome/brands/linkedin 121 | link: https://www.linkedin.com/in/jr0cket/ 122 | - icon: fontawesome/brands/slack 123 | link: https://clojurians.slack.com/messages/practicalli 124 | - icon: fontawesome/brands/twitter 125 | link: https://twitter.com/practical_li 126 | - icon: fontawesome/brands/github 127 | link: https://github.com/practicalli 128 | - icon: fontawesome/brands/docker 129 | link: https://hub.docker.com/u/practicalli 130 | 131 | # Navigation 132 | nav: 133 | - Introduction: 134 | - index.md 135 | - Clojure in Fifteen Mins: introduction/clojure-in-15-minutes.md 136 | - REPL Workflow: introduction/repl-workflow.md 137 | - Concepts: 138 | - introduction/concepts/index.md 139 | - introduction/concepts/model-context-protocol.md 140 | - Contributing: https://practical.li/contributing/ 141 | - Writing Tips: https://practical.li/writing-tips/ 142 | - Install: 143 | - install/index.md 144 | - Java Host: install/java.md 145 | - Clojure CLI: install/clojure-cli.md 146 | - Clojure Editors: 147 | - clojure-editors/index.md 148 | - Reference: 149 | - reference/index.md 150 | -------------------------------------------------------------------------------- /docs/introduction/learning-clojure.md: -------------------------------------------------------------------------------- 1 | # Learning Clojure 2 | 3 | Learning the syntax of Clojure is really quick (its very small and simple). Learning to think functionally and discovering the 700+ functions in the Clojure API can take a little longer. I recommend you find someone with a bit of Clojure experience to guide you. 4 | 5 | Here is my suggested path to learning Clojure and thinking functionally. Many of the tasks can be done in parallel. 6 | 7 | ## Simple rather than complex - the foundation of Clojure 8 | 9 | Gaining an appreciation that systems should be simple is a crucial step truly understanding Clojure. So early in your journey into Clojure, spend an hour watching [Rich Hickey talk about Simple made Easy](https://www.infoq.com/presentations/Simple-Made-Easy) - ([transcript of talk](https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md)). 10 | 11 | ## Experience the Clojure syntax 12 | 13 | Take a quick look at the Syntax of Clojure. The syntax is very small, so this will take about 15 minutes to 1 hour (dependent on your own experiences with coding). Don't try to remember all the syntax, it will come through practise. 14 | 15 | - eg. [Clojure in 15 minutes](https://adambard.com/blog/clojure-in-15-minutes/) 16 | 17 | ## Set up an enjoyable environment to work in 18 | 19 | Find how to use Clojure with your favourite editor or IDE. Configure this tool so you can easily run a REPL and evaluate some expressions. 20 | 21 | - [repl.it](https://repl.it) - web based repl you can share / fork 22 | - [Spacemacs](https://spacemacs.org) - for the ultimate Emacs & Vim experience 23 | - [IntelliJ](https://www.jetbrains.com/idea/) and [Cursive](https://cursive-ide.com/) 24 | - [Leiningen](https://leiningen.org) & any editor you like 25 | 26 | ## Building a frame of reference for functional programming 27 | 28 | Find an introductory book that you like which provides lots of example code to help you feel more comfortable with the syntax and more importantly the major concepts of functional programming with Clojure. Type in the exercises as you read and don't be afraid to play around with code examples 29 | 30 | - [Clojure for the Brave and the True](https://www.braveclojure.com/) 31 | - [Living Clojure](http://shop.oreilly.com/product/0636920034292.do) - includes a training guide 32 | - [Practicalli Clojure](/) - you are already here :) 33 | - [ClojureBridge London workshop](https://clojurebridgelondon.github.io/workshop/) - aimed at those new to coding 34 | - [PurelyFunctional - Introduction to Clojure](https://purelyfunctional.tv/courses/introduction-to-clojure-v2/) 35 | 36 | ## Practice Clojure standard library (clojure.core) 37 | 38 | Practice Clojure. Write lots of small and relatively simple examples in Clojure and experiment with the code in the REPL and try break things. This will start helping you learn the [Clojure API](https://clojure.github.io/clojure/) 39 | 40 | You should become comfortable in your understanding of: 41 | 42 | - basic values (strings, numbers, etc) and persistent collections (list, vector, map, set) 43 | - binding names to values and their scope (def, defn, let) 44 | - calling functions, defining functions, arity options for functions 45 | - Higher order functions and basics of functional composition (map, reduce, filter, etc) 46 | - Designing with data, Extensible Data Notation (EDN), data manipulation 47 | 48 | Activities to help practice Clojure include: 49 | 50 | - [4Clojure.org](https://4clojure.org) - aim to complete the first 50 exercises, the first 10 are relatively easy 51 | - Coding Kata exercises 52 | - [Awesome Kata collection](https://github.com/gamontal/awesome-katas) 53 | - [Alice In Wonderland inspired Katas](https://github.com/gigasquid/wonderland-clojure-katas) 54 | - Attend coding dojo events - e.g. [London Clojurians](https://meetup.com/london-clojurians) 55 | 56 | ## Solidify some of the basics you have learned so far 57 | 58 | Work on a relatively small project that you care about enough to work on 59 | 60 | - eg. a tool to help you at work 61 | 62 | ## Learn more tools to help you think functionally 63 | 64 | - mostly using immutable values and pure functions 65 | - functional composition, sequences and transducers 66 | - atoms for managing mutable state changes (with immutable values) 67 | 68 | ## Get a broader view of Clojure and learn some common practices 69 | 70 | Start reading a book which is aimed at intermediate 71 | 72 | - [Clojure CookBook](http://clojure-cookbook.com/) 73 | 74 | Watch Video's about Clojure on subjects that are relevant to work or projects you want to work on. 75 | 76 | - [ClojureTV on YouTube](https://www.youtube.com/user/ClojureTV) 77 | 78 | Follow tutorials on Clojure, especially those that introduce the amazing libraries available in Clojure 79 | 80 | - [Lambda Island](https://lambdaisland.com/) 81 | - [PurelyFunctional.tv](https://purelyfunctional.tv/) 82 | - [Practical.li](https://practicalli,github.io) 83 | 84 | Work with some of the most common libraries in Clojure 85 | 86 | - [Ring]() / [Compojure]() for web development (server side) 87 | - [ClojureScript](https://clojurescript.org/) for web UI or native mobile apps (client side) 88 | - [Reagent](https://reagent-project.github.io/) - reactjs style single page apps 89 | - [Reagent deep dive](http://timothypratley.blogspot.co.uk/2017/01/reagent-deep-dive-part-1.html) - excellent tutorial 90 | - [core.async]() - for asynchronous programming 91 | - [clojure.string]() - specific functions for string manipulation 92 | - [tools.logging](https://clojure.github.io/tools.logging/) 93 | - [java.jdbc](https://clojure.github.io/java.jdbc/) - database access 94 | - [data.zip](https://clojure.github.io/data.zip/) - manipulating trees of data, e.g. XML 95 | -------------------------------------------------------------------------------- /docs/introduction/who-uses-clojure.md: -------------------------------------------------------------------------------- 1 | # Who uses Clojure 2 | 3 | [Hundreds of companies](http://clojure.org/community/companies) actively advertised their Clojure adoption. Given the broad participation in user groups there are clearly many more organizations using Clojure at some level in their technology stack. 4 | 5 | A quick scan of various job sites shows Clojure positions at companies like Walmart, Facebook, Staples, Consumer Reports, Salesforce, and Amazon. It doesn't get much more mainstream than that. 6 | 7 | If you are looking for a new role using Clojure or other functional programming languages, visit [Functional Works](http://functionalworks.com), the only Functional Recruiters in the world. 8 | 9 | Here is just a small and diverse set of example companies that I am aware of that use Clojure for development. 10 | 11 | | Company | Type of applications | 12 | |----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 13 | | Boeing | [Boeing 737 MAX - onboard maintenance](https://www.youtube.com/watch?v=iUC7noGU1mQ) | 14 | | Puppet Labs | DevOps apps & services e.g. trapperkeeper | 15 | | Cisco | Malware analysis & threat intelligence platform (expert system with core.logic) | 16 | | Deuche Bank (UK) | [Processing event streams](http://blog.malcolmsparks.com/) from [Apache Storm](https://storm.apache.org/) | 17 | | Atlassian | Collaborative editing platform for all their products | 18 | | Netflix | Map-Reduce languages for writing apps for Hadoop / Pig | 19 | | USwitch (UK) | Creating meaningful data from multiple sources | 20 | | Daily Mail Online (UK) | Publishing pipeline | 21 | | Circle CI (USA) | [Front-end of CI server](https://github.com/circleci/frontend) in ClojureScript & [test suite](https://circleci.com/blog/rewriting-your-test-suite-in-clojure-in-24-hours/) | 22 | | CitiGroup | Financial Trading | 23 | | Student Loans company (UK) | Loans management system written in Clojure | 24 | | LinkedIn | Powers the LinkedIn social graph | 25 | | Walmart (USA) | eReceipts project to process every purchase from 5,000+ stores | 26 | | SwiftKey (UK) | Predictive intelligence platform (possibly used with Microsoft Cortana) | 27 | | Roomkey.co.uk | [Hotel booking system to rival Expedia](http://www.colinsteele.org/post/23103789647/against-the-grain-aws-clojure-startup) (with a tiny development team) | 28 | | Funding Circle (UK & USA) | Adopting Clojure as their main language (from Ruby, etc) | 29 | | Braintree | [Payment processing pipeline with Kafka](https://www.youtube.com/watch?v=0D3jev1E5ks) | 30 | | Mastodon C | Data centre analysis (Incanta, Storm) | 31 | | Thoughtworks | Agile development for Client projects world wide | 32 | | Vero Insurance (AUS) | Rebuilt policy management system in Clojure with Thoughworks | 33 | | Meta-X | Performance art (Overtone, Quil) | 34 | | Salesforce (USA) | Build & deployment with [Puppet](https://github.com/puppetlabs/puppetserver) & Application Routing with [nginx-clojure](https://nginx-clojure.github.io) | 35 | 36 | There are many more examples of projects on the HackerNews thread: [Ask HN: Who's using Clojure in Production](https://news.ycombinator.com/item?id=8549823) 37 | 38 | ## Tech Radar 39 | 40 | Clojure is also defined as a technology that can be adopted since 2014, according to the Thoughtworks technology radar. 41 | 42 | ![Thoughtworks Technology Radar](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/thoughtworks-tech-radar.png) 43 | 44 | JUXT also created its own [Clojure specific technology radar](https://juxt.pro/radar.html) as there is such an encompassing ecosystem of libraries and services. 45 | -------------------------------------------------------------------------------- /docs/assets/practicalli-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | image/svg+xml 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | I 31 | P 32 | 9 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/install/java.md: -------------------------------------------------------------------------------- 1 | ![Java](https://raw.githubusercontent.com/practicalli/graphic-design/live/banners/practicalli-java-adoptium-banner.png) 2 | 3 | Java is a host platform for Clojure, on which Clojure projects and tools run. Java provides a virtual machine which runs the bytecode generated when Clojure code is compiled. 4 | 5 | Java virtual machine includes a Just In Time (JIT) compiler that optimises running of bytecode. 6 | 7 | !!! INFO "Practicalli recommends OpenJDK version 21" 8 | 9 | 10 | ## Install Java 11 | 12 | Check to see if there is an appropriate version of Java already installed. 13 | 14 | Open a terminal and run the command 15 | 16 | ```shell 17 | java --version 18 | ``` 19 | 20 | If Java is installed and on the execution path, the version infomation is returned 21 | 22 | ![Java version check - dark version](https://github.com/practicalli/graphic-design/blob/live/java/screenshots/java-openjdk-21-version-in-terminal-light.png?raw=true#only-light) 23 | ![Java version check - light version](https://github.com/practicalli/graphic-design/blob/live/java/screenshots/java-openjdk-21-version-in-terminal-dark.png?raw=true#only-dark) 24 | 25 | 26 | === "Debian Packages" 27 | Install Java development kit (JDK) using the `apt` package manager (login as `su -` or prefix the command with `sudo`) 28 | 29 | ```shell 30 | apt install openjdk-21-jdk 31 | ``` 32 | 33 | ??? HINT "Check available versions of OpenJDK" 34 | Long terms support versions should include OpenJDK 17 and may include OpenJDK 21. Check versions available via the `apt` package management tool. 35 | 36 | ```shell 37 | apt search --names-only openjdk 38 | ``` 39 | 40 | ??? HINT "Optionally include Java docs and sources" 41 | Install the `openjdk-21-doc` locally to provide Javadocs to support Java Interop code. 42 | 43 | Install the `openjdk-21-source` package to support navigation of Java Object and Method source code, especially useful when using Java Interoperability from within Clojure code. 44 | 45 | ```shell 46 | sudo apt install openjdk-21-doc openjdk-21-source 47 | ``` 48 | 49 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) provides the `:src/java17` alias to include the Java sources in the classpath when running a REPL. 50 | 51 | If `openjdk-21-jdk` package is not available, add the [Ubuntu OpenJDK personal package archive](https://launchpad.net/~openjdk-r/+archive/ubuntu/ppa){target=_blank} 52 | 53 | !!! NOTE "" 54 | ```shell 55 | sudo add-apt-repository ppa:openjdk-r/ppa 56 | sudo apt-get update 57 | ``` 58 | When multiple versions of Java are installed, set the version using the `update-alternatives` command in a terminal 59 | 60 | !!! NOTE "" 61 | ```shell 62 | sudo update-alternatives --config java 63 | ``` 64 | Available java versions will be listed. Enter the list number for the version you wish to use. 65 | 66 | === "Homebrew" 67 | Using [Homebrew](https://brew.sh/){target=_blank}, run the following command in a terminal to install Java 17: 68 | 69 | !!! NOTE "" 70 | ```shell 71 | brew install openjdk@21 72 | ``` 73 | 74 | ??? HINT "Switching between Java versions" 75 | More than one version of Java can be installed on MacOSX. Set the Java version by opening a terminal and using one of the following commands 76 | 77 | Show the Java versions installed 78 | ```shell 79 | /usr/libexec/java_home -V 80 | ``` 81 | 82 | Switch to Java version 21 83 | ```shell 84 | export JAVA_HOME=$(/usr/libexec/java_home -v 21) 85 | ``` 86 | 87 | Alternatively, install [JEnv Java version manager](https://www.jenv.be/) 88 | 89 | === "Windows" 90 | For Windows 10 use [Windows Subsystem for Linux and Windows Terminal are recommended](https://conan.is/blogging/clojure-on-windows.html){target=_blank} if you have administrative privileges and are happy to use a Unix system on the command line. 91 | 92 | Alternatively use [scoop.sh](https://scoop.sh/){target=_blank}, a command line installer for windows. [Powershell 5](https://aka.ms/wmf5download){target=_blank} or greater is required. 93 | 94 | Follow the [scoop-clojure install instructions](https://github.com/littleli/scoop-clojure){target=_blank}, summarized here: 95 | 96 | ```shell 97 | scoop bucket add java 98 | scoop install temurin-lts-jdk 99 | ``` 100 | 101 | scoop can also be used to [install clojure](clojure-cli.md) 102 | 103 | If neither Scoop or Windows Subsystem for Linux work, try the [Chocolatey](https://chocolatey.org/){target=_blank} package manager. Install the [Java Runtime (JRE)](https://chocolatey.org/packages/javaruntime){target=_blank} using the following command in a command line window 104 | 105 | ```shell 106 | choco install javaruntime 107 | ``` 108 | 109 | If Chocolatey does not work, then try the [manual Java install](install-java.html#manual). 110 | 111 | === "Manual" 112 | [Download OpenJDK from Adoptium](https://adoptium.net/){target=_blank} - pre-build OpenJDK binaries freely available for multiple operating systems. 113 | 114 | Run the file once downloaded and follow the install instructions. 115 | 116 | ![Adoptium Prebuilt OpenJDK Binaries - web page](https://github.com/practicalli/graphic-design/blob/live/java/screenshots/java-adoptium-website-temurin-21.png?raw=true) 117 | 118 | 119 | ## Multiple versions of Java 120 | 121 | [:globe_with_meridians: jenv](https://www.jenv.be/){target=_blank} provides a simple way to switch between multiple installed versions of Java. jenv can be used to set the java version globally, for the current shell or for a specific project by adding `.java-version` file containing the Java version number in the root of the project. 122 | 123 | 124 | ## A little Java Knowledge 125 | 126 | Very little knowledge of the Java language or the Java Virtual Machine is required. 127 | 128 | It is quite simple to call Java methods from Clojure, although there are a wealth of functions and libraries provided by Clojure and its community to minimise the need for Java Interoperability. 129 | 130 | [Reading stack traces](https://8thlight.com/blog/connor-mendenhall/2014/09/12/clojure-stacktraces.html){target=_blank} may benefit from some Java experience, although its usually the first couple of lines in a stack trace that describe the issue. 131 | 132 | Clojure uses its own build tools (Leiningen, Clojure CLI tools) and so Java build tool knowledge is not required. 133 | 134 | When libraries are added to a project, they are downloaded to the `$HOME/.m2` directory. This is the default Maven cache used by all JVM libraries. 135 | 136 | `clojure -Spom` will generate a Maven pom.xml file used for deployment. Understanding of a [minimal Maven POM (pom.xml) file](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#minimal-pom){target=_blank} is useful when managing issues with packaging and deployment. 137 | 138 | [Maven in 5 minutes](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html){target=_blank .md-button} 139 | 140 | The Java Virtual Machine is highly optimised and does not usually require any options to enhance its performance. The most likely configuration to supply to the JVM are to manage the amount of memory assigned, specifically for resource constrained environments. 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Practicalli Clojure AI Tools 2 | 3 | ```none 4 | ██████╗ ██████╗ █████╗ ██████╗████████╗██╗ ██████╗ █████╗ ██╗ ██╗ ██╗ 5 | ██╔══██╗██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██║██╔════╝██╔══██╗██║ ██║ ██║ 6 | ██████╔╝██████╔╝███████║██║ ██║ ██║██║ ███████║██║ ██║ ██║ 7 | ██╔═══╝ ██╔══██╗██╔══██║██║ ██║ ██║██║ ██╔══██║██║ ██║ ██║ 8 | ██║ ██║ ██║██║ ██║╚██████╗ ██║ ██║╚██████╗██║ ██║███████╗███████╗██║ 9 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ 10 | 11 | ██████╗██╗ ██████╗ ██╗██╗ ██╗██████╗ ███████╗ 12 | ██╔════╝██║ ██╔═══██╗ ██║██║ ██║██╔══██╗██╔════╝ 13 | ██║ ██║ ██║ ██║ ██║██║ ██║██████╔╝█████╗ 14 | ██║ ██║ ██║ ██║██ ██║██║ ██║██╔══██╗██╔══╝ 15 | ╚██████╗███████╗╚██████╔╝╚█████╔╝╚██████╔╝██║ ██║███████╗ 16 | ╚═════╝╚══════╝ ╚═════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ 17 | 18 | █████╗ ██╗ ████████╗ ██████╗ ██████╗ ██╗ ███████╗ 19 | ██╔══██╗██║ ╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██╔════╝ 20 | ███████║██║ ██║ ██║ ██║██║ ██║██║ ███████╗ 21 | ██╔══██║██║ ██║ ██║ ██║██║ ██║██║ ╚════██║ 22 | ██║ ██║██║ ██║ ╚██████╔╝╚██████╔╝███████╗███████║ 23 | ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝ 24 | ``` 25 | 26 | > NOTE: Ascii Art Generator: https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Astro%205 27 | 28 | ## Book Overview 29 | 30 | A guide to software Artificial Intelligence tools to support development with the Clojure programming language, using Clojure CLI and a wide range of community tools for a REPL focused workflow. 31 | 32 | The guide uses Practicalli Clojure CLI Config to provide aliases to run over 30 community tools that complement the workflow, including a REPL Reloaded workflow for a highly interactive and effective development experience. 33 | 34 | Learning Clojure syntax and how to think in a functional design is also covered with code examples and challenge that help embed these concepts. 35 | 36 | 37 | ## Book status 38 | 39 | [![MegaLinter](https://github.com/practicalli/clojure/actions/workflows/megalinter.yaml/badge.svg)](https://github.com/practicalli/clojure/actions/workflows/megalinter.yaml)[![Publish Book](https://github.com/practicalli/clojure/actions/workflows/publish-book.yaml/badge.svg)](https://github.com/practicalli/clojure/actions/workflows/publish-book.yaml) 40 | [![Publish Book](https://github.com/practicalli/clojure/actions/workflows/publish-book.yaml/badge.svg)](https://github.com/practicalli/clojure/actions/workflows/publish-book.yaml) 41 | [![pages-build-deployment](https://github.com/practicalli/clojure/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/practicalli/clojure/actions/workflows/pages/pages-build-deployment) 42 | 43 | [![Ideas & Issues](https://img.shields.io/github/issues/practicalli/clojure?label=content%20ideas%20and%20issues&logoColor=green&style=for-the-badge)](https://github.com/practicalli/clojure/issues) 44 | [![Pull requests](https://img.shields.io/github/issues-pr/practicalli/clojure?style=for-the-badge)](https://github.com/practicalli/clojure/pulls) 45 | 46 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/practicalli/clojure?style=for-the-badge) 47 | ![GitHub contributors](https://img.shields.io/github/contributors/practicalli/clojure?style=for-the-badge&label=github%20contributors) 48 | 49 | ## Creative commons license 50 | 51 |
52 | Creative Commons License 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 | [![Sponsor Practicalli via GitHub](https://raw.githubusercontent.com/practicalli/graphic-design/live/buttons/practicalli-github-sponsors-button.png)](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 | [![Star History Chart](https://api.star-history.com/svg?repos=practicalli/clojure-ai-tools&type=Date)](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 | ![Clojure CLI Logo](https://raw.githubusercontent.com/practicalli/graphic-design/live/logos/practicalli-clojure-cli-logo.png){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 | ![Clojure CLI version](https://github.com/practicalli/graphic-design/blob/live/clojure/clojure-cli/clojure-cli-install-check-version.png?raw=true){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 | [![youtube live recorded broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live recorded broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live recorded broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | [![youtube live broadcast](/images/youtube-live-recorded-broadcast.png)](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 | ![Clojure CLI tools - using -P flag to download project dependencies](/images/clojure-cli-tools-dependencies-p-flag.png) 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 | ![Clojure repl driven development](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/clojure-repl-workflow-concept.png){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 | ![Clojure repl driven development using Clojure aware editor](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/clojure-repl-driven-development-clojure-aware-editor.png){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 | ![Clojure Terminal UI REPL](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/rebel/clojure-repl-rebel-eval-map-function-dark.png#only-dark){loading=lazy} 41 | ![Clojure Terminal UI REPL](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/rebel/clojure-repl-rebel-eval-map-function-light.png#only-light){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 | ![Portal - view and navigate Clojure data and event logs](https://raw.githubusercontent.com/practicalli/graphic-design/live/portal/portal-data-browser-example.png) 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 | ![Clojure code static analysis for live linting](https://raw.githubusercontent.com/practicalli/graphic-design/live/spacemacs/screenshots/spacemacs-clojure-live-linting-flycheck-errors-light.png#only-light) 127 | ![Clojure code static analysis for live linting](https://raw.githubusercontent.com/practicalli/graphic-design/live/spacemacs/screenshots/spacemacs-clojure-live-linting-flycheck-errors-dark.png#only-dark) 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 | ![Clojure REPL driven development (RDD) and Test Driven Development (TDD)](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/repl-tdd-flow.png){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 | ![Continuous Integration](https://raw.githubusercontent.com/practicalli/graphic-design/live/continuous-integration/continuous-integration-overview.svg) 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 | --------------------------------------------------------------------------------