├── docs ├── .nojekyll ├── thinking-functionally │ ├── managing-state-changes.md │ ├── arity.md │ ├── persistent-data-structures.md │ ├── pattern-matching.md │ ├── sequences.md │ ├── sequence-abstractions.md │ ├── iterate-over-values.md │ ├── recursion-polymorphism.md │ ├── immutable-values.md │ ├── immutability.md │ ├── functors.md │ ├── immutable-local-bindings.md │ ├── tail-recursion.md │ ├── first-class-functions.md │ ├── function-composition.md │ ├── polymorphism.md │ ├── index.md │ ├── side-effects.md │ ├── immutable-collections.md │ ├── pure-functions.md │ ├── recursion.md │ ├── lazy-evaluation.md │ ├── higher-order-functions.md │ ├── impure-functions.md │ ├── list-comprehension.md │ ├── threading-macros.md │ ├── example-hitchhikers-guide.md │ ├── homoiconicity.md │ └── partial-functions.md ├── code-challenges │ ├── code-kata │ │ ├── palindrome │ │ │ ├── simple-palindrome-test.md │ │ │ └── index.md │ │ └── index.md │ ├── koans.md │ ├── exercism │ │ ├── hamming.md │ │ ├── space-age.md │ │ └── bob │ │ │ └── index.md │ ├── codewars │ │ └── index.md │ ├── index.md │ └── advent-of-code.md ├── puzzles │ ├── index.md │ └── random-seat-assignment.md ├── 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 ├── clojure-design │ ├── naming.md │ └── index.md ├── additional-projects │ ├── clojurebridge-website │ │ ├── responsive-design.md │ │ ├── mount-lifecycle-management.md │ │ ├── add-welcome-section.md │ │ ├── interacting │ │ │ └── index.md │ │ ├── live-reloading.md │ │ ├── add-bluma-css.md │ │ ├── run-the-project.md │ │ ├── create-project.md │ │ ├── add-more-sections.md │ │ ├── index.md │ │ ├── add-content-namespace.md │ │ ├── hiccup-for-html.md │ │ ├── app-state-section.md │ │ ├── deploy-build.md │ │ ├── add-welcome-message.md │ │ ├── organising-the-code.md │ │ ├── github-pages-deploy.md │ │ └── add-navigation.md │ ├── index.md │ └── clojurebridge-london-website.md ├── kata │ └── index.md ├── introduction │ ├── concepts │ │ ├── all-bytecode-in-the-end.md │ │ ├── naming-local.md │ │ ├── purpose.md │ │ ├── naming-things.md │ │ ├── what-is-functional-programming.md │ │ ├── functional-reactive-programming.md │ │ ├── index.md │ │ ├── design.md │ │ └── clojure-from-the-author.md │ ├── five-steps-to-clojure.md │ └── learning-path-wip.md ├── small-projects │ ├── data-transformation │ │ ├── common-english-words.csv │ │ └── index.md │ ├── championships │ │ └── index.md │ ├── generate-web-page.md │ ├── encode-decode │ │ ├── index.md │ │ └── convert-boolean-values.md │ ├── mutating-state │ │ ├── index.md │ │ └── mutants-assemble.md │ ├── index.md │ └── split-the-bill.md ├── first-steps │ ├── index.md │ ├── date-time.md │ ├── hello-world.md │ └── functions.md ├── games │ ├── index.md │ └── tictactoe-cli │ │ ├── create-project.md │ │ └── index.md ├── code-kata │ ├── index.md │ └── tripple-lock.md ├── colours-and-shapes │ └── index.md ├── namespace-design.md ├── index.md └── clojure-repl │ └── index.md ├── includes └── abbreviations.md ├── .github ├── CODEOWNERS ├── config │ ├── secretlintrc.json │ ├── markdown-link-check.json │ ├── gitleaks.toml │ ├── lychee.toml │ └── megalinter.yaml ├── 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 ├── CHANGELOG.md └── Makefile /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/thinking-functionally/managing-state-changes.md: -------------------------------------------------------------------------------- 1 | # Managing state changes 2 | -------------------------------------------------------------------------------- /docs/thinking-functionally/arity.md: -------------------------------------------------------------------------------- 1 | # Arity 2 | 3 | > **Fixme** work in progress 4 | -------------------------------------------------------------------------------- /docs/thinking-functionally/persistent-data-structures.md: -------------------------------------------------------------------------------- 1 | # Persistent data structures 2 | -------------------------------------------------------------------------------- /docs/code-challenges/code-kata/palindrome/simple-palindrome-test.md: -------------------------------------------------------------------------------- 1 | # Simple palindrome test 2 | -------------------------------------------------------------------------------- /docs/puzzles/index.md: -------------------------------------------------------------------------------- 1 | # Puzzles 2 | 3 | Simple puzzles to help you start thinking functionally 4 | -------------------------------------------------------------------------------- /includes/abbreviations.md: -------------------------------------------------------------------------------- 1 | *[HTML]: Hyper Text Markup Language 2 | *[W3C]: World Wide Web Consortium 3 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/favicon-16x16.png -------------------------------------------------------------------------------- /docs/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/assets/images/practicalli-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/learn-clojure/main/docs/assets/images/practicalli-logo.png -------------------------------------------------------------------------------- /docs/clojure-design/naming.md: -------------------------------------------------------------------------------- 1 | # Naming 2 | 3 | - functions (defn, fn) 4 | - data (def) 5 | - namespaces 6 | - require aliases 7 | - specifications 8 | 9 | -------------------------------------------------------------------------------- /docs/thinking-functionally/pattern-matching.md: -------------------------------------------------------------------------------- 1 | # Pattern matching 2 | 3 | > **Fixme** work in progress 4 | 5 | # Regular Expression 6 | 7 | # Destructuring 8 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/responsive-design.md: -------------------------------------------------------------------------------- 1 | # responsive-design 2 | 3 | > #### TODO::work in progress, sorry 4 | > Discuss ideas behind Bulma 5 | -------------------------------------------------------------------------------- /.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/code-challenges/koans.md: -------------------------------------------------------------------------------- 1 | # ClojureScript Koans 2 | 3 | Koans are a collection of small challenges that slowly increase in complexity. They are similar to the 4Clojure challenges in scope. 4 | 5 | > #### TODO::work in progress, sorry 6 | -------------------------------------------------------------------------------- /docs/clojure-design/index.md: -------------------------------------------------------------------------------- 1 | # Clojure Design 2 | 3 | 4 | - [naming](naming.md) functions, symbols, namespaces, projects 5 | - documentation is an essential part of good design (tips on how to make documentation very effective) 6 | - [idioms](idioms/) (the clojure way) 7 | - simple design 8 | - immutability 9 | 10 | -------------------------------------------------------------------------------- /docs/thinking-functionally/sequences.md: -------------------------------------------------------------------------------- 1 | # Sequences 2 | 3 | > **Fixme** work in progress 4 | 5 | Data structures can be built by combining functions 6 | 7 | ```clojure 8 | (cons 1 (cons 2 (cons 3 (cons 4 nil)))) 9 | ``` 10 | 11 | ```clojure 12 | (->> 13 | nil 14 | (cons 4) 15 | (cons 3) 16 | (cons 2) 17 | (cons 1)) 18 | ``` 19 | -------------------------------------------------------------------------------- /.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 Learn Clojure is a minimal guide to Clojure programming for beginners 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /docs/kata/index.md: -------------------------------------------------------------------------------- 1 | # Code Kata: Practising TDD with Clojure 2 | 3 | ## Suggested kata exercises 4 | 5 | * String Calculator 6 | * Bowling Game 7 | * FizzBuzz 8 | * Odd Even 9 | * The Calc Stat 10 | * The Leap Year 11 | * The Prime Factor 12 | * The Recently Used List ([recent song list](recent-song-list.html)) 13 | * The Word Wrap 14 | * The Natural String Sorting 15 | -------------------------------------------------------------------------------- /docs/thinking-functionally/sequence-abstractions.md: -------------------------------------------------------------------------------- 1 | # Sequence abstraction 2 | 3 | > **Fixme** work in progress 4 | 5 | ```clojure 6 | (first '(1 2 3 4 5)) 7 | (rest '(1 2 3 4 5)) 8 | (last '(1 2 3 4 5)) 9 | ``` 10 | 11 | ```clojure 12 | (defn nth [items n] 13 | (if (= n 0) 14 | (first items) 15 | (recur (rest items) (- n 1)))) 16 | 17 | (define squares '(0 1 4 9 16 25)) 18 | 19 | (nth squares 3) 20 | ``` 21 | -------------------------------------------------------------------------------- /.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/assets/images/social/README.md: -------------------------------------------------------------------------------- 1 | # Social Cards 2 | 3 | Social Cards are visual previews of the website that are included when sending links via social media platforms. 4 | 5 | Material for MkDocs is [configured to generate beautiful social cards automatically](https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/), using the colors, fonts and logos defined in `mkdocs.yml` 6 | 7 | Generated images are stored in this directory. 8 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/mount-lifecycle-management.md: -------------------------------------------------------------------------------- 1 | # Mount - lifecycle management 2 | 3 | > #### TODO::work in progress, sorry 4 | > Pull requests are welcome 5 | 6 | Essentially [mount](https://github.com/tolitius/mount) allow you to start and stop parts of your application (components), so you dont have to stop the whole of your application 7 | 8 | TODO: write a simple explanation of [mount](https://github.com/tolitius/mount) 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/introduction/concepts/all-bytecode-in-the-end.md: -------------------------------------------------------------------------------- 1 | # Its all Bytecode in the end 2 | 3 | > The REPL is your compiler 4 | 5 | ![Clojure - conceptual view of how Clojure runs](../images/clojure-evaluation-process.png) 6 | 7 | As soon as you evaluate your code in the REPL it is also being compiled in the background into Java Bytecode. So there is no need for a separate build and run phase. 8 | 9 | Injecting code into the running environment provides the means for fast iterative development of code. 10 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/add-welcome-section.md: -------------------------------------------------------------------------------- 1 | # Refactor welcome to a specific component 2 | 3 | Rename the `landing-page` function to `welcome-message`. 4 | 5 | Below the `welcome-message` function, create a new `landing-page` function definition as follows 6 | 7 | 8 | ```clojure 9 | (defn landing-page [] 10 | [welcome-message]) 11 | ``` 12 | 13 | The `welcome-message` function needs to be defined above the `landing-page` function, as it `welcome-message` is call this new `landing-page` function. 14 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/puzzles/random-seat-assignment.md: -------------------------------------------------------------------------------- 1 | # Random Seat assignment 2 | 3 | 4 | 5 | Take a functional / data oriented approach to solving this problem 6 | 7 | ## Description 8 | 9 | You want to randomly assign seating to a number of people for a fixed number of seats. Each seat is represented by an integer number between 1 and 30. 10 | 11 | How do you randomly assign seats without choosing the same seat twice. 12 | 13 | ## Loop / recur approach 14 | 15 | Bad... 16 | 17 | ## recursive function 18 | -------------------------------------------------------------------------------- /docs/thinking-functionally/iterate-over-values.md: -------------------------------------------------------------------------------- 1 | # iterate Over Values 2 | 3 | This 4 | 5 | > #### Hint::Work in progress 6 | 7 | * loop recur 8 | * reducing functions 9 | * map apply reduce 10 | * partition group-by sort-by 11 | 12 | ## loop recur 13 | 14 | loop recur is a very detailed way of defining a way to iterate over values. map, reduce and apply are commonly used abstractions for iterating over values. They simplify the code (once you are comfortable with them) 15 | 16 | Functions that iterate over values usually treat a string as a sequence of characters. 17 | -------------------------------------------------------------------------------- /docs/small-projects/data-transformation/common-english-words.csv: -------------------------------------------------------------------------------- 1 | a,able,about,across,after,all,almost,also,am,among,an,and,any,are,as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your 2 | -------------------------------------------------------------------------------- /.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/first-steps/index.md: -------------------------------------------------------------------------------- 1 | # First Steps in Clojure 2 | 3 | [Start a Clojure REPL](/learn-clojure/clojure-repl/) and evalute the code examples in this section to gain experience with Clojure. 4 | 5 | 6 | Hello World is a common first line of code in any new language. 7 | 8 | From this humble beginning the following concepts are introduced. 9 | 10 | - fundamental syntax 11 | - expressions 12 | - calling functions and arguments 13 | - implicity types 14 | - homoiconicity 15 | 16 | Exercises 17 | 18 | ## Mathematics 19 | 20 | - number representations 21 | - integer, double, float, ratio, BigInt, BigDecimal, E, M 22 | - clojure.core math functions 23 | - Java math functions 24 | 25 | Exercises 26 | 27 | - Age of Languages 28 | - Convert time to numbers 29 | 30 | -------------------------------------------------------------------------------- /docs/small-projects/data-transformation/index.md: -------------------------------------------------------------------------------- 1 | # Data Transformation 2 | 3 | In a sense all Clojure project are about data transformation, however, these projects will introduce you to many techniques used to transform larger and larger data sets. 4 | 5 | ![data transformation - concept](/images/data-transformation.png) 6 | 7 | | Project | Topics | Overview | 8 | |-----------------------------------------|-----------------------------|----------------------------------------------------------------------------| 9 | | [Most common word](most-common-word.md) | regex filter re-seq sort-by | Find the most common word in a give book that is not a common English word | 10 | -------------------------------------------------------------------------------- /docs/small-projects/championships/index.md: -------------------------------------------------------------------------------- 1 | # Project: Championships 2 | 3 | A project to show the winner, predicted winner and leaderboard from a competition. 4 | 5 | Competitions are defined in a hashmap 6 | 7 | 8 | ```clojure 9 | {:competition :competition-id 10 | :year :2023 11 | :winner "" 12 | :predicted-winner "" 13 | :drivers 14 | [{:name "Max Verstapen" :score 364} 15 | {:name "Sergio Perez" :score 219} 16 | {:name "Fernando Alonso" :score 219} 17 | ,,, 18 | ]} 19 | ``` 20 | 21 | 22 | Data for each race 23 | 24 | ```clojure 25 | {:name :monza 26 | :date "month-day (main race)" 27 | :results 28 | [{:driver "" :score 25}]]} 29 | ``` 30 | 31 | 32 | ## Edge cases 33 | 34 | - Drivers with equal scores considers overall results and highest scores over current season 35 | -------------------------------------------------------------------------------- /docs/thinking-functionally/recursion-polymorphism.md: -------------------------------------------------------------------------------- 1 | # Recursion & Polymorphism 2 | 3 | > **Fixme** work in progress 4 | 5 | The following `sum` function will calculate the value of adding all the elements in a collection. You can alter the results by adding a starting value to the calculation as a second argument when calling `sum` 6 | 7 | ```clojure 8 | (defn sum 9 | ([vals] (sum vals 0)) 10 | ([vals accumulating-total] 11 | (if (empty? vals) 12 | accumulating-total 13 | (sum (rest vals) (+ (first vals) accumulating-total))))) 14 | 15 | (sum [2 7 9 11 13]) 16 | (sum [1]) 17 | (sum [2 7 9 11 13] 9) 18 | ``` 19 | 20 | Rather than duplicate the calculation, the behaviour of calling `sum` with just a collection simply calls `sum` again, this time passing a starting value of zero. 21 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/interacting/index.md: -------------------------------------------------------------------------------- 1 | # Interacting with the website 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | So far our website has been pretty static. We can change the page by saving new function definitions or by updating data in the application state. 6 | 7 | What about user interaction? 8 | 9 | In this section we will learn how to interact with the website and understand how reagent manages updates for us. 10 | 11 | Ideas we could do 12 | 13 | - a daily like / love / interested button - each time the button is pressed it increases the number of likes (eg. a fancy counter). Without persisting the number of likes in a data store, the number of likes would be reset each time the application is deployed. 14 | 15 | - minimise a section, minimise all sections but that which you click on 16 | -------------------------------------------------------------------------------- /docs/code-challenges/exercism/hamming.md: -------------------------------------------------------------------------------- 1 | # Hamming 2 | 3 | 4 | ## Topics covered 5 | 6 | 7 | 8 | !!! HINT "Code for this solution on GitHub" 9 | [practicalli/exercism-clojure-guides](https://github.com/practicalli/exercism-clojure-guides/) contains the design journal and solution to this exercise and many others. 10 | 11 | 12 | ## Create the project 13 | 14 | Download the RNA transcription exercise using the exercism CLI tool 15 | 16 | !!! NOTE "" 17 | ```bash 18 | exercism download --exercise=rna-transcription --track=clojure 19 | ``` 20 | 21 | !!! HINT "Use the REPL workflow to explore solutions locally" 22 | Open the project in a [Clojure aware editor](/clojure/clojure-editors) and [start a REPL](/clojure/coding-challenges/exercism/#repl-workflow), using a rich comment form to experiment with code to solve the challenge. 23 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-values.md: -------------------------------------------------------------------------------- 1 | # Immutable values 2 | 3 | > **Fixme** work in progress 4 | 5 | Values in Clojure include numbers, characters and strings. When you use functions on these values they do not change, instead a new value is returned. 6 | 7 | Lets look at a simple example with a number: 8 | 9 | ```clojure 10 | (def two-little-ducks 22) 11 | 12 | (inc two-little-ducks) 13 | ;; => 23 14 | 15 | two-little-ducks 16 | ;; => 22 17 | ``` 18 | 19 | Another example with a string: 20 | 21 | ```clojure 22 | (def message "Strings are immutable") 23 | 24 | (str message "," " " "you cant change them") 25 | ;; => "Strings are immutable, you cant change them" 26 | 27 | message 28 | ;; => "Strings are immutable" 29 | ``` 30 | 31 | > **Fixme** Add an exercise 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/live-reloading.md: -------------------------------------------------------------------------------- 1 | # Live reloading 2 | 3 | figwheel-main provides live reloading of our application in the browser. When we save a change to the code (JavaScript or CSS), that change is automatically pushed to the browser and the web page is updates. 4 | 5 | You may see a little ClojureScript logo appear briefly. 6 | 7 | 8 | > #### Note::Edit the code to see live reloading 9 | > Edit the file `clojurebridge-landing-page/src/clojurebridge_landing_page/core.cljs` 10 | > 11 | > Change the heading 3 contents in the `hello-world` function 12 | ```clojure 13 | (defn hello-world [] 14 | [:div 15 | [:h1 (:text @app-state)] 16 | [:h3 "Live reloading in the repl makes web development fun!"]]) 17 | ``` 18 | 19 | The web page should have automatically updated 20 | 21 | ![ClojureScript project - live reloading website](/images/cljs-website-run--webpage-live-reloading.png) 22 | -------------------------------------------------------------------------------- /docs/games/index.md: -------------------------------------------------------------------------------- 1 | # Writing Games with Clojure 2 | 3 | > #### TODO::work in progress, sorry 4 | > 5 | > Pull requests are welcome 6 | 7 | Games are driven by events and require state to be managed, so are a good way to explore how to manage state with immutable values. 8 | 9 | For games in Clojure the events are simply function calls and we prefer to pass the state around rather than have a central mutable container for our state. 10 | 11 | This section will contain several games that have been built using a functional approach with immutable data structures. 12 | 13 | * TicTacToe on the command line 14 | 15 | > #### Hint::Games in ClojureScript 16 | > 17 | > There is a section on games in the Practicalli ClojureScript book, including [a TicTacToe game using Reagent](https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/index.html) (react.js style library) and Scalable Vector Graphics (SVG). 18 | -------------------------------------------------------------------------------- /docs/introduction/five-steps-to-clojure.md: -------------------------------------------------------------------------------- 1 | # 5 Steps to Clojure 2 | 3 | ## Set up your environment 4 | 5 | Install Clojure and a build tool 6 | 7 | Setup a Clojure aware editor 8 | 9 | * Emacs & CIDER - Spacemacs, Doom, Prelude 10 | * Neovim & Conjure 11 | * VSCode & Clover or Calva 12 | * Sublime Text & SublimedClojure 13 | 14 | ## Learn the syntax 15 | 16 | ## Practice the core functions 17 | 18 | * 4clojure.org 19 | * Exercism.io 20 | 21 | ### def / defn / let 22 | 23 | ### map / reduce / apply 24 | 25 | ### for / while / loop / recur 26 | 27 | ## Adopt functional programming practices 28 | 29 | ## Learn the commonly used libraries 30 | 31 | ### Server-side websites 32 | 33 | #### Ring / Compojure / Reitit / Hiccup | Selma 34 | 35 | ### React client-side single page apps 36 | 37 | #### React.js / Om-next / Reagent / Re-frame 38 | 39 | #### core.async 40 | 41 | ### Full Stack apps 42 | 43 | #### Kit Framework 44 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/add-bluma-css.md: -------------------------------------------------------------------------------- 1 | # Add Bulma CSS library 2 | 3 | Bulma is a CSS only framework (no JavaScript) that provides a range of easy to apply styles using meaningful style names. Its quite lightweight and therefore fast to load along with your website. 4 | 5 | Bulma also recommends using FontAwesome library, to add common logos such at GitHub. 6 | 7 | > ####Note::Add Bulma CSS and FontAwesome icons to project web page 8 | > Edit the `resources/public/index.html` file in your project 9 | > Add the following line of code inside the `` tag: 10 | ```html 11 | 12 | 13 | ``` 14 | 15 | See the [Practicalli Landing page index.html file](https://github.com/practicalli/practicalli-landing-page/blob/master/resources/public/index.html) for an example 16 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutability.md: -------------------------------------------------------------------------------- 1 | # Immutability 2 | 3 | There is a strong emphasis on immutability in Clojure. Rather than create variables that change, Clojure uses values that do not change. 4 | 5 | Values in Clojure include numbers, characters, strings. 6 | 7 | When functions act on values, a new value is created and returned, rather than modifying the existing value. 8 | 9 | > **TODO** include a diagram to visualise this... 10 | 11 | # Immutabile data structures 12 | 13 | List, Map, Vector and Set are all immutable data structures in Clojure. 14 | 15 | So when you use these data structures with a function, a new data structure is returned. 16 | 17 | > **Hint** When a new data structure is created from an existing data structure, then under the covers the two data structures actually share memory use for any elements that are common. This keeps copies very cheap to create in terms of memory used. 18 | 19 | > See the section on [data structures](/data-structures/) for more details. 20 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/run-the-project.md: -------------------------------------------------------------------------------- 1 | # Run the project 2 | 3 | ## On the command line 4 | 5 | Change into the directory `clojurebridge-landing-page` and run the command 6 | 7 | `lein fig:build` 8 | 9 | or 10 | 11 | `clojure -A:fig:build` 12 | 13 | ## Spacemacs 14 | 15 | `SPC f f` and open the file `clojurebridge-landing-page/src/clojurebridge_landing_page/core.cljs` 16 | 17 | `, "` or `, s I` to run the command `clojurescript-jack-in` 18 | 19 | When prompted for the REPL type, select `figwheel-main` 20 | 21 | ![Spacemacs - ClojureScript REPL - prompt for REPL type](/images/cljs-website-run-spacemacs-repl-type.png) 22 | 23 | When prompted for the build, type `dev` 24 | 25 | ![Spacemacs - ClojureScript REPL - figwheel-main build name](/images/cljs-website-run-spacemacs-build-dev.png) 26 | 27 | 28 | After a few moments, your default browser will automatically open at [http://localhost:9500/] 29 | 30 | ![ClojureScript project - initial website](/images/cljs-website-run--webpage.png) 31 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/create-project.md: -------------------------------------------------------------------------------- 1 | # Create a ClojureScript project 2 | 3 | > ####Note:: Create a ClojureScript website for ClojureBridge London 4 | > 5 | > Create a project using the figwheel template, adding reagent library. 6 | > 7 | > Using [Leiningen](https://github.com/technomancy/leiningen) 8 | ```shell 9 | lein new figwheel-main clojurebridge-landing-page -- --reagent 10 | ``` 11 | > 12 | > or the [Clojure CLI tools](https://clojure.org/guides/getting_started#_clojure_installer_and_cli_tools) and [clj-new](https://github.com/seancorfield/clj-new) 13 | ```shell 14 | clj -A:new figwheel-main clojurebridge-landing-page -- --reagent 15 | ``` 16 | 17 | 18 | This will create a project with a working web page (usually described as a single page app). 19 | 20 | > #### Hint::figwheel-main template 21 | > [figwheel-main-template](https://github.com/bhauman/figwheel-main-template) project provides a simple way to create a minimal ClojureScript project, optionally using reagent, rum, om or react/sablono 22 | -------------------------------------------------------------------------------- /.github/config/lychee.toml: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # Base URL or website root directory to check relative URLs. 4 | base = "https://practical.li/learn-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 | -------------------------------------------------------------------------------- /docs/small-projects/generate-web-page.md: -------------------------------------------------------------------------------- 1 | # Generate Web Page 2 | 3 | Generate a web page from Clojure code, using Hiccup 4 | 5 | Generate a full HTML webpage with content. 6 | 7 | Add a CSS library (bulma.io, bootstrap) to improve generation 8 | 9 | 10 | 11 | ## Summary 12 | 13 | Generating a web page in Clojure shows how easy it is to structure data and transform that data into other structures. 14 | 15 | Although this kind of project is easy enough to just do in a REPL directly, using a Clojure aware editor with a Clojure project makes changes to the code far simpler, without loosing any of the immediate feedback of the REPL. 16 | 17 | Most Clojure developers use the REPL by evaluating code in the editor showing the source code from the project. 18 | 19 | [:fontawesome-solid-book-open: Practicalli Web Services book](https://practical.li/clojure-web-services/) shows how to build websites, create self-documented API's, manage Web Application servers and use databases to persist data. 20 | -------------------------------------------------------------------------------- /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 Learn Clojure book cover 28 | 29 |

30 | 31 |

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

37 | 38 | 39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /docs/thinking-functionally/functors.md: -------------------------------------------------------------------------------- 1 | # Functors 2 | 3 | > **Fixme** work in progress 4 | 5 | Put simply, a function that takes a value and a function as its arguments, eg `map`. The argument pass as a value is most commonly a collection type (vector, map, string, list). 6 | 7 | > From Wikipedia 8 | 9 | > In mathematics, a functor is a type of mapping between categories which is applied in category theory. Functors can be thought of as homomorphisms between categories. In the category of small categories, functors can be thought of more generally as morphisms. 10 | 11 | A functor applies the given function to each element in the the collection by unpacking and each element from the collection and passing it to the function as an argument. The result from each application of the function from the element of the collection is put into a new collection. This new collection is returned once all elements of the original collection have been processed. 12 | 13 | The function, eg. + is applied in turn to each value and returns a structured value as a result, 14 | eg. a list or vector 15 | 16 | ``` 17 | (map inc [1 2 3 4 5]) 18 | 19 | (inc 1 ) 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-local-bindings.md: -------------------------------------------------------------------------------- 1 | # Immutable Local Bindings 2 | 3 | Names can be bound to values & and data structures with either the `def` or `let` function. The `def` binding is global to the namespace, however the `let` function is local to its use. 4 | 5 | > **Hint** The `let` function is typically used to define names within a function definition, or in snippets of code created during repl driven development. 6 | 7 | ```clojure 8 | 9 | (let [five 5] 10 | (str "Within the let expression the value is " five)) 11 | ;; => Within the let expression the value is 5 12 | 13 | ;; evaluating the name five outside the let expression returns an error 14 | five 15 | ;; => Unable to resolve symbol: five in this context 16 | ``` 17 | 18 | > **Note** Create a local binding called number that represents the value 5 using the `let` function. Increment the number, then print out the value of number. 19 | 20 | 21 | ```clojure 22 | (let [number 5] 23 | (inc number) 24 | (str "The number is still " number)) 25 | ``` 26 | 27 | So the value that any local binding points to is immutable too. 28 | 29 | -------------------------------------------------------------------------------- /docs/additional-projects/index.md: -------------------------------------------------------------------------------- 1 | # Additional Projects 2 | 3 | Some guided projects that help you apply many of the ideas learned in the workshop. 4 | 5 | * [Celebrity Name Smash](celebrity-name-smash.html) - string manipulation: subs count, take, str, lazy sequence, apply, reduce 6 | * [Clacks messages](clacks-messages.html) - simple encoding & decoding of messages, thinking about data structures: maps (key value pairs), get, map reduce (extension of the map reduce sandwich) 7 | * [Most common word](https://clojurebridgelondon.github.io/community-docs/docs/curriculum/most-common-word) - find the most common word in a book (not including common English language words, like 'the') 8 | 9 | ## Challenge: Build a new ClojureBridge London website 10 | 11 | Try **[build a new website for ClojureBridge London](clojurebridge-website/)** and give us ideas of what you would like to see in our website. 12 | 13 | 14 | ## Bigger projects 15 | * [Practicalli, Clojure WebApps](http://practicalli.github.io/clojure-webapps/) - Serverside web application with Ring, Compojure and PostgresQL database 16 | * [Tic-tac-toe game in ClojureScript, reagent and SVG](http://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/) 17 | -------------------------------------------------------------------------------- /docs/thinking-functionally/tail-recursion.md: -------------------------------------------------------------------------------- 1 | # Tail recursion 2 | 3 | > **Fixme** work in progress 4 | 5 | If we generate a very large collection we run the risk of blowing our heap space. For example we could use range to generate a very large collection, say a vector containing 10 billion values 6 | 7 | Don't try this example below 8 | 9 | ```clojure 10 | (vec (range 0 9999999999)) 11 | ;; this will crash after a short while as it will use up all your heap space 12 | ``` 13 | 14 | Using tail call optimisation (tail recursion) allows us to reuse a memory location when we call a function recursively. This tail recursion is not part of the underlying Java Virtual Machine (JVM), so instead Clojure has a specific function called `recur` 15 | 16 | The `recur` function allows the processing of a very large data set without blowing the heap space because the memory space will be reused. 17 | 18 | The `recur` function must be the last expression in order to work. 19 | 20 | ```clojure 21 | (defn sum 22 | ([vals] (sum vals 0)) 23 | ([vals accumulating-total] 24 | (if (empty? vals) 25 | accumulating-total 26 | (recur (rest vals) (+ (first vals) accumulating-total))))) 27 | 28 | (sum (vec (range 0 9999999))) 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/code-kata/index.md: -------------------------------------------------------------------------------- 1 | # Kata challenges 2 | 3 | A kata is a small challenge that you attempt to solve in different ways, so experiment with your solutions to these challenges. 4 | 5 | Kata are often coupled with Test Driven Development approach. 6 | 7 | | Project | Topics | Overview | 8 | |---------------------------------------------------|--------|--------------------------------------------------------| 9 | | [Recent song-list](recent-song-list.md) | TDD | Keep a list of recent songs played, without duplicates | 10 | | [Salary Slip Generator](salary-slip-generator.md) | TDD | Generate play slips for an employee | 11 | 12 | [Code Kata Website](http://codekata.com/){target=_blank .md-button} 13 | 14 | ![Kata TDD - lego](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/tdd-kata-lego.png) 15 | 16 | 17 | 18 | ## Suggested kata exercises 19 | 20 | * String Calculator 21 | * Bowling Game 22 | * FizzBuzz 23 | * Odd Even 24 | * The Calc Stat 25 | * The Leap Year 26 | * The Prime Factor 27 | * The Recently Used List ([recent song list](recent-song-list.html)) 28 | * The Word Wrap 29 | * The Natural String Sorting 30 | -------------------------------------------------------------------------------- /docs/thinking-functionally/first-class-functions.md: -------------------------------------------------------------------------------- 1 | # First Class functions 2 | 3 | Idempotent - given the same input you get the same output 4 | 5 | > ####Note:: 6 | > Write an expression to add up the numbers from 1 to 10 and return the overall total. 7 | 8 | ```clojure 9 | (+ 1 2 3 4 5 6 7 8 9 10) 10 | ``` 11 | 12 | > ####Note:: 13 | > Create an expression to do the same calculation, but without having to write all the numbers. Hint: consider the functions called range and reduce. 14 | 15 | The `range` function generates a sequence of numbers and when given arguments it does so from a specific range. The second number is exclusive, so for 1 to 10 the second argument should be 11. 16 | 17 | ```clojure 18 | (range 1 11) 19 | ``` 20 | 21 | Unfortunately we cant just add the result of a range, because it returns a [lazy sequence](lazy-evaluation.html) So `(range)` by itself will create an error 22 | 23 | ```clojure 24 | (+ 1 (range 1 11)) 25 | ``` 26 | 27 | Using a function called `reduce` we can calculate a single total value from all the numbers in the collection. 28 | 29 | The reduce function take 2 arguments, the first is the function to apply to a data structure, the second is the data structure. 30 | 31 | ```clojure 32 | (reduce + (range 1 11)) 33 | 34 | (reduce + (1 2 3 4 5 6 7 8 9 10)) 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/code-challenges/code-kata/palindrome/index.md: -------------------------------------------------------------------------------- 1 | # Project Palindrome 2 | 3 | In this section we will create a simple Clojure project using Leiningen and build up a palindrome checker step by step. 4 | 5 | We will start with the simplest possible thing we can create and steadily add 6 | 7 | ## What is a Palindrome 8 | 9 | For this project it is assumed that a palindrome is a string of characters from the English alphabet and not any other language or an alphanumeric sequence. 10 | 11 | It is assumed that a palindrome is at least 3 characters long, meaning a single character cannot be a palindrome. If a single character was a palindrome, then any valid sequence would contain at least as many palindromes as characters in that sequence. 12 | 13 | ## Task set 14 | 15 | Write an algorithm to find the 3 longest unique palindromes in a string. For the 3 longest palindromes, report the palindrome, start index and length in descending order of length. Any tests should be included with the submission. 16 | 17 | For example, the output for string, "sqrrqabccbatudefggfedvwhijkllkjihxymnnmzpop" should be: 18 | 19 | ``` 20 | Text: hijkllkjih, Index: 23, Length: 10 21 | Text: defggfed, Index: 13, Length: 8 22 | Text: abccba, Index: 5 Length: 6 23 | ``` 24 | 25 | * Check for a palindrome 26 | * Generate a series of palindromes 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ### Added 6 | 7 | - mkdocs: create new book configuration for Practicalli Learn Clojure 8 | - ci: add standard Practicalli workflows for book 9 | - dev: add standard Practicalli Makefile to build & run book locally, check book using MegaLinter 10 | - mkdocs: Creative Commons Licence 11 | - mkdocs: git ignore patterns for mkdocs 12 | - mkdocs: add Google Analytics tag (new property created in Practicalli Websites group) 13 | - mkdocs: instruct GitHub to skip jekyll post processing of gh-pages content 14 | - intro: book introduction, clojure syntax in 15 mins, concepts 15 | - intro: standard practicalli book pages (contribute, writing-tips) 16 | - dev: readme describing book, CI status links and dev workflow 17 | - mkdocs: standard Practicalli book assets (images, stylesheet) 18 | - mkdocs: 404 and announcement theme overrides 19 | - repl: using clojure repl in terminal or editor 20 | - mkdocs: clojure idiom custom admonition 21 | - intro: learning paths page describing aspects of journey into clojure 22 | 23 | ### Changed 24 | - ci: spell lychee & repository trufflehog linters warn only (false positives) 25 | - intro: refactor learning path order and enhance content 26 | - dev: standardise github workflow and mkdocs configuration 27 | - dev: lychee excludes an errors as warnings 28 | - dev: scheduled stale issue & pr check (monthly) 29 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/introduction/concepts/naming-local.md: -------------------------------------------------------------------------------- 1 | # Naming - local scope 2 | 3 | ## Local names in functions 4 | 5 | You can define names for things within the scope of a function using the `let` function. 6 | 7 | ### Example 8 | 9 | You can use the let function to define a simple expression, for which everything will go out of scope once it has been evaluated 10 | 11 | ``` 12 | (let [local-name "some value"]) 13 | (let [minutes-in-a-day (* 60 60 24)]) 14 | ``` 15 | 16 | You can also use `let` inside a function to do something with the arguments passed to that function. Here we calculate the hourly-rate from a yearly salary, returning the calculated-rate. 17 | 18 | (defn hourly-rate [yearly-salary weeks-in-year days-in-week] 19 | (let [calculated-rate (/ yearly-salary weeks-in-year days-in-week)] 20 | calculated-rate)) 21 | 22 | (hourly-rate 60000 48 5) 23 | 24 | ``` 25 | 26 | 27 | 28 | ## Local names in data structures 29 | 30 | When defining a map you are creating a series of key value pairs. The key is essentially a name that represents the value it is paired with. Keys are often defined using a `:keyword`. 31 | 32 | ```clojure 33 | {:radius 10, :pi 22/7 :colour purple} 34 | 35 | (def my-circle {:radius 10, :pi 22/7 :colour purple}) 36 | ``` 37 | 38 | > **Fixme** This is incorrect, as a Clojure keyword type (a name starting with :) have global scope within a namespace. If the keys were strings, then they would have the scope of just the collection. 39 | -------------------------------------------------------------------------------- /docs/thinking-functionally/function-composition.md: -------------------------------------------------------------------------------- 1 | # Function Composition 2 | 3 | We have discussed how functional programs are essentially a number of functions that work together, this is called composition (functional composition). 4 | 5 | ``` 6 | (let [calculated-value (* 10 (reduce + (map inc (range 5))))] 7 | calculated-value) 8 | ``` 9 | 10 | This expression is common in the Lisp & Clojure languages. Occasionally the created expressions can becomes challenging to read. To overcome this parsing complexity, developers often break down a more complex expression into its parts, extracting code into its own function. 11 | 12 | > **Note** Brake down the above example into each expression that gives a value 13 | 14 | 15 | 16 | ``` 17 | (range 5) 18 | 19 | (map inc (range 5)) 20 | 21 | (reduce + (map inc (range 5))) 22 | 23 | (* 10 (reduce + (map inc (range 5)))) 24 | 25 | 26 | ;; Additional examples 27 | 28 | ;; Use a let expression for code that is used more than once in a function 29 | 30 | (let [calculated-value (* 10 (reduce + (map inc (range 5))))] 31 | calculated-value) 32 | 33 | ;; Use defn to define a function for code that multiple functions will call 34 | ;; and generalise the function with arguments 35 | 36 | (defn common-data-calculation 37 | [certainty-factor scope] 38 | (* certainty-factor (reduce + (map inc (range scope))))) 39 | ``` 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/thinking-functionally/polymorphism.md: -------------------------------------------------------------------------------- 1 | # Polymorphic function definitions 2 | 3 | Polymorphic means many forms. 4 | 5 | The simplest example of polymorphism in Clojure is a function definition that acts differently based on the number of arguments passed. 6 | 7 | Usually you define a function with one set of arguments, either none `[]`, one `[one]` or many `[any number of args]`, using the basic syntax 8 | 9 | ```clojure 10 | (defn name 11 | "I am the doc string to describe the function" 12 | [args] 13 | (str "define your behaviour here")) 14 | ``` 15 | 16 | Instead of writing multiple functions with the same name that each take different numbers of arguments, you can use the following polymorphic syntax in Clojure 17 | 18 | ```clojure 19 | (defn name 20 | "I am the doc string to describe the function" 21 | ([] 22 | (str "behaviour with no args")) 23 | ([one] 24 | (str "behaviour with one arg")) 25 | ([one two & args] 26 | (str "behaviour with multiple args"))) 27 | ``` 28 | 29 | > **Note** Write a simple function called `i-am-polly` that returns a default message when given no arguments and a custom message when given a custom string as an argument 30 | 31 | 32 | 33 | ```clojure 34 | (defn i-am-polly 35 | ([] (i-am-polly "My name is polly")) 36 | ([message] (str message))) 37 | 38 | (i-am-polly) 39 | (i-am-polly "I call different behaviour depending on arguments sent") 40 | ``` 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/thinking-functionally/index.md: -------------------------------------------------------------------------------- 1 | # Thinking Functionally 2 | 3 | In this section I cover some simple examples of Clojure code to help you think about the concepts involved in functional programming. 4 | 5 | An overview of thinking functionally is also covered in the presentation entitled [Getting into Functional Programming with Clojure](http://www.slideshare.net/jr0cket/cebit-get-into-functional-programming-with-clojure) on slideshare and its accompanying [youtube video](https://www.youtube.com/watch?v=mEfqULqChZs) 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | [![Join the conversation on Clojurians Slack](/images/practicalli-slack-channel.png)](https://clojurians.slack.com/messages/practicalli) 14 | 15 | Get a [free Clojurians slack community account](https://clojurians.net/) 16 | -------------------------------------------------------------------------------- /docs/thinking-functionally/side-effects.md: -------------------------------------------------------------------------------- 1 | # Side effects 2 | 3 | A side effect is something that creates a change outside of the current code scope, or something external that affects the behaviour or result of executing the code in the current scope. 4 | 5 | ## Nondeterministic - the complexity iceberg 6 | 7 | When you have side effects, you cannot reason accurately about a piece of the code. 8 | 9 | In order to understand a piece of code you must look at all possible side effects created in all lines of code to ensure you fully understand the result of executing your code. 10 | 11 | With side effects in your system, complexity is hidden, causing a far greater risk of a dangerous situation. 12 | 13 | ## Side causes - side effects 14 | 15 | You can think about these effects is in two specific areas, **Side Causes** and **Side Effects** 16 | 17 | * **Side Causes** - are where other pieces of code (function) or state change affects the behaviour of a function. 18 | 19 | * **Side Effects** - are where the current code (function) affects the rest of the system 20 | 21 | [![Side Causes & Side Effects - Kris Jenkins](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/side-causes-side-effects.png)](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/side-causes-side-effects.png) 22 | 23 | > #### Hint::Side Causes term 24 | > 25 | > The term of side causes was coined by [Kris Jenkins](https://twitter.com/krisajenkins) in the superb article [What is Functional Programming?](http://blog.jenkster.com/2015/12/what-is-functional-programming.html) 26 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/add-more-sections.md: -------------------------------------------------------------------------------- 1 | # Add a function to define each section 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | List of section ideas 6 | - Registration (sign up button, date, location, etc.) 7 | - Current sponsor (details of the current sponsor, sponsor messaging) 8 | - ClojureBridge overview (description of the event) 9 | - Clojure Showcase (some projects that demonstrate what Clojure can do) 10 | - Learning paths (links to the various curriculums) 11 | - Clojure Installation (how to set up a development environment) 12 | - ClojureBridge Schedule (what happens and when) 13 | - Resources (documentation, how to practice clojure, books, videos, etc) 14 | - Coaching guide (documentation to help coaches coach the students) 15 | - Sponsors guide (how to sponsor ClojureBridge and what to get out of it) 16 | - Past events (overview of all previous events, date, location, sponsors, etc.) 17 | - Models of learning (ideas on how to learn more effectively) 18 | 19 | 20 | 21 | 22 | Create a function for each section of the website you want to add. 23 | 24 | > ####HINT::Reagent examples 25 | > [Introduction to Reagent](https://reagent-project.github.io/) has many simple examples of functions you can include in the website 26 | > 27 | > [Guide to Reagent](https://purelyfunctional.tv/guide/reagent/) has even more examples 28 | 29 | #### Create a banner heading using Bootstrap hero style 30 | 31 | ```clojure 32 | (defn website-title [] 33 | [:section {:class "hero"} 34 | [:h1 (get-in @app-state [:website :title])] 35 | [:h4 (get-in @app-state [:website :description])]]) 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/code-kata/tripple-lock.md: -------------------------------------------------------------------------------- 1 | # Code Kata: Combination Lock 2 | 3 | An exercise to become more comfortable with the concept of _List Comprehension_ 4 | 5 | 6 | 7 | Lisp comprehension 8 | 9 | 10 | In Clojure, list comprehension is via the [for](https://clojuredocs.org/clojure.core/for) function 11 | 12 | * Clojure docs: [for](https://clojuredocs.org/clojure.core/for) 13 | * Reference: [Lisp Comprehension in Clojure](https://practicalli.github.io/clojure/thinking-functionally/list-comprehension.html) - Clojure, practicalli 14 | 15 | 16 | 17 | > ####Note::Generate all the combinations for a lock with 3 combination tumblers 18 | > Each combination is made up of three numbers (the tumblers). 19 | > Each number is between 0 and 9 20 | ```eval-clojure 21 | 22 | ``` 23 | 24 | 25 | The range function can generate a sequence of numbers, so could be used to create each tumbler. 26 | 27 | Combinations can be returned as a collection, eg. a vector. 28 | 29 | 30 | 31 | 32 | ```clojure 33 | (for [tumbler-1 (range 10) 34 | tumbler-2 (range 10) 35 | tumbler-3 (range 10)] 36 | [tumbler-1 tumbler-2 tumbler-3]) 37 | ``` 38 | 39 | 40 | 41 | 42 | ## Alternative Exercises 43 | 44 | Here are some exercises that could also be solved with list comprehension 45 | 46 | * Fizzbuzz 47 | 48 | -------------------------------------------------------------------------------- /docs/code-challenges/code-kata/index.md: -------------------------------------------------------------------------------- 1 | # Code Kata 2 | 3 | A kata is a small challenge that you attempt to solve in different ways, to experiment with alternative solutions to a challenge and widen experience. 4 | 5 | Kata help gain understanding in a wider range of Clojure functions and ways to represent values in Clojure. 6 | 7 | Code Kata can also be used to practice a Test Driven Development (TDD) approach. 8 | 9 | ## Guided Challenges 10 | 11 | Challenges with design journals that describe and demonstrate different design choices and constraints. 12 | 13 | | Project | Topics | Overview | 14 | |---------------------------------------------------|--------|--------------------------------------------------------| 15 | | [Recent song-list](recent-song-list.md) | TDD | Keep a list of recent songs played, without duplicates | 16 | | [Salary Slip Generator](salary-slip-generator.md) | TDD | Generate play slips for an employee | 17 | | [Palindrome](palindrome/) | | transforming data structures | 18 | 19 | 20 | ## Resources 21 | 22 | External sites with example kata challenges 23 | 24 | - [Awesome Kata collection](https://github.com/gamontal/awesome-katas) 25 | - [Alice In Wonderland inspired Katas](https://github.com/gigasquid/wonderland-clojure-katas) 26 | 27 | 28 | [Code Kata Website](http://codekata.com/){target=_blank .md-button} 29 | 30 | ![Kata TDD - lego](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/tdd-kata-lego.png) 31 | -------------------------------------------------------------------------------- /docs/code-challenges/exercism/space-age.md: -------------------------------------------------------------------------------- 1 | # Space Age 2 | 3 | ![Exercism](https://raw.githubusercontent.com/practicalli/graphic-design/live/banners/exercism/exercisim-exercise-space-age-banner.png) 4 | 5 | ## Topics covered 6 | 7 | 8 | !!! HINT "Code for this solution on GitHub" 9 | [practicalli/exercism-clojure-guides](https://github.com/practicalli/exercism-clojure-guides/) contains the design journal and solution to this exercise and many others. 10 | 11 | ## Create the project 12 | 13 | Download the RNA transcription exercise using the exercism CLI tool 14 | 15 | !!! NOTE "" 16 | ```bash 17 | exercism download --exercise=rna-transcription --track=clojure 18 | ``` 19 | 20 | !!! HINT "Use the REPL workflow to explore solutions locally" 21 | Open the project in a [Clojure aware editor](/clojure/clojure-editors) and [start a REPL](/clojure/coding-challenges/exercism/#repl-workflow), using a rich comment form to experiment with code to solve the challenge. 22 | 23 | 24 | ## Challenge introduction 25 | 26 | Given an age in seconds, calculate how old someone would be on: 27 | 28 | - Earth: orbital period 365.25 Earth days, or 31557600 seconds 29 | - Mercury: orbital period 0.2408467 Earth years 30 | - Venus: orbital period 0.61519726 Earth years 31 | - Mars: orbital period 1.8808158 Earth years 32 | - Jupiter: orbital period 11.862615 Earth years 33 | - Saturn: orbital period 29.447498 Earth years 34 | - Uranus: orbital period 84.016846 Earth years 35 | - Neptune: orbital period 164.79132 Earth years 36 | 37 | So if you were told someone were 1,000,000,000 seconds old, you should be able to say that they're 31.69 Earth-years old. 38 | -------------------------------------------------------------------------------- /docs/introduction/concepts/purpose.md: -------------------------------------------------------------------------------- 1 | # When to use Clojure 2 | 3 | Clojure is a general purpose language suitable for any kind of application or service. As Clojure implementations run across multiple technology platforms and operating systems, there are very few barriers to its use. 4 | 5 | So Clojure is great for webapps, data science, big data, finance industry (banking, trading, insurance, etc), devops tools (log analysis, etc) and anything else really. 6 | 7 | There are areas where Clojure obviously excels. 8 | 9 | ## Effective Data Manipulation 10 | 11 | Fundamentally all software systems take in data (in the form of values or events), process or react to that data and return as a result. 12 | 13 | The persistent data structures in Clojure (list, vector, hash-map and set) provide an efficient way to use immutable collections of data. 14 | 15 | The `clojure.core` library contains a vast number of data processing functions in Clojure so data is easily transformed 16 | 17 | ## Highly Scalable 18 | 19 | Clojure code is encouraged to be immutable and functions to be pure, you can run millions of parallel instances of your application or service for massive processing power. These features also vastly simplify concurrent programming. 20 | 21 | ## Reducing Complexity 22 | 23 | Clojure encourages a component design through functional composition, breaking down problems into components 24 | 25 | Clojure and its libraries are all great examples of well designed components and the community strongly encourages this approach. 26 | 27 | > #### Hint::Functional Reactive Programming 28 | > 29 | > You can also use ClojureScript for Functional Reactive programming of client-side apps for browsers and mobile device. 30 | -------------------------------------------------------------------------------- /docs/introduction/learning-path-wip.md: -------------------------------------------------------------------------------- 1 | 2 | ## Learn more tools to help you think functionally 3 | 4 | - mostly using immutable values and pure functions 5 | - functional composition, sequences and transducers 6 | - atoms for managing mutable state changes (with immutable values) 7 | 8 | 9 | ## Get a broader view of Clojure and learn some common practices 10 | 11 | Start reading a book which is aimed at intermediate 12 | 13 | - [Clojure CookBook](http://clojure-cookbook.com/) 14 | 15 | Watch Video's about Clojure on subjects that are relevant to work or projects you want to work on. 16 | 17 | - [ClojureTV on YouTube](https://www.youtube.com/user/ClojureTV) 18 | 19 | Follow tutorials on Clojure, especially those that introduce the amazing libraries available in Clojure 20 | 21 | - [Lambda Island](https://lambdaisland.com/) 22 | - [PurelyFunctional.tv](https://purelyfunctional.tv/) 23 | - [Practical.li](https://practicalli,github.io) 24 | 25 | Work with some of the most common libraries in Clojure 26 | 27 | - [Ring]() / [Compojure]() for web development (server side) 28 | - [ClojureScript](https://clojurescript.org/) for web UI or native mobile apps (client side) 29 | - [Reagent](https://reagent-project.github.io/) - reactjs style single page apps 30 | - [Reagent deep dive](http://timothypratley.blogspot.co.uk/2017/01/reagent-deep-dive-part-1.html) - excellent tutorial 31 | - [core.async]() - for asynchronous programming 32 | - [clojure.string]() - specific functions for string manipulation 33 | - [tools.logging](https://clojure.github.io/tools.logging/) 34 | - [java.jdbc](https://clojure.github.io/java.jdbc/) - database access 35 | - [data.zip](https://clojure.github.io/data.zip/) - manipulating trees of data, e.g. XML 36 | -------------------------------------------------------------------------------- /docs/small-projects/encode-decode/index.md: -------------------------------------------------------------------------------- 1 | # Encoding and Decoding with Clojure 2 | 3 | ![Matrix decode](/images/matrix-decode.png) 4 | 5 | Projects that use a range of ciphers, from simple to more complex, to encode and decode text. 6 | 7 | A common approach to encoding and decoding text is to use a dictionary lookup, defined in Clojure as a hash-map. Each key-value pair provides a mapping for encoding and decoding. Looking up a a character as a key in the map provides a value that is the encrypted character. 8 | 9 | These projects show several ways to transform data in Clojure. 10 | 11 | | Project | Topics | Description | 12 | |------------------------------------------------------|------------------|-------------------------------------------------| 13 | | [Boolean names to 0 or 1](convert-boolean-values.md) | hash-map get | Convert boolean values to classic 1 or 0 values | 14 | | [Caesar cipher - ROT13](caesar-cipher rot13.md) | seq cycle zipmap | A simple alphabet rotation cipher | 15 | | [RNA / DNA converter](rna-dna.md) | | Convert between DNA and RNA | 16 | | [Clacks telegram](clacks.md) | | Encoding and decoding messages with Clacks | 17 | 18 | ## Examples of Encoding 19 | 20 | * [Portable Network Graphics for image compression](https://en.wikipedia.org/wiki/Portable_Network_Graphics) 21 | * [Vorbis for music and video compression](https://en.wikipedia.org/wiki/Vorbis) plus several commercial compression encoders 22 | * [Enigma machine - encrypted communications](https://www.google.com/search?q=clojure+enigma+machine) 23 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/index.md: -------------------------------------------------------------------------------- 1 | # Live ClojureScript websites 2 | 3 | > ####Info::This section is new, so feedback is appreciated 4 | 5 | This tutorial guides you through the creation of a relatively simple, responsive website that is deployed live on the Internet. 6 | 7 | You will discover how to use the following tools: 8 | - ClojureScript - writing functions for sections of content 9 | - Reagent - add basic state management to the website 10 | - Bulma - a CSS only framework for responsive design 11 | - Figwheel-main - an interactive development environment for ClojureScript 12 | - GitHub Pages - a free service for deploying your website on the Internet 13 | 14 | 15 | ### Example ClojureScript websites 16 | * [ClojureBridge London](https://clojurebridgelondon.github.io/), a landing page for the event using Bulma CSS, global navigation and responsive design. 17 | * [ClojureBridge London source code](https://github.com/ClojureBridgeLondon/clojurebridge-landing-page) 18 | * [Practicalli](https://practicalli.github.io/), a landing page using Bulma CSS (similar to the above) 19 | * [Clojure Study Group project](https://github.com/practicalli/clojure-study-group-website) a ClojureScript and bootstrap website 20 | 21 | 22 | 23 | ## Looking for something more? 24 | 25 | [pesterhazy/cljs-spa-example](https://github.com/pesterhazy/cljs-spa-example.git) is a more involved example of a Single Page Application in ClojureScript. This project uses figwheel-main, reagent, webpack, router5, yarn package management for npms, 26 | 27 | 28 | ## References 29 | * [Reagent Mysteries - part 1: vectors and sequences](https://presumably.de/reagent-mysteries-part-1-vectors-and-sequences.html) 30 | * [SVG in reagent](https://www.mattgreer.org/articles/embedding-svg-into-a-reagent-component/) 31 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/add-content-namespace.md: -------------------------------------------------------------------------------- 1 | # Refactor content to its own namespace 2 | 3 | > #### TODO::work in progress, sorry 4 | 5 | Create a new namespace called `clojurebridge-landing-page.content` in the file `src/clojurebridge_landing_page/content.cljs` 6 | 7 | Move all function definitions, except `landing-page` from the content section to the new file for the namespace. 8 | 9 | ## require the `content` namespace 10 | 11 | Edit the the file `src/clojurebridge_landing_page/core.cljs`. 12 | 13 | Update the `clojurebridge-landing-page.core` namespace to require the new `clojurebridge-landing-page.content` namespace 14 | 15 | The `content` namespace should be given the alias `content` 16 | 17 | ```clojure 18 | (ns ^:figwheel-hooks clojurebridge-london-landing-page.core 19 | (:require 20 | [goog.dom :as gdom] 21 | [reagent.core :as reagent :refer [atom]] 22 | [clojurebridge-landing-page.content :as content])) 23 | ``` 24 | 25 | ## Add the alias to the function calls 26 | 27 | 28 | ```clojure 29 | (defn main-page [] 30 | [:div 31 | [content/navigation-top] 32 | [content/banner-columns] 33 | [content/sponsor-current (get-in @app-state [:sponsors :current])] 34 | (content/level-separator "overview") 35 | [content/overview] 36 | (content/level-separator "showcase") 37 | [content/showcase] 38 | (content/level-separator "learning-paths") 39 | [content/learning-paths] 40 | (content/level-separator "install") 41 | [content/install] 42 | (content/level-separator "schedule") 43 | [content/schedule] 44 | (content/level-separator "resources") 45 | [content/resources] 46 | (content/level-separator "coaches") 47 | [content/coaches] 48 | (content/level-separator "sponsors") 49 | [content/sponsors] 50 | ]) 51 | 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/small-projects/mutating-state/index.md: -------------------------------------------------------------------------------- 1 | # Mutating State in a Controlled way 2 | 3 | Mutating state should be used carefully and sparingly in Clojure (and all other programming languages). 4 | 5 | `atom` is a mutable container that can manage any value. The atom ensures that only one call at a time can affect the value it manages. This is part of the [software transactions memory system](https://clojure.org/reference/refs) in Clojure. 6 | 7 | As the atom is mutable in that the value it manages can be changed, however, this must be done with special commands (swap!, reset!, compare-and-set!, swap-vals!). 8 | 9 | Even though the atom is mutable, the values it manages are not. They are normal immutable (unchangeable) Clojure values. 10 | 11 | `ref` is similar to `atom` and can manage transactions, ensuring that all changes happen or no changes happen. 12 | 13 | | Project | Topics | Overview | 14 | |------------------|-----------------------|-----------------------------------------------------------------------------------------------| 15 | | Mutants assemble | atom swap! reset! | Using an atom to manage state changes | 16 | | Undo/Redo | atom add-watch | Traversing the history of an atom | 17 | | Poker game | atom swap! reset! ref | Simple transaction management using atom and ref in a card game, using constraints on an atom | 18 | 19 | ## References 20 | 21 | * [Atoms](https://clojure.org/reference/atoms) - clojure.org 22 | * [Refs and Transactions](https://clojure.org/reference/refs) - clojure.org 23 | * [Agents](https://clojure.org/reference/agents) - clojure.org 24 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/hiccup-for-html.md: -------------------------------------------------------------------------------- 1 | # Writing html in Clojure with hiccup 2 | 3 | Rather than write `
`, `

`, `

` pairs of tags in html, we define our content using a syntax called hiccup. 4 | 5 | A vector, `[]` is used to hold the name of the html tag, represented by a keyword such as `:div` 6 | 7 | Defining our content in this way makes it easier to generate and transform using Clojure, so you can generate structure and content dynamically too. 8 | 9 | For example this simple html code uses open and closing tags to define the scope of where to apply that tag. 10 | 11 | ```html 12 |
13 |

Hello World

14 |

Live reloading in the repl makes web development fun!

15 |
16 | ``` 17 | 18 | In Clojure, we only have the one tag, which is represented by a keyword that has the same name as the HTML tag. The vector defines the scope of the tag. 19 | 20 | Using vectors for scope its trivial to use structured editing to organise and refactor the structure of your web page content. 21 | 22 | 23 | ```clojure 24 | (defn hello-world [] 25 | [:div 26 | [:h1 (:text @app-state)] 27 | [:h3 "Live reloading in the repl makes web development fun!"]]) 28 | ``` 29 | 30 | ## Using state for dynamic content 31 | 32 | We can also use dynamic information from the application state, defined as an atom called `app-state`. As this name refers to an atom, we need to use the `@` character or the `deref` function to access its values 33 | 34 | In this example, the atom contains a Clojure map with a key called `text`. We can get the value associated with `text` in the map by de-referencing the atom. 35 | ```clojure 36 | [:h1 (get @app-state :text)] 37 | ``` 38 | 39 | Its quite common to use the short form when the key of a map is the keyword type 40 | ```clojure 41 | [:h1 (:text @app-state)] 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/colours-and-shapes/index.md: -------------------------------------------------------------------------------- 1 | # Colours and Shapes 2 | 3 | The [Colours and Shapes in Clojure learning path](https://www.maria.cloud/gist/9afbc2adf3638f1bcf6a6c08b6f33226) uses an interactive website called Maria.cloud. It lets you write code and see the results straight away. You can also make your own note and save them once you have taken a copy. 4 | 5 | > ####Hint::Requirements 6 | > We will ask you to login to Maria.cloud with your GitHub account. 7 | > 8 | > Please [create a free GitHub account](https://github.com/join) if you do not already have one. 9 | 10 | 11 | ## Sign-in to Maria.cloud 12 | 13 | Open the [Maria.cloud website](https://www.maria.cloud/) and sign-in using the same GitHub account. 14 | 15 | You may be prompted to authorize Maria.cloud access to your GitHub account. This is safe (and can be revoked after the event) 16 | 17 | ![Maria.cloud GitHub access request](/images/clojurebridge-london-maria-cloud-github-authorize-maria-.png) 18 | 19 | 20 | ## Open and duplicate the ClojureBridge London curriculum 21 | 22 | Click on the [start here](https://www.maria.cloud/cb-london) link for the Feb 2020 London ClojureBridge event 23 | 24 | ![Maria.cloud ClojureBridge London course](/images/clojurebridge-london-events-london-clojure-bridge.png) 25 | 26 | Press the duplicate button to create your own copy (so you can save all your code) 27 | 28 | ![Maria.cloud duplicate course](/images/clojurebridge-london-maria-cloud-duplicate.png) 29 | 30 | You should now have your own copy, as show in the top bar 31 | 32 | ![Maria.cloud duplicated course](/images/clojurebridge-london-maria-cloud-duplicated-course.png) 33 | 34 | 35 | > #### Hint::Duplicate not working? 36 | > Try logging out of maria.cloud and then logging back in again (I know, but it might work) 37 | > 38 | > If you are using Chrome, try using an Incognito browser tab or window. 39 | > Or try Firefox browser. 40 | -------------------------------------------------------------------------------- /docs/first-steps/date-time.md: -------------------------------------------------------------------------------- 1 | # Dates and Time 2 | 3 | 4 | 5 | Introduction to Java Interoperability 6 | 7 | Use cases for dates and times 8 | 9 | - timestamps on log events 10 | - calendars 11 | - tracking work 12 | - calculating time between two date stamps 13 | 14 | 15 | !!! INFO "Java Time available by default" 16 | `java.time` namespace is available on the class path by default, `require` or `import` expressions are not required. 17 | 18 | 19 | ## Current data time 20 | 21 | Create a date and time stamp with the current data and time for the Operating System timezone. 22 | 23 | ```clojure 24 | (java.time.LocalDateTime/now) 25 | ``` 26 | 27 | A Java time object is returned 28 | 29 | ```clojure 30 | #object[java.time.LocalDateTime 0xf36f34d "2023-07-13T13:32:22.483261516"] 31 | ``` 32 | 33 | Current date 34 | 35 | ```clojure 36 | (java.time.LocalDate/now) 37 | 38 | ;; => #object[java.time.LocalDate 0x5814b4fb "2023-07-13"] 39 | ``` 40 | 41 | Instant 42 | 43 | ```clojure 44 | (java.time.Instant/now) 45 | ;; => #object[java.time.Instant 0x3684d2c0 "2023-07-13T13:02:27.889805413Z"] 46 | ``` 47 | 48 | 49 | ## Clojure Spec 50 | 51 | use Clojure Spec to define a data structure containing a java.time.LocalDate element 52 | 53 | ```clojure 54 | (spec/def :employee/first-name string?) 55 | (spec/def :employee/last-name string?) 56 | (spec/def :employee/birth-date #(instance? java.time.LocalDate %)) 57 | 58 | (spec/def :employee/person 59 | (s/keys :req [:employee/first-name 60 | :employee/last-name 61 | :employee/birth-date])) 62 | 63 | (def jenny #:ex{:first-name "Jenny" 64 | :last-name "Barnes" 65 | :birth-date (java.time.LocalDate/parse "1910-03-15")}) 66 | 67 | ``` 68 | 69 | Check to see if Jenny is a valid employee 70 | 71 | ```clojure 72 | (s/valid? :employee/person jenny) 73 | ;; => true 74 | ``` 75 | 76 | -------------------------------------------------------------------------------- /docs/small-projects/index.md: -------------------------------------------------------------------------------- 1 | # Small Projects 2 | 3 | ![Projects](/images/projects.png) 4 | 5 | An effective way to get comfortable with Clojure is to start writing small projects. In this section several small projects are used to walk the audience through how to create and develop a project, as well as learn some Clojure functions and functional programming techniques along the way. 6 | 7 | | Project | Topics | Description | 8 | |-------------------------------------------------------|-----------------------|----------------------------------------------------------------| 9 | | [Random Clojure Function](random-clojure-function.md) | namespace vars | print a random function from the Clojure standard library | 10 | | [Encoding and decoding](encode-decode/) | hash-map dictionaries | transforming messages between one form and another | 11 | | [Data Transformation](data-transformation/) | | transforming larger and larger data sets | 12 | | [Test Driven Development and Kata](tdd-kata/) | Unit testing | Unit testing and solving challenges using different approaches | 13 | 14 | !!! HINT "Create a Clojure project" 15 | [Clojure CLI tools and clj-new](/clojure/clojure-cli/projects/create-from-template/) to create a new Clojure project. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /docs/games/tictactoe-cli/create-project.md: -------------------------------------------------------------------------------- 1 | # Create a Clojure project 2 | > 3 | > #### TODO::work in progress, sorry 4 | 5 | Create a project for our game. 6 | 7 | {% tabs deps="deps.edn projects", lein="Leiningnen projects" %} 8 | 9 | {% content "deps" %} 10 | Create a new project using `clj-new` alias, found in [:fontawesome-solid-book-open: Practicalli Clojure CLI Config]({{ book.P9IClojureDepsEdn }}) 11 | 12 | ```bash 13 | clojure -M:new practicalli/tictactoe-cli 14 | ``` 15 | 16 | Open the project in [a Clojure aware editor](/clojure-editors/) or run a rebel REPL 17 | 18 | ```bash 19 | clojure -M:repl/rebel 20 | ``` 21 | 22 | Once the rebel REPL is running, load the project and change to the main namespace 23 | 24 | ```clojure 25 | (require 'practicalli/tictactoe-cli) 26 | 27 | (in-ns 'practicalli/tictactoe-cli) 28 | ``` 29 | 30 | {% content "lein" %} 31 | The default Leiningen template is suitable fine for the project as no additional libraries are used. 32 | 33 | ``` 34 | lein new tictactoe-cli 35 | ``` 36 | 37 | > #### Hint::Alternatively clone the github repository 38 | > 39 | > You can also clone the tictactoe-cli game from GitHub 40 | 41 | ```bash 42 | git clone https://github.com/practicalli/tictactoe-cli.git 43 | ``` 44 | 45 | ## Updating Clojure version and licence 46 | 47 | In the `project.clj` file I have updated Clojure to version 1.10.0 and changed the licence to be the more open Creative Commons license. 48 | 49 | ```clojure 50 | (defproject tictactoe-cli "0.1.0-SNAPSHOT" 51 | :description "TicTacToe game played on the command line" 52 | :url "https://github.com/practicalli/tictactoe-cli" 53 | :license {:name "Creative Commons Attribution Share-Alike 4.0 International" 54 | :url "https://creativecommons.org"} 55 | :dependencies [[org.clojure/clojure "1.10.0"]]) 56 | ``` 57 | 58 | I also removed the `license` file and added a brief description of the project to the `README.md` file 59 | 60 | {% endtabs %} 61 | -------------------------------------------------------------------------------- /docs/thinking-functionally/immutable-collections.md: -------------------------------------------------------------------------------- 1 | # Immutable collections 2 | 3 | As we have discussed, immutable data structures cannot be changed. So when you run a function over a collection a copy of that collection is returned. Lets see this by running some code in the REPL. 4 | 5 | > **Note** Define a data structure called `numbers` using a vector. Then write a function that uses the `map` and `inc` function to increment all the numbers in a vector. 6 | 7 | > Then check the current value of the `numbers` data structure by evaluating its name. 8 | 9 | 10 | ```clojure 11 | ;; define the data structure 12 | (defn numbers [1 2 3 4 5]) 13 | 14 | ;; increment the numbers 15 | (map inc numbers) 16 | 17 | ;; see the current value of numbers 18 | numbers 19 | ``` 20 | 21 | 22 | 23 | > **Note** Use the `conj` function to first add the number `5` to the `numbers` vector from the previous exercise and check the value of `numbers`. Then add the number `6` to the `numbers` vector and check the value of `numbers`. 24 | 25 | > Finally, use the `conj` function to add both `5` and `6` to the `numbers` vector and check the value of `numbers` 26 | 27 | 28 | 29 | ``` 30 | (def numbers [1 2 3 4]) 31 | 32 | ;; add 5 to the numbers vector 33 | (conj numbers 5) 34 | 35 | ;; check the value of numbers 36 | numbers 37 | ;; => [1 2 3 4] 38 | 39 | ;; add 6 to the numbers vector 40 | (conj numbers 6) 41 | 42 | ;; check the value of numbers 43 | numbers 44 | ;; => [1 2 3 4] 45 | 46 | ;; add 5 and 6 to the numbers vector 47 | (conj numbers 5 6) 48 | 49 | ;; Alternatively, you can use the threading macro to chain two conj function calls 50 | (-> numbers 51 | (conj 5) 52 | (conj 6)) 53 | 54 | ;; check the value of numbers 55 | numbers 56 | ;; => [1 2 3 4] 57 | ``` 58 | 59 | So even though we have applied several functions on the `numbers` data structure it still has the same value. 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/app-state-section.md: -------------------------------------------------------------------------------- 1 | # App State driven section 2 | 3 | The sponsor section of the website will change the sponsor details each time we have a new event. Rather than _hard code_ the sponsor details, we can add them to the application state. Then each time we have a new event, all we need to do is update that state with new sponsor details. 4 | 5 | Update the map to contain a `:sponsors` key whose value is a map. 6 | 7 | That map containes `:current` and `:past` sponsors (past sponsors will be a map that has the number of the event and the sponsor details copied from the :current map). 8 | 9 | ```clojure 10 | (defonce 11 | app-state 12 | (atom 13 | {:text "Hello world!" 14 | :sponsors 15 | {:current 16 | {:name "Functional Works" 17 | :logo "images/functional-works-logo.svg" 18 | :website "https://functional.works-hub.com/" 19 | :message "Breaking down the barriers to hiring the right software engineers, 20 | providing a platform to managing the whole process (written in ClojureScript)."} 21 | :past {:9 {,,,}}}})) 22 | ``` 23 | 24 | Here is an example of a current sponsor component. 25 | 26 | ```clojure 27 | (defn sponsor-current 28 | "Sponsors for our current event, to help that sponsor get some exposure 29 | 30 | Argument: hash-map of strings - :name, :website, :logo, :message" 31 | [sponsor-details] 32 | [:div {:class "container"} 33 | [:div {:class "box"} 34 | [:div {:class "column is-half is-8 is-offset-2"} 35 | [:a {:href (get sponsor-details :website)} 36 | [:h3 {:class "title is-5 has-text-centered"} (str "Our Sponsors:" " " (get sponsor-details :name))]] 37 | [:div {:class "columns"} 38 | [:div {:class "column"} 39 | [:figure {:class "image"} 40 | [:a {:href (get sponsor-details :website)} 41 | [:img {:src "images/functional-works-logo.png"}]]]] 42 | [:div {:class "column"} 43 | [:div {:class "content"} 44 | [:p (get sponsor-details :message)]]]]]]]) 45 | ``` 46 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/deploy-build.md: -------------------------------------------------------------------------------- 1 | # Creating a Deploy build configuration 2 | 3 | Create a build configuration for deployment, to remove the need to copy the `.js` file to the deploy location. 4 | 5 | These instructions assume the `/docs` directory is used by GitHub pages for deploying the website. 6 | 7 | ## Create a deploy build configuration file 8 | 9 | Copy the `dev.cljs.edn` to `deploy.cljs.edn`. 10 | 11 | Edit `deploy.cljs.edn` and add the `ouptput-to:` configuration option 12 | 13 | ```clojure 14 | ^{:watch-dirs ["test" "src"] 15 | :css-dirs ["resources/public/css"] 16 | :auto-testing true} 17 | {:main clojurebridge-landing-page.core 18 | :output-to "docs/cljs-out/dev-main.js"} 19 | ``` 20 | 21 | > #### Hint::Adjust file and path if required 22 | > This configuration assumes GitHub pages has been configured to use the `/docs` directory in the master branch to serve the website. 23 | 24 | 25 | ## Create a new alias 26 | 27 | Edit the `project.clj` file. 28 | 29 | Add a new `:aliases` line, the same as the `fig:min` line, except using `deploy` at the end. 30 | 31 | ```clojure 32 | :aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"] 33 | "fig:build" ["trampoline" "run" "-m" "figwheel.main" "-b" "dev" "-r"] 34 | "fig:min" ["run" "-m" "figwheel.main" "-O" "advanced" "-bo" "dev"] 35 | "fig:deploy" ["run" "-m" "figwheel.main" "-O" "advanced" "-bo" "deploy"] 36 | "fig:test" ["run" "-m" "figwheel.main" "-co" "test.cljs.edn" "-m" juxt-edge.test-runner]} 37 | ``` 38 | 39 | ## Deploying updates 40 | 41 | `lein fig:deploy` will now deploy any changes to your ClojureScript app without the need to copy any files. 42 | 43 | In a terminal window open in the root of your project, run the commands: 44 | 45 | ```shell 46 | lein clean 47 | lein fig:deploy 48 | ``` 49 | 50 | Then commit the new file and push to GitHub 51 | 52 | ```shell 53 | git commit -a "Deploy version 4.2" 54 | git push origin master 55 | ``` 56 | 57 | If you make any changes to the index.html or css/styles.css files, then still need to copy them into `/docs` directory manually, then commit those changes too. 58 | -------------------------------------------------------------------------------- /docs/small-projects/split-the-bill.md: -------------------------------------------------------------------------------- 1 | # Split the bill 2 | 3 | 4 | 5 | In a restaurant a group of friends and relatives are having a reunion dinner after a year of not seeing each other. 6 | 7 | Once the meal comes to an end, its time to pay the bill. So how would you write code to split the bill? 8 | 9 | Start with the simplest possible approach, with everyone paying the same. 10 | 11 | ## Create a new Clojure project 12 | 13 | [:fontawesome-solid-book-open: Pracitcalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) provides the `:project/create` alias to create projects using deps-new project. 14 | 15 | ```bash 16 | clojure -T:project/create :template app :name practicalli/split-the-bill 17 | ``` 18 | 19 | ```clojure 20 | (str "Create code to calculate the bill, including what each person should pay") 21 | ``` 22 | 23 | Tke a look at the [Who am I](/community-docs/docs/curriculum/who-am-i) section for ideas on how to model the bill. Also look at [More Than Average](/community-docs/docs/curriculum/more-than-average) for ideas on how to write code to work out how to pay the bill. 24 | 25 | ### Paying what was ordered 26 | 27 | As not everyone had eaten the same amount of food or arrived at the same time, then there was an ask for everyone to pay just what they ordered. 28 | 29 | So create a collection to capture what each person ordered and create an itemised bill so each person knows what they should pay. 30 | 31 | Define a detailed bill based on what each person ordered, then create an itemised bill based on each persons order 32 | 33 | Now it was realised that what everyone ordered is not what everyone ate. So now we need to take the order and create an itemised bill based on what everyone actually ate (lets suspend believe here a little and assume everyone knows exactly what they ate, and is honest about it). 34 | 35 | Define a detailed bill based on what each person ordered, then create an itemised bill based on each person actually ate 36 | 37 | ## Spliting the bill with a Social Group 38 | 39 | Extend the exercise by splitting bills over multiple events and activities with multiple people. 40 | -------------------------------------------------------------------------------- /docs/introduction/concepts/naming-things.md: -------------------------------------------------------------------------------- 1 | # Naming things - data structures and functions 2 | 3 | The `def` function is used to name data structures in Clojure. 4 | 5 | You can also use `def` to name functions, however it is more common to use `defn` (which is a macro around def) to give a function a name. 6 | 7 | ## Keeping things private 8 | 9 | There is less emphasis on keeping functions and data structures private (compared to Java, C++, C#). If you want to define a function name so that it is only accessible by other functions of the same namespace, you can use the `defn-` function. 10 | 11 | There is no private equivalent for `def` (as of Clojure 1.6) however you can use metadata to specify this 12 | 13 | (def ^:private name data) 14 | 15 | > TODO: check if there is anything new around this or other common practices 16 | 17 | ## Misc - writing a private def macro 18 | 19 | You could write your own macro to create a private `def` called `def-` 20 | 21 | ```clojure 22 | (defmacro def- [item value] 23 | `(def ^{:private true} ~item ~value) 24 | ) 25 | ``` 26 | 27 | > There are no naming conventions for a private symbol name. As its defined an used within the scope of that one namespace (file), then there is no real need to make a special convention. Private functions will just be called as normal within the namespace and it will be quite clear from the function definition that it is private. 28 | 29 | [Clojure community style guide](https://github.com/bbatsov/clojure-style-guide) 30 | 31 | ## example 32 | 33 | Learning Clojure #4: private functions 34 | 35 | 36 | Sometimes in a Clojure file you just want some helper functions that shouldn’t be exposed outside the namespace. You can create a private function using the special defn- macro instead of defn. 37 | 38 | For instance, create a file foo/bar.clj with a public and a private function: 39 | 40 | (ns foo.bar) 41 | (defn- sq [x] (* x x)) 42 | (defn sum-squares [a b] (+ (sq a) (sq b))) 43 | 44 | Then use it from the REPL: 45 | 46 | user=> (use 'foo.bar) 47 | nil 48 | user=> (sum-squares 3 4) 49 | 25 50 | user=> (sq 5) 51 | java.lang.Exception: Unable to resolve symbol: sq in this context (NO_SOURCE_FILE:6) 52 | -------------------------------------------------------------------------------- /docs/namespace-design.md: -------------------------------------------------------------------------------- 1 | # Namespace Design 2 | 3 | In a small project with only a few developers, things like naming and style conventions don’t matter all that much, because almost everyone has worked with almost all of the code. 4 | 5 | With larger teams and sizable code bases — think tens of developers, tens of thousands of lines of Clojure — there’s a good chance that anyone reading the code has never seen it before. For that reader, a few conventions can be a big help. 6 | 7 | Optimizing for readability usually means being more verbose. Don’t abbreviate unless you have to. 8 | 9 | Optimizing for a reader who is not necessarily familiar with the entire code base, or even an entire file. They’ve just jumped to a function definition in their editor, or maybe pulled a line number from a stack trace. They don’t want to take the time to understand how all the different namespaces relate. They especially don’t want to have to scroll to the top of the file just to see where a symbol comes from. 10 | 11 | So these conventions are about maximizing readability at the level of single function definitions. Yes, it means more typing. But it makes it much easier to navigate a large codebase maintained by multiple people. 12 | 13 | As a general first rule, make the alias the same as the namespace name with the leading parts removed. 14 | 15 | ```clojure 16 | (ns com.example.application 17 | (:require 18 | [clojure.java.io :as io] 19 | [clojure.string :as string])) 20 | ``` 21 | 22 | Keep enough trailing parts to make each alias unique. Did you know that namespace aliases can have dots in them? 23 | 24 | ```clojure 25 | [clojure.data.xml :as data.xml] 26 | [clojure.xml :as xml] 27 | ``` 28 | 29 | Eliminate redundant words such as “core” and “clj” in aliases. 30 | 31 | ```clojure 32 | [clj-http :as http] 33 | [clj-time.core :as time] 34 | [clj-time.format :as time.format] 35 | ``` 36 | 37 | Use :refer sparingly. It’s good for symbols that have no alphabetic characters, such as >! !! ! !!]] 43 | [clojure.test :refer [deftest is]] 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /docs/thinking-functionally/pure-functions.md: -------------------------------------------------------------------------------- 1 | # Pure functions 2 | 3 | A function is considered pure if does not side effects or is affected by side causes. A pure function does not change any other part of the system and is not affected by any other part of the system. 4 | 5 | When you pass arguments to a function and that function returns a value without interacting with any other part of the system, then that function is considered pure. 6 | 7 | Should something from outside a function be allowed to affect the result of evaluating a function, or if that function be allowed to affect the outside world, then its an impure function. 8 | 9 | ![Pure function basic concept](/images/functional-programming-concepts-pure-function.png) 10 | 11 | So lets look at a simple code example 12 | 13 | > ####Note::Write a pure function that adds two numbers together ? 14 | 15 | ```clojure 16 | (defn add-numbers [number1 number2] 17 | (+ number1 number2)) 18 | 19 | (add-numbers 1 2) 20 | ``` 21 | 22 | Lets look at each line of this suggested answer 23 | 24 | ```clojure 25 | ;; function takes 2 arguments 26 | ;; function uses both arguments for result 27 | (defn add-numbers [number1 number2] 28 | (+ number1 number2)) 29 | 30 | ;; specific values are passed as arguments 31 | (add-numbers 1 2) 32 | ``` 33 | 34 | # An example with map 35 | 36 | > **Note** Define a collection called numbers and write a named function that increments each number of the numbers collection. 37 | > Is your function pure or impure ? 38 | 39 | ```clojure 40 | (def numbers '(5 4 3 2 1)) 41 | 42 | (defn increment-numbers [] 43 | (map inc numbers)) 44 | 45 | (increment-numbers) 46 | ``` 47 | 48 | The function takes no arguments and is pulling in a value from outside the function. This is a trivial example, but if all your code is like this it would be more complex. If the value pointed to by `numbers` is mutable and changes before the `increment-numbers` function is called then you will get different results. 49 | 50 | Here is a Pure function example 51 | 52 | ```clojure 53 | (def numbers '(5 4 3 2 1)) 54 | 55 | (defn increment-numbers [number-collection] 56 | (map inc number-collection)) 57 | 58 | (increment-numbers numbers) 59 | ``` 60 | 61 | In this example we are explicitly passing the `numbers` collection to the function. The function works on passed value and returns a predictable result. 62 | -------------------------------------------------------------------------------- /docs/thinking-functionally/recursion.md: -------------------------------------------------------------------------------- 1 | # Recursion 2 | 3 | > **Fixme** work in progress 4 | 5 | Recursion is used greatly in Clojure to iterate through data and as anything can be treated as data in Clojure you can understand why. 6 | 7 | The constructs available in Clojure for recursion include 8 | 9 | * `loop` and `recur` 10 | * Named function that calls itself 11 | * `map`, `reduce`, `filter`, `remove`, etc. 12 | * `for` 13 | 14 | # Recursively calling the same function 15 | 16 | Lets iterate though a collection using recursion by writing a function that calls itself 17 | 18 | ```clojure 19 | (defn recursively-use-a-collection [collection] 20 | (println (first collection)) 21 | (if (empty? collection) 22 | (print-str "no more values to process") 23 | (recursively-use-a-collection (rest collection)))) 24 | 25 | (recursively-use-a-collection [1 2 3]) 26 | ``` 27 | 28 | Lets take this recursive approach to create a function that can tell us the length of a collection (list or vector) 29 | 30 | We define a function that takes a collection of an argument. The collection is tested to see if it is empty and if so a zero value is returned. If the collection is not empty, then we 31 | 32 | ```clojure 33 | (defn length [collection] 34 | (if (empty? collection) 35 | 0 36 | (+ 1 (length (rest collection))))) 37 | ;; => #'clojure-through-code.01-basics/length 38 | ``` 39 | 40 | If we call the `length` function with an empty collection, then the `empty?` condition will return true and the `if` expression will evaluate the first expression, 0, returning 0. 41 | 42 | ```clojure 43 | (length []) 44 | ;; => 0 45 | 46 | ``` 47 | 48 | If we call the `length` function with a collection containing 3 values, then the `empty?` function will return `false` and the `if` function will evaluate the second expression. 49 | 50 | The second expression starts with a simple counter, using the `+` function and the value one 51 | 52 | ```clojure 53 | (length [0 1 2]) 54 | ;; => 3 55 | 56 | ``` 57 | 58 | ```clojure 59 | (+ 1 (length [1 2])) 60 | (+ 1 (+ 1 (length [2]))) 61 | (+ 1 (+ 1 (+ 1 (length [])))) 62 | (+ 1 (+ 1 (+ 1 0))) 63 | 64 | (length (range 24)) 65 | ;; => 24 66 | 67 | ``` 68 | 69 | (defn length [collection] 70 | (kk)) 71 | 72 | # Further recursion examples 73 | 74 | Other functions to consider 75 | 76 | * every 77 | * accumulating / accumulative 78 | * keep 79 | -------------------------------------------------------------------------------- /.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/introduction/concepts/what-is-functional-programming.md: -------------------------------------------------------------------------------- 1 | # What is Functional Programming 2 | 3 | Functional programming can seem quite different from imperative programming used in languages like C, C++ and Java. 4 | 5 | Imperative languages may seem easier initially, as defining one step after another is familiar approach to many things in live. As the scale of a system grows, so does complexity. Imperative languages applied object oriented design to manage complexity with varied rates of success. 6 | 7 | When shared mutable state is common in an OO design, then a system quickly becomes complex and very difficult to reason about. 8 | 9 | Functional programming is actually simpler that the OO approach, although initially it may be unfamiliar and not considered as easy. As systems grow in complexity, the building blocks are still simple and deterministic, creating a system that is far easier to reason about. 10 | 11 | ## Imperative programming languages 12 | 13 | In Imperative languages code is written that specifies a **sequential of instructions** that complete a task. These instructions typically **modifies program state** until the desired result is achieved. 14 | 15 | Variables typically represent **memory addresses that are mutable** (can be changed) by default. 16 | 17 | ![Imperative program - conceptual view](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/functional-programming-imperative-program.png) 18 | 19 | ## Functional programming languages 20 | 21 | Individual tasks are small and achieved by passing data to a function which returns a result. 22 | 23 | Functions are **composed** together to form more complex tasks and satisfy larger business logic. These composed functions pass the result of their evaluation to the next function, until all functions in the composition have been evaluated. 24 | 25 | The entire functional program can be thought of as a single function defined in terms of smaller ones. 26 | 27 | Program execution is an **evaluation of expressions**, with the nesting structure of function composition determining program flow. 28 | 29 | Data is **immutable** and cannot be change once created. Changes are expressed as new values, with complex values [sharing common values](/data-structures/shared-memory.md) for efficiency. 30 | 31 | ![Functional program - conceptual view](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/theory/functional-composition-illustrated.png) 32 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/add-welcome-message.md: -------------------------------------------------------------------------------- 1 | # Add welcome message to the website 2 | 3 | Update the `landing-page` function to contain content. 4 | 5 | Use the hiccup style to define the structure of the content 6 | 7 | Add style class names to the hiccup code to use CSS styles from [Bulma](https://bulma.io/). Feel free to experiment on what you want to add here. 8 | 9 | 10 | ## Simple Example 11 | 12 | ```clojure 13 | ;; Content components 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | 16 | (defn landing-page [] 17 | 18 | [:section {:class "section"} 19 | 20 | [:h1 21 | {:class "title"} 22 | "Welcome to ClojureBridge"] 23 | 24 | ;; Content will just use HTML tags directly, useful when you have no specific styles 25 | ;; https://bulma.io/documentation/elements/content/ 26 | 27 | [:div {:class "content"} 28 | [:p 29 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed enim ante. Nullam consectetur, sapien in rutrum facilisis, augue velit finibus est, at lobortis odio eros sollicitudin risus. Nullam mollis, metus a varius volutpat, metus elit mollis est, finibus pretium dui enim non velit. Praesent sit amet volutpat nulla. Sed volutpat venenatis nisi id sagittis. Nunc nec efficitur mi. Duis consequat sapien ultricies quam bibendum, elementum faucibus sapien bibendum. Morbi diam elit, gravida iaculis metus vitae, ullamcorper mattis mi. Maecenas luctus lorem metus. Maecenas eleifend nisl sit amet felis accumsan, sit amet tincidunt turpis consequat. Cras non molestie ante, a pellentesque dui."] 30 | [:p 31 | "Vivamus ullamcorper at orci ac tincidunt. Vivamus tincidunt sed erat nec consequat. Donec venenatis lorem justo, eget imperdiet arcu ultrices vitae. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec congue tempor posuere. Sed nec nisl mauris. Maecenas elementum quam justo, vitae auctor felis dapibus a. Phasellus leo justo, mattis a auctor tempus, facilisis vel tellus. Etiam at scelerisque justo, ac facilisis purus. Duis in leo pretium purus bibendum ultricies ac vitae lectus. Proin nec mi nec urna sollicitudin iaculis. In a orci felis. Sed luctus posuere luctus. Cras id euismod orci, id mollis nibh. Vestibulum et tellus quis lorem placerat scelerisque non et nisl. Ut dictum lacus nulla, sit amet ultricies eros pharetra vitae. "] 32 | ]]) 33 | 34 | ``` 35 | 36 | > #### Hint::Lorem ipsum generators 37 | > When you want to thing about the layout of a page without considering the wording of the content, you can use a Lorem ipsum generator to create text that most people will not just sit and read. 38 | -------------------------------------------------------------------------------- /docs/small-projects/encode-decode/convert-boolean-values.md: -------------------------------------------------------------------------------- 1 | ![Clojure - Simple projects - Encoding and decoding](/images/simple-projects-encoding-true-false.png) 2 | 3 | # Convert boolean true false to 1 and 0 4 | 5 | A very simple example of encoding and decoding is converting the Clojure values of `true` and `false` to `1` and `0` respectively. 6 | 7 | > Using `1` for true and `0` for false has been a common idiom in programming languages, especially where a language did not include `true` and `false` syntax. 8 | 9 | ## Define an association between values 10 | 11 | Define a Clojure `hash-map` to associate the Clojure boolean `true` an `false` values to `1` and `0` respectively 12 | 13 | ```clojure 14 | {false 0 15 | true 1} 16 | ``` 17 | 18 | ## Find an associated value for the conversion 19 | 20 | Using the `get` function the `boolean-value` is used to find a matching key in the map and if found the value that key is associated is returned. 21 | 22 | ```clojure 23 | (get {false 0 true 1} boolean-value) 24 | ``` 25 | 26 | Example: 27 | 28 | ```clojure 29 | (get {false 0 true 1} true) 30 | ``` 31 | 32 | A map can be called, just like a function. the `boolean-value` is passed to the map as a function argument. As with the `get` expression, if the map contains the key the associated value is returned. 33 | 34 | ```clojure 35 | ({false 0 true 1} boolean-value) 36 | ``` 37 | 38 | Example: 39 | 40 | ```clojure 41 | ({false 0 true 1} true) 42 | ``` 43 | 44 | ## Convert multiple boolean values 45 | 46 | If there are a collection of boolean values to convert, the `map` function can be used to convert them all to 1 or 0. 47 | 48 | Map this over a collection of values 49 | 50 | ```clojure 51 | (map {false 0 true 1} [collection-of-boolean-values]) 52 | ``` 53 | 54 | Example: 55 | 56 | ```clojure 57 | (map {false 0 true 1} [true false false true true true false false true false true false false true]) 58 | ``` 59 | 60 | ### How does this work? 61 | 62 | The `map` function takes two arguments, a function and a collection. The `map` function calls the function given as an argument and calls it with each element of the collection in turn. The result of each call is remembered by the `map` function and when the last element of the collection has been used, a new collection of all the results is returned. 63 | 64 | In the above example, the hash-map {false 0 true 1} acts as a function. 65 | 66 | ```clojure 67 | ({false 0 true 1} true) 68 | ``` 69 | 70 | A hash-map acts as a function in that it can return an associated value when given a key as an argument. 71 | 72 | Calling `{false 0 true 1}` with `true` as an argument returns the value `1`. 73 | -------------------------------------------------------------------------------- /docs/games/tictactoe-cli/index.md: -------------------------------------------------------------------------------- 1 | # TicTacToe on the command line 2 | 3 | [Tic-tac-toe](https://en.wikipedia.org/wiki/Tic-tac-toe) is a paper-and-pencil game for two players, X and O, who take turns marking the spaces in a 3×3 grid. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row wins the game 4 | 5 | ![TicTacToe game - winning line](https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Tic-tac-toe-game-1.svg/719px-Tic-tac-toe-game-1.svg.png) 6 | 7 |

8 | 9 |

10 | 11 | 12 | The code for this section is published on GitHub at: [practicalli/tictactoe-cli](https://github.com/practicalli/tictactoe-cli/tree/solution-no-tests) 13 | 14 | A [TicTacToe game](https://en.wikipedia.org/wiki/Tic-tac-toe) that you run on the command line. The game takes input from a human player and the program is the second player. 15 | 16 | ![Clojure Games TicTacToe board winner X](/images/clojure-games-tictactoe-board-winner-x.png) 17 | 18 | Output from the game appears in the REPL 19 | 20 | ```clojure 21 | Current board: 22 | 1 | 2 | 3 23 | --------- 24 | 4 | 5 | 6 25 | --------- 26 | 7 | 8 | 9 27 | X: Select your move (press a number between 1 and 9 then press enter) 28 | Current board: 29 | X | 2 | 3 30 | --------- 31 | 4 | 5 | 6 32 | --------- 33 | 7 | 8 | 9 34 | O: Select your move (press a number between 1 and 9 then press enter) 35 | Current board: 36 | X | O | 3 37 | --------- 38 | 4 | 5 | 6 39 | --------- 40 | 7 | 8 | 9 41 | X: Select your move (press a number between 1 and 9 then press enter) 42 | Current board: 43 | X | O | X 44 | --------- 45 | 4 | 5 | 6 46 | --------- 47 | 7 | 8 | 9 48 | O: Select your move (press a number between 1 and 9 then press enter) 49 | Current board: 50 | X | O | X 51 | --------- 52 | O | 5 | 6 53 | --------- 54 | 7 | 8 | 9 55 | X: Select your move (press a number between 1 and 9 then press enter) 56 | Current board: 57 | X | O | X 58 | --------- 59 | O | X | 6 60 | --------- 61 | 7 | 8 | 9 62 | O: Select your move (press a number between 1 and 9 then press enter) 63 | Current board: 64 | X | O | X 65 | --------- 66 | O | X | O 67 | --------- 68 | 7 | 8 | 9 69 | X: Select your move (press a number between 1 and 9 then press enter) 70 | Current board: 71 | X | O | X 72 | --------- 73 | O | X | O 74 | --------- 75 | X | 8 | 9 76 | Player X wins! 77 | ``` 78 | 79 | ## References 80 | 81 | * [TicTacToe game created by Brian Will](https://www.youtube.com/watch?v=vWSBGD96BHU). 82 | -------------------------------------------------------------------------------- /docs/thinking-functionally/lazy-evaluation.md: -------------------------------------------------------------------------------- 1 | # Lazy Evaluation 2 | 3 | > **Fixme** work in progress 4 | 5 | In the most basic way possible, laziness is the ability to evaluate an expression only when it's actually needed. Taken further, laziness is also evaluating an expression only to the extent required. 6 | 7 | # Laziness in definition 8 | 9 | # Laziness in evaluation 10 | 11 | # Laziness in partial evaluation 12 | 13 | Clojure is not entirely lazy, only the majority of sequence operations like map, reduce, filter or repeatedly are lazy evaluated. 14 | 15 | The most common use of laziness are infinite lists or streams. For example, we could define a list of all prime numbers. In case you didn't know, that's a lot of prime numbers (infinitely many). 16 | 17 | If we would define such list in a language like C++ or Python then the language would try to calculate all prime numbers immediately, which would run literally forever. 18 | 19 | If we define such list in Haskell or Clojure, then nothing is calculated just yet. As a matter of fact we could happily print out the first 1000 prime numbers from that list without running into a problem. Again, because lazy evaluation only calculates what is really needed, and nothing more. 20 | 21 | # Laziness in number calculation - Ratio type 22 | 23 | Dividing an integer value by another results in a Ratio type if the result would otherwise result in a decimal number. Clojure only partially evaluates this expression. 24 | 25 | ```clojure 26 | (/ 22 7) 27 | ``` 28 | 29 | We can also just express a value as a ratio. This works because of the prefix notation of Clojure 30 | 31 | ```clojure 32 | 22/7 33 | ``` 34 | 35 | The laziness can be overridden by specifying a precision, eg coercing the result into a specific type 36 | 37 | ```clojure 38 | (/ 22 7.0) 39 | (double (/ 22 7)) 40 | (float (/ 22 7)) 41 | ``` 42 | 43 | # Making something lazy 44 | 45 | The `range` function returns a sequence of numbers limited by any arguments given when calling the range function. 46 | 47 | Calling the range function without arguments will force an infinite sequence of numbers to be generated, quickly resulting in an out of memory error in the heap. 48 | 49 | Instead, we can either pass arguments to the range function that limit the sequence size or wrap the range function in another function 50 | 51 | ```clojure 52 | (take 7 (range)) 53 | ``` 54 | 55 | The `take` function defines how much of a sequence that `range` should generate. So we can call range without arguments and it will generate only those numbers in the sequence as specified by `take`. 56 | 57 | # References 58 | 59 | * [Being lazy in Clojure](http://noobtuts.com/clojure/being-lazy-in-clojure) - lazily generating monsters 60 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Learning Clojure 2 | 3 | Clojure is an elegant programming language for a more civilized development experience. 4 | 5 | Clojure encourages the creation of simple software systems using immutable values and a pragmatic approach to pure functional design. 6 | 7 | The aim of Learning Clojure is to cover the essentials required to start a journey into the Clojure programming language. 8 | 9 | !!! QUOTE "Patients, Practice and Time" 10 | Learning any programming language takes a lot of patients, practice and time to feel comfortable. 11 | 12 | [:fontawesome-solid-book-open: Practicalli recommended Clojure Learning Path](/learn-clojure/introduction/learning-path/){.md-button} 13 | 14 | [Clojure Concepts](/learn-clojure/introduction/concepts/) 15 | 16 | ## Navigate the book 17 | 18 | Use the mouse or built-in key bindings to navigate the pages of the book 19 | 20 | - ++p++ , ++comma++ : go to previous page 21 | - ++n++ , ++period++ : go to next page 22 | 23 | Use the search box to quickly find a specific topic 24 | 25 | - ++f++ , ++s++ , ++slash++ : open search dialog 26 | - ++arrow-down++ , ++arrow-up++ : select next / previous result 27 | - ++esc++ , ++tab++ : close search dialog 28 | - ++enter++ : follow selected result 29 | 30 | 31 | ## Resources 32 | 33 | [:fontawesome-solid-book-open: Practicalli Clojure CLI Config - additional tools via aliases](/clojure/clojure-cli/practicalli-config/){target=_blank .md-button} 34 | [Clojure Aware Editors](/clojure/clojure-editors){target=_blank .md-button} 35 | [Practicalli YouTube channel](https://youtube.com/practicalli){target=_blank .md-button} 36 | 37 | 38 | ## Sponsor Practicalli 39 | 40 | [![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/) 41 | 42 | All sponsorship funds are used to support the continued development of [Practicalli series of books and videos](https://practical.li/){target=_blank}, although most work is done at personal cost and time. 43 | 44 | Thanks to [Cognitect](https://www.cognitect.com/){target=_blank}, [Nubank](https://nubank.com.br/){target=_blank} and a wide range of other [sponsors](https://github.com/sponsors/practicalli-johnny#sponsors){target=_blank} from the Clojure community for your continued support 45 | 46 | 47 | ## Creative commons license 48 | 49 |
50 | Creative Commons License 51 | This work is licensed under a Creative Commons Attribution 4.0 ShareAlike License (including images & stylesheets). 52 |
53 | 54 | ## External Resources 55 | 56 | - Clojure Cookbook 57 | - Clojure Standard Reference 58 | - Getting Clojure 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/code-challenges/codewars/index.md: -------------------------------------------------------------------------------- 1 | # CodeWars 2 | 3 | Coding challenges in various languages with ranking scoreboard, experience levels and voting on solutions. Many of the challenges tend toward mathematics, so may require some background research before solving them. 4 | 5 | ## Requirements 6 | 7 | Codewars is a web browser based system in which you can write code and run tests. Sample unit tests are provided with each challenge, so its all self-contained. 8 | 9 | Create a free account and select the language you wish to attempt challenges in. Two simple coding tests will need to be completed in order to access that specific language. 10 | 11 | ## Challenges Dashboard 12 | 13 | After logging in, the dashboard suggests a challenge for you at a suitable level. 8 kyu is the easiest level, the smaller the number the harder the challenge. 14 | 15 | ![Clojure code challenges - Codewars suggested challenge](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/clojure-code-challenges-codewars-suggested-challenge.png) 16 | 17 | ## Tackling a challenge 18 | 19 | Read the instructions and take a look at the sample tests. 20 | 21 | Many of the challenges have only a very basic explanation, so always review the sample unit tests to help with the understanding. The sample tests are not necessarily the full suite of tests run when testing your solution, so there may be undocumented edge cases to solve 22 | 23 | The source and test code can be copied into a new project, as has been done with the [practicalli/codewars-guides solutions](https://github.com/practicalli/codewars-guides) 24 | 25 | ```bash 26 | clojure -M:new lib practicalli/readability-is-king 27 | ``` 28 | 29 | Update the solution window with your solution and use the **TEST** button to run the sample unit tests. 30 | 31 | The **ATTEMPT** button will run all the unit tests for the challenge, which may be more than the sample tests. If the attempt passes all the tests then the solution can be submitted an other solutions reviewed. 32 | 33 | ![Clojure code challenges - Codewars training with example challenge](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/clojure-code-challenges-codewars-training-example.png) 34 | 35 | ## Tracking progress 36 | 37 | View your profile page to track your progress and revisit kata challenges already completed. 38 | 39 | ![Clojure code challenges - Codewars profile dashboard](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/clojure-code-challenges-codewars-profile.png) 40 | 41 | ## References 42 | 43 | [practicalli/codewars-guide](https://github.com/practicalli/codewars-guides){target=_blank} - a repository of code solutions to CodeWars challenges, each challenge is its own Clojure CLI (deps.edn) project. 44 | 45 | [:fontawesome-brands-youtube: YouTube: CodeWars video guides](https://www.youtube.com/playlist?list=PLpr9V-R8ZxiCsYNLH9Wlt6L6L4Wk5GcTS){target=_blank .md-button} 46 | [Unofficial Free Code Camp Clojure Challenges](https://www.codewars.com/collections/unofficial-fcc-challenges-basic-algorithm-scripting){target=_blank .md-button} 47 | -------------------------------------------------------------------------------- /docs/thinking-functionally/higher-order-functions.md: -------------------------------------------------------------------------------- 1 | # Higher Order functions 2 | 3 | Functions can be used as an arguments to other functions as we have seen in function composition. This is possible because a function always evaluates to a value. This is the basis of function composition. 4 | 5 | Higher Order functions can also return a function definition, as when that function definition is evaluated it to will return a value. 6 | 7 | You could have a function that returns a function definition which in turn returns a function definition, but at some point this will get very confusing for the developers (yes, that means you). 8 | 9 | > #### Note::Return the even numbers from 1 to 10 10 | > 11 | > Generate a range of numbers from 1 to 10 12 | > 13 | > Use a function that checks if a number is even 14 | > and filter the range of numbers to return only the numbers that match 15 | 16 | ```clojure 17 | 18 | ``` 19 | 20 | 21 | 22 | ``` 23 | (filter 24 | even? 25 | (range 1 10)) 26 | 27 | ``` 28 | 29 | 30 | 31 | > #### Note::Create a named function as a higher order function called `twice` 32 | > 33 | > The function twice which takes a function and value as arguments. 34 | > 35 | > The twice function should call the function passed as an argument on the value passed as an argument. 36 | > 37 | > The result should be then used as an argument to calling the function passed as an argument again. 38 | > 39 | > Call the twice function with an inline function which takes a number as an argument and adds it to Pi, `3.14`. 40 | 41 | ```clojure 42 | (defn twice [f] 43 | ,,,) 44 | ``` 45 | 46 | 47 | 48 | ``` 49 | ;; Our higher order function 50 | 51 | (defn twice [function x] 52 | (function (function x))) 53 | 54 | (twice 55 | (fn [arg] 56 | (* 3.14 arg)) 57 | 21) 58 | ;; => 207.0516 59 | 60 | ;; using the short syntax for a function definition 61 | 62 | (twice #(+ 3.14 %) 21) 63 | ;; => 207.0516 64 | ``` 65 | 66 | 67 | > #### Note::Define a function that returns a function 68 | > 69 | > The function should take a clojure.core function for a mathematical calculation, i.e. `+`, `-`, `*`, `/` 70 | > 71 | > The returning function should take one or more arguments `[& args]` 72 | > and use the function originally passed as an argument to `reduce` the data to a single value. 73 | 74 | ```clojure 75 | (defn calculation [f] 76 | ,,,) 77 | ``` 78 | 79 | 80 | 81 | ``` 82 | (defn calculation [f] 83 | (fn [& args] 84 | (reduce f args))) 85 | 86 | ((calculation +) 1 1 2 3 5 8 13) 87 | 88 | ;; The result of `(calculation +)` is also in a list, 89 | ;; so it will be called as a function, with the arguments 1 1 2 3 5 8 13 90 | ``` 91 | 92 | 93 | # References 94 | 95 | * [Writing Elegant Clojure code using Higher Order functions](http://christophermaier.name/blog/2011/07/07/writing-elegant-clojure-code-using-higher-order-functions) 96 | -------------------------------------------------------------------------------- /docs/thinking-functionally/impure-functions.md: -------------------------------------------------------------------------------- 1 | # Impure functions 2 | 3 | We have seen some simple examples of pure functions, so lets see impure functions as a comparison. 4 | 5 | ```clojure 6 | (def global-value '(5 4 3 2 1)) 7 | 8 | (defn impure-increment-numbers [number-collection] 9 | (map inc global-value)) 10 | 11 | (impure-increment-numbers '(1 2 3 4 5)) 12 | ``` 13 | 14 | The above function is using a global value rather than the argument passed makes this function deterministic 15 | 16 | ## Side Effect: Printing to the console log 17 | 18 | Although the following example is probably quite harmless, it is a simple example of a function effecting the state of something outside. These side effects should be avoided where possible to keep your code simpler to reason about. 19 | 20 | ```clojure 21 | (defn print-to-console [value-to-print] 22 | (println "The value is:" value-to-print)) 23 | 24 | (print-to-console "a side effect") 25 | ``` 26 | 27 | ## Side Causes: Calling libraries 28 | 29 | To demonstrate a side causes form of impure functions, lets create a task-comple function that marks a current task complete using the current timestamp. 30 | 31 | ```clojure 32 | (defn task-complete [task-name] 33 | (str "Setting task " task-name " completed on " (js/Date))) 34 | 35 | (task-complete "hack clojure") 36 | ``` 37 | 38 | 39 | ```clojure 40 | (:import java.util.Date) 41 | 42 | (defn task-complete [task-name] 43 | (str "Setting task " task-name " completed on " (java.util.Date.))) 44 | 45 | (task-complete "hack clojure") 46 | ``` 47 | 48 | > ####Hint:: The function `(java.util.Date.)` is actually a call to instantiate a java.util.Date object. The full-stop character at the end of the name makes it a function call and is the short form of `(new java.util.Date)` 49 | 50 | In this example we have called to the outside world to generate a value for us. The above example is fairly simple, however by calling the outside world rather than passing in a date it makes the functions purpose far less clear. 51 | 52 | 53 | 54 | ## Re-write as a pure function 55 | 56 | Change the task-complete function definition and function call to take both the task-name and completed-date as arguments. 57 | 58 | ```clojure 59 | (defn task-complete [task-name completed-date] 60 | (str "Setting task " task-name " completed on " completed-date)) 61 | 62 | (task-complete "hack clojure" (js/Date)) 63 | ``` 64 | 65 | Required values should be generated outside a function where possible. In this case in the `(js/Date)` function is first evaluated and replaced by its value, then that date value is passed to the function as an argument, keeping the function pure. 66 | 67 | 68 | 69 | The pure version of the function in Clojure, using the java.util.Date function. 70 | 71 | ```clojure 72 | (:import java.util.Date) 73 | 74 | (defn task-complete [task-name completed-date] 75 | (str "Setting task " task-name " completed on " completed-date)) 76 | 77 | (task-complete "hack clojure" (java.util.Date.)) 78 | ``` 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-london-website.md: -------------------------------------------------------------------------------- 1 | # Create a Clojurebridge London website 2 | 3 | > ####Info::This section is new, so feedback is appreciated 4 | 5 | We have a very [simple website for ClojureBridge London](https://clojurebridgelondon.github.io/) and we would like to know how to make it easier to use. 6 | 7 | We would also like to have a website create with Clojure or preferably ClojureScript, so we can host the website on GitHub pages. 8 | 9 | > ####HINT::An example ClojureScript website using Bootstrap CSS library 10 | > [Clojure Study Group project](https://github.com/practicalli/clojure-study-group-website) contains examples of code you can include to create your ClojureScript website 11 | 12 | ## Create a ClojureScript project 13 | 14 | > ####Note:: Create a ClojureScript website for ClojureBridge London 15 | > 16 | > Create a project using the figwheel template, adding reagent library. 17 | > 18 | ```shell 19 | lein new figwheel clojurebridge-website -- --reagent 20 | ``` 21 | 22 | This will create a project with a working web page (usually described as a single page app). 23 | 24 | 25 | ## Add Bootstrap CSS library 26 | 27 | Bootstrap is a simple way to structure and make your website more appealing, using a wide range of CSS styles available. 28 | 29 | > ####Note::Add Bootstrap to project web page 30 | > Edit the `resources/public/index.html` file in your project 31 | > Add the following line of code inside the `` tag: 32 | ```html 33 | 34 | ``` 35 | 36 | See the [Clojure study group index.html](https://github.com/practicalli/clojure-study-group-website/blob/master/resources/public/index.html fs) file for an example 37 | 38 | The [Bootstrap Introduction documentation](https://getbootstrap.com/docs/4.2/getting-started/introduction/) includes examples of layouts and component styles you can include in your website. 39 | 40 | 41 | ## Adding sections to the website 42 | 43 | Create a function for each section of the website you want to add. 44 | 45 | > ####HINT::Reagent examples 46 | > [Introduction to Reagent](https://reagent-project.github.io/) has many simple examples of functions you can include in the website 47 | > 48 | > [Guide to Reagent](https://purelyfunctional.tv/guide/reagent/) has even more examples 49 | 50 | #### Create a banner heading using Bootstrap jumbotron style 51 | 52 | ```clojure 53 | (defn website-title [] 54 | [:div {:class "jumbotron practicalli-jumbotron"} 55 | [:h1 (get-in @app-state [:website :title])] 56 | [:h4 (get-in @app-state [:website :description])]]) 57 | ``` 58 | 59 | ## Writing html in Clojure with hiccup 60 | 61 | Rather than write `
`, `

`, `

` pairs of tags in html, we define our content using a syntax called hiccup. 62 | 63 | A vector, `[]` is used to hold the name of the html tag, represented by a keyword such as `:div` 64 | 65 | Defining our content in this way makes it easier to generate and transform using Clojure, so you can generate structure and content dynamically too. 66 | 67 | 68 | ## References 69 | * [Reagent Mysteries - part 1: vectors and sequences](https://presumably.de/reagent-mysteries-part-1-vectors-and-sequences.html) 70 | * [SVG in reagent](https://www.mattgreer.org/articles/embedding-svg-into-a-reagent-component/) 71 | -------------------------------------------------------------------------------- /docs/introduction/concepts/functional-reactive-programming.md: -------------------------------------------------------------------------------- 1 | # Functional Reactive Programming 2 | 3 | Functional Reactive programming is used in ClojureScript with libraries including reagent, re-frame, rum, etc. 4 | 5 | Functional Reactive Programming is an elegant means of modeling state over time in an easily composable way. Approaching the problem conceptually and developing a formal semantics first can lead to better optimization potential and simpler implementation. 6 | 7 | Taking a functional reactive programming approach results in systems that are: 8 | 9 | * Easier to reason about 10 | * Simpler to implement 11 | * Easier to optimize 12 | 13 | Functional Reactive Programming (FRP) is a specific formalism of a model of behaviors that change in response to events, created by Conal Elliot. That's a pretty abstract statement, but FRP is abstract itself. It does not denote a particular implementation, but rather a formal semantics. It is not a style of programming or a paradigm. It's simply a formalism. 14 | 15 | Functional Reactive Programming (and Denotational Design, also by Conal Elliott) has a lot to teach us about how to design functional programs. 16 | 17 | ## [Conal Elliott on FRP](http://www.haskellcast.com/episode/009-conal-elliott-on-frp-and-denotational-design/) Audio Interview 18 | 19 | If you're looking for an explanation of the Functional Reactive Programming from the man who invented it, along with an idea of the intriguing process he used to invent it, this HaskellCast episode is for you. 20 | 21 | ## [Functional Reactive Animation](http://conal.net/papers/icfp97/) 22 | 23 | A great paper from 1997 that spells out an early form of Functional Reactive Programming. It specifies behaviors (functions of time to a value) that change when events occur. 24 | 25 | ## [Conal Elliot's home page](http://conal.net/) 26 | 27 | Conal Elliot created FRP while he was researching graphics and animation. Be sure to check out his [FRP papers](http://conal.net/papers/frp.html) section. 28 | 29 | ## [Push-pull functional reactive programming](http://conal.net/papers/push-pull-frp/) 30 | 31 | A more advanced formulation of Functional Reactive Programming that formalizes the types and operations using Haskell's common type classes (Functor, ApplicativeFunctor, Monoid, etc). This one also includes a video of the paper presentation given at ICFP. 32 | 33 | The main breakthrough of this paper is to model the notion of a future value for events that have not yet happened. But if they have not happened, how can future values be compared? For instance, how does one ask if event a happens before event b if neither of them has happened yet? The paper develops a cool and inspiring formalism which resolves this problem. And one is left with a value that represents the entire behavior of a system past, present, and future. 34 | 35 | ## [Elm Thesis](https://www.seas.harvard.edu/sites/default/files/files/archived/Czaplicki.pdf) - PDF 36 | 37 | Elm is a different take on FRP (and it is potentially not FRP, according to some). Instead of behaviors (functions of time to a value), Elm uses discreet signals which are transformed to other signals using functional map-like operations. It also solves a real issue with computationally expensive operations blocking the propagation of signals by making some signals asynchronous. 38 | 39 | All-in-all, the thesis is a pleasure to read. It is very clear and a decent introduction to the myriad implementations of FRP out there. See the bibliography. 40 | -------------------------------------------------------------------------------- /docs/clojure-repl/index.md: -------------------------------------------------------------------------------- 1 | # Clojure REPL 2 | 3 | Use the Clojure repl to 4 | 5 | - call functions from clojure core 6 | - create symbol names (def) that represent a value 7 | - write custom functions (defn) and call them with data 8 | - see immediate results when evaluating code 9 | 10 | ## Getting Started 11 | 12 | [:fontawesome-solid-book-open: Install Clojure CLI and Practicalli Clojure CLI Config](https://practical.li/clojure/install/){target=_blank} for a comprehensive set of developmet tools. 13 | 14 | Use a [:fontawesome-solid-book-open: terminal UI REPL](#terminal-ui-repl) as a quick way to get started, or set up a preferred [:fontawesome-solid-book-open: Clojure editor](#clojure-editors). 15 | 16 | ??? HINT "Editor Connected REPL" 17 | An [:fontawesome-solid-book-open: Editor connected REPL](https://practical.li/clojure/clojure-editors/) is recommended once working with Clojure projects 18 | 19 | !!! HINT "Create a Clojure project from a template" 20 | `:project/create` alias from [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](https://practical.li/clojure/clojure-cli/practicalli-config/) will create a Clojure project structure 21 | ```clojure 22 | clojure -T:project/create :name github-name/project-name 23 | ``` 24 | 25 | ## Terminal UI REPL 26 | 27 | Rebel Readline provides a rich REPL experience, providing syntax highlighting, function signatures and documentation. 28 | 29 | The REPL can be used with or without a Clojure project. 30 | 31 | Start Rebel Readline REPL using the `:repl/rebel` alias provided by [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](https://practical.li/clojure/clojure-cli/practicalli-config/) 32 | 33 | ```shell 34 | clojure -M:repl/rebel 35 | ``` 36 | 37 | [:fontawesome-solid-book-open: Rebel REPL Terminal UI](https://practical.li/clojure/clojure-cli/repl/){target=_blank .md-button} 38 | 39 |

40 | 41 |

42 | 43 | 44 | ## Clojure Editors 45 | 46 | Clojure editors are the preferred way to write code and evaluating source code. Working with source files is more effective than entering all expressions directly at a REPL prompt. 47 | 48 | Use an editor to **jack-in** (start) a Clojure REPL process and connect to the running REPL. 49 | 50 | Or **connect** to a running REPL process, e.g. Rebel Terminal UI (over a network repl, nREPL). 51 | 52 | Use an editor that is most familiar or comfortable to minimise the learning curve. 53 | 54 | Clojure editors should provide 55 | 56 | - running / connecting to a REPL process 57 | - evaluation results inline (instant feedback on code behaviour) 58 | - syntax highlighting, including parens matching 59 | - Structural editing, balancing parens (paredit / parinfer) 60 | - data inspector to navigate large & nested data, or connection to external [:fontawesome-solid-book-open: data inpector tools](https://practical.li/clojure/data-inspector/) 61 | 62 | [:fontawesome-solid-book-open: Clojure aware editors](https://practical.li/clojure/clojure-editors/){target=_blank .md-button} 63 | 64 | ![:fontawesome-solid-book-open: Clojure Editor connected REPL](https://raw.githubusercontent.com/practicalli/graphic-design/live/clojure/clojure-repl-driven-development-clojure-aware-editor.png){loading=lazy} 65 | -------------------------------------------------------------------------------- /docs/small-projects/mutating-state/mutants-assemble.md: -------------------------------------------------------------------------------- 1 | # Mutants Assemble 2 | 3 | 4 | 5 | In this section you will apply changes to values, how to define your own simple functions. 6 | 7 | We will also introduce the following functions for the first time: 8 | 9 | | function | Description | 10 | |--------------|--------------------------------------------------| 11 | | `atom` | create an anonymous function, one without a name | 12 | | `deref`, `@` | assign a name to a function | 13 | 14 | ## Create a new Clojure project 15 | 16 | [:fontawesome-solid-book-open: Pracitcalli Clojure CLI Config](/clojure/clojure-cli/practicalli-config/) provides the `:project/create` alias to create projects using deps-new project. 17 | 18 | ```bash 19 | clojure -T:project/create :template app :name practicalli/mutants-assemble 20 | ``` 21 | 22 | Open the `src/practicalli/mutants_assemble.clj` file in a Clojure aware editor and start the REPL. 23 | 24 | ## Define an atom 25 | 26 | Use the `def` function to bind a name to an atom. 27 | 28 | The atom wraps data, initially an empty vector. 29 | 30 | ```clojure 31 | (def mutants (atom [])) 32 | ``` 33 | 34 | > The vector remains an immutable value, even though it is contained within a mutable atom container 35 | 36 | Define a function using `defn` which takes a mutant as an argument and updates the value managed by the atom. The reference to the atom is also an argument, making this a pure function and more generic as any given atom can be updated with this function. 37 | 38 | ```clojure 39 | 40 | (defn add-mutant [mutants mutant] 41 | (swap! mutants conj mutant)) 42 | ``` 43 | 44 | [`swap!`](https://clojuredocs.org/clojure.core/swap!) uses a function to create a new value for the atom to manage. In this case the `conj` function is used to join the value of mutant with the existing mutants atom value, creating a new vector. 45 | 46 | [`swap!`](https://clojuredocs.org/clojure.core/swap!) is a macro so the syntax is a little different. Essentially this is the same as an expression `(conj mutants mutant)`, with the value this returns swapped into the atom. 47 | 48 | Call the function with the `mutants` atom and a mutant to add, which is a string containing the name of a mutant character. 49 | 50 | ```clojure 51 | (add-mutant mutants "Black Widow") 52 | ``` 53 | 54 | The value the atom is managing has been swapped for a new value. The original value was not modified (vectors are immutable) so the atom now points to a new value, a vector containing a string. 55 | 56 | ## Viewing the value managed by the atom 57 | 58 | Use the `deref` function to see the value the atom is managing. 59 | 60 | ```clojure 61 | (deref mutants) 62 | ``` 63 | 64 | It is idiomatic to use `@` which is a syntax alias for the `deref` function, rather than explicitly using `deref`. 65 | 66 | ```clojure 67 | @mutants 68 | ``` 69 | 70 | ## Reset the atom value 71 | 72 | `reset!` will change the value managed by the atom by providing the new value. This is simpler than using `swap!` as it does not use the existing value in the atom. 73 | 74 | ``` 75 | (reset! mutants []) 76 | ``` 77 | 78 | Now all the mutants are gone (and we can start looking for new ones to add). 79 | 80 | 81 | 82 | 94 | -------------------------------------------------------------------------------- /docs/first-steps/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | Writing code to print out hello world is a common first step with any new language. 4 | 5 | Use the `println` function from clojure.core to accomplish this task. 6 | 7 | Enter the following code at the REPL prompt: 8 | 9 | !!! NOTE "" 10 | ```clojure 11 | (println "Hello World") 12 | ``` 13 | 14 | Expected result 15 | 16 | ```clojure 17 | Hello World 18 | nil 19 | ``` 20 | 21 | `Hello World` is printed, followed by a `nil` value. 22 | 23 | !!! HINT "println is a side-effect function" 24 | `println` is considered a function that creates a side-effect, as it sends information to the standard out process rather than returning a value. 25 | 26 | `nil` is the default return value if an expression does not return a value. 27 | 28 | 29 | ## A Clojure Expression 30 | 31 | `()` are used to define a Clojure expression. 32 | 33 | `()` means a list of elements, the first element is a call to a function and all other elements are passed as arguments to the function. 34 | 35 | ??? INFO "Homoiconicity - one represent for code and data" 36 | Clojure is homoiconic as code and data share the same representation, i.e. use the same iconography. 37 | 38 | A `()` list is a data structure, a collection of data values. 39 | 40 | A `()` list is also used to represent code behaviour (algorithms), e.g calling built-in or custom functions. 41 | 42 | A function call returns a data value (nil is also a value). 43 | 44 | 45 | ## Return a value 46 | 47 | Expressions and Function calls always return a value, the `nil` value being returned by default. 48 | 49 | An explicit return form is not required, the result of the last expression is returned. 50 | 51 | Enter the following code at the REPL prompt: 52 | 53 | !!! NOTE "" 54 | ```clojure 55 | (str "Hello World") 56 | ``` 57 | 58 | Expected result 59 | 60 | ```clojure 61 | "Hello World" 62 | ``` 63 | 64 | `Hello World` is returned as a data value, instead of the default `nil` return value. 65 | 66 | The example is a single expression, so the value created by evaluating the expresion is returned. 67 | 68 | `clojure.core/str` is a function that takes one or more values and return a string. The values do not need to be strings as Clojure will dynamically convert them. 69 | 70 | !!! NOTE "Join strings and a numeric value" 71 | ```clojure 72 | (str "hello world" " " 2) 73 | ``` 74 | 75 | Expected result 76 | 77 | ```clojure 78 | "hello world 2" 79 | ``` 80 | 81 | !!! NOTE "Join strings and the result of a function call" 82 | ```clojure 83 | (str "hello world" " " (+ 1 2)) 84 | ``` 85 | 86 | Expected result 87 | 88 | ```clojure 89 | "hello world 3" 90 | ``` 91 | 92 | `+` is the name of a function, its qualified name is `clojure.core/+`. `+` takes zero or more arguments, adds the values together and returns the result. 93 | 94 | A function call always returns a value so can be used as an argument to another funciton, or anywhere a value would be used. 95 | 96 | 97 | !!! INFO "Implicit types" 98 | Clojure uses types underneath and infers the type of something by its literal shape 99 | 100 | `"string"` is a string type, `java.lang.String` 101 | 102 | `123` is an Integer value, `java.lang.Long` 103 | 104 | `3.14` is a Decimal value, `java.lang.Double` 105 | 106 | `22/7` is a Ratio value, `clojure.lang.Ratio` (used to preserve accuracy of data) 107 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/organising-the-code.md: -------------------------------------------------------------------------------- 1 | # Re-organising the code a little 2 | 3 | Create comments to make it easier to identify the specific parts of the code 4 | 5 | > #### Hint::Good structure helps refactor later 6 | > Having an organised structure to your code helps you maintain the code and help you refactor code into multiple namespaces. 7 | 8 | 9 | ## Rename main component to landing-page 10 | 11 | To make our code clearer, rename `hello-world` function to `landing-page` 12 | 13 | ```clojure 14 | (defn landing-page [] 15 | [:div 16 | [:h1 (:text @app-state)] 17 | [:h3 "Live reloading in the REPL makes web development fun!"]]) 18 | ``` 19 | 20 | And update the `mount` function to use this new function name as the main component 21 | 22 | ```clojure 23 | (defn mount [el] 24 | (reagent/render-component [landing-page] el)) 25 | 26 | ``` 27 | 28 | ## Create a system section 29 | 30 | Move the `get-app-element` to the other mount / reagent functions and call that section System 31 | 32 | ```clojure 33 | ;; System 34 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 35 | 36 | (defn get-app-element [] 37 | (gdom/getElement "app")) 38 | 39 | (defn mount [el] 40 | (reagent/render-component [landing-page] el)) 41 | 42 | ``` 43 | 44 | ## Add a date/time stamped reload message 45 | 46 | As a quick sanity check, add a date / time stamp to the println message at the top of the file, so you can see the time figwheel reloads the page in the REPL output. 47 | 48 | ```clojure 49 | ;; simple debug statement for each build 50 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 51 | 52 | (println (js/Date.) "Reloading: src/clojurebridge_landing_page/core.cljs") 53 | ``` 54 | 55 | 56 | ## Final result 57 | 58 | After all the changes the file should look as follows 59 | 60 | ```clojure 61 | (ns ^:figwheel-hooks clojurebridge-landing-page.core 62 | (:require 63 | [goog.dom :as gdom] 64 | [reagent.core :as reagent :refer [atom]])) 65 | 66 | ;; simple debug statement for each build 67 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 68 | 69 | (println (js/Date.) "Reloading: src/clojurebridge_landing_page/core.cljs") 70 | 71 | 72 | ;; Application state 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | 75 | ;; define your app data so that it doesn't get over-written on reload 76 | (defonce app-state (atom {:text "Hello world!"})) 77 | 78 | 79 | ;; Helper functions 80 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 81 | 82 | (defn multiply [a b] (* a b)) 83 | 84 | 85 | ;; Content components 86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 87 | 88 | (defn landing-page [] 89 | [:div 90 | [:h1 (:text @app-state)] 91 | [:h3 "Live reloading in the REPL makes web development fun!"]]) 92 | 93 | 94 | ;; System 95 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 96 | 97 | (defn get-app-element [] 98 | (gdom/getElement "app")) 99 | 100 | (defn mount [el] 101 | (reagent/render-component [landing-page] el)) 102 | 103 | (defn mount-app-element [] 104 | (when-let [el (get-app-element)] 105 | (mount el))) 106 | 107 | ;; conditionally start your application based on the presence of an "app" element 108 | ;; this is particularly helpful for testing this ns without launching the app 109 | (mount-app-element) 110 | 111 | ;; specify reload hook with ^;after-load metadata 112 | (defn ^:after-load on-reload [] 113 | (mount-app-element) 114 | ;; optionally touch your app-state to force rerendering depending on 115 | ;; your application 116 | ;; (swap! app-state update-in [:__figwheel_counter] inc) 117 | ) 118 | ``` 119 | -------------------------------------------------------------------------------- /.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/thinking-functionally/list-comprehension.md: -------------------------------------------------------------------------------- 1 | # List Comprehension 2 | 3 | In general terms, list comprehensions should: 4 | 5 | * be distinct from (nested) for loops and the use of map & filter functions within the syntax of the language. 6 | * return either a list or an iterator (an iterating being something that returns successive members of a collection, in order), 7 | 8 | In Clojure, list comprehension is via the `for` function. This is different to the for in other languages as you will see. 9 | 10 | ``` 11 | (for [number [1 2 3]] (* number 2)) 12 | ``` 13 | 14 | The `for` function should be read as follows: 15 | 16 | "for each number in the collection [1 2 3], apply the function (* number 2)" 17 | 18 | Couldn't we just do this with map? Yes, we could. 19 | 20 | ``` 21 | (map #(* % 2) [1 2 3]) 22 | ``` 23 | 24 | So why do we need `for` function? It really shows its value when you are working with multiple collections 25 | 26 | ``` 27 | (for [number [1 2 3] 28 | letter [:a :b :c]] 29 | (str number letter)) 30 | ``` 31 | 32 | Again we could use `map` function for this as follows 33 | 34 | ``` 35 | (mapcat (fn [number] (map (fn [letter] (str number letter))))) 36 | ``` 37 | 38 | So with the `for` function we can do the same calculation with much easier code to reason about. 39 | 40 | ## Filtering results with predicates 41 | 42 | With the `for` function we can add a filter on the results by using a predicate, to test if a condition is true or false. Any values that meet the condition as true are returned, values that are false are omitted. 43 | 44 | ``` 45 | (for [x (range 10) :when (odd? x)] x) 46 | 47 | (for [x (range 10) :while (even? x)] x) 48 | ``` 49 | 50 | To do this kind of filtering with maps would be possible, however the code would be harder for humans to parse and understand. 51 | 52 | > **Note** Create a 3-tumbler combination padlock, with each tumbler having a range of 0 to 9. Count the number of possible combinations. Then add a predicate that filters out some of the combinations 53 | 54 | 55 | 56 | Lets just model all the possible combinations 57 | 58 | ``` 59 | (for [tumbler-1 (range 10) 60 | tumbler-2 (range 10) 61 | tumbler-3 (range 10)] 62 | [tumbler-1 tumbler-2 tumbler-3]) 63 | ``` 64 | 65 | Now lets count the combinations 66 | 67 | ``` 68 | (count (for [tumbler-1 (range 10) 69 | tumbler-2 (range 10) 70 | tumbler-3 (range 10)] 71 | [tumbler-1 tumbler-2 tumbler-3])) 72 | ``` 73 | 74 | Now add a predicate using `:when` to filter out the combinations that do not match. 75 | 76 | ``` 77 | (count (for [tumbler-1 (range 10) 78 | tumbler-2 (range 10) 79 | tumbler-3 (range 10) 80 | :when (or (= tumbler-1 tumbler-2) 81 | (= tumbler-2 tumbler-3) 82 | (= tumbler-3 tumbler-1))] 83 | [tumbler-1 tumbler-2 tumbler-3])) 84 | ``` 85 | 86 | 87 | 88 | > **Note** Create a 2 character prefix for tickets, using capital letters from the English alphabet. However, exclude I and O as they can be mistaken for numbers 89 | 90 | 91 | 92 | Lets just model all the possible combinations 93 | 94 | ``` 95 | (for [letter-1 capital-letters 96 | letter-2 capital-letters 97 | :when (and (not (blacklisted letter-1)) 98 | (not (blacklisted letter-2)))] 99 | (str letter-1 letter-2)) 100 | ``` 101 | 102 | 103 | -------------------------------------------------------------------------------- /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/thinking-functionally/threading-macros.md: -------------------------------------------------------------------------------- 1 | # Threading macros 2 | 3 | The thread-first `->` and thread-last `->>` macros allow Clojure code to be written in a more sequential style and with a more terse syntax. This can sometimes make code easier to understand by humans. 4 | 5 | Using the thread-first macro, `->`, the result of the first evaluation is passed as the **first argument** to the next function and so on. 6 | 7 | ```clojure 8 | (-> 9 | (clojure.string/lower-case "HELLO") 10 | (str ", Clojure world")) 11 | ``` 12 | 13 | The value hello is converted to lower case and that result is passed as the first argument to the next function. The string function is then evaluated with this new argument and the final "hello, Clojure world" string is returned as the result. 14 | 15 | The thread-last macro `->>` passes the result of the first evaluation as the **last argument** to the next expression. 16 | 17 | ```clojure 18 | (->> " this" 19 | (str " is") 20 | (str " backwards")) 21 | ``` 22 | 23 | > #### Hint::Parens optional 24 | > 25 | > function calls that only take one argument, the one passed by earlier expressions, can be included in the threading macro code without the surrounding `()` parens. 26 | 27 | ## Reading Clojure code 28 | 29 | To read Clojure it is common to start from the inside out, as this is how the Clojure reader also works. This style is inherited from Lisp of which Clojure is an implementation. 30 | 31 | The following code is written in classic Lisp style. 32 | 33 | ```clojure 34 | (reverse 35 | (sort-by val (frequencies 36 | (remove common-english-words 37 | (map #(clojure.string/lower-case %) 38 | (re-seq #"[a-zA-Z0-9|']+" 39 | (slurp book.txt))))))) 40 | ``` 41 | 42 | Reading inside out: 43 | 44 | 1. slurp in the contents of the book.txt file, converting it to a string. 45 | 2. use a regular expression to create a new sequence where the book is a sequence of individual strings for each word. 46 | 3. convert each string in the sequence by mapping the lower-case function over each element of the sequence. 47 | 4. remove common english words such as the and and from the sequence. 48 | 5. count how many times each word occurs and pair the string with its frequency in the book. 49 | 6. reverse the order of the sequence by the value of frequency, so the most used word is at the start of the sequence. 50 | 51 | This function uses the var `common-english-words` which is defined as: 52 | 53 | ```clojure 54 | (def (set 55 | (clojure.string/split (slurp "common-english-words.txt") #"," ))) 56 | ``` 57 | 58 | This takes a comma separated file of words and splits them. The words are put into a set so only one instance of each word is included. 59 | 60 | ## Rewriting the code with threading macros 61 | 62 | ```clojure 63 | (->> (slurp book.txt) 64 | (re-seq #"[a-zA-Z0-9|']+" ,,,) 65 | (map #(clojure.string/lower-case %)) 66 | (remove common-english-words) 67 | frequencies 68 | (sort-by val) 69 | reverse) 70 | ``` 71 | 72 | `frequencies` and `reverse` only take one argument, so they do not require surrounding `()` inside the threading macro. 73 | 74 | The common-english-words var is fairly easy to read, so probably doesn't need to be written using a threading macro, however, for completeness here is a thread-first example. 75 | 76 | ```clojure 77 | (def common-english-words 78 | (-> (slurp "common-english-words.txt") 79 | (clojure.string/split #",") 80 | set)) 81 | ``` 82 | 83 | > #### Hint::Macroexpand 84 | > 85 | > use `macroexpand-1` around the threading macro code to show resulting lisp style Clojure code. Clojure editors also provide evaluation functions that will macroexpand. 86 | -------------------------------------------------------------------------------- /docs/additional-projects/clojurebridge-website/github-pages-deploy.md: -------------------------------------------------------------------------------- 1 | # Deploy to GitHub pages 2 | 3 | GitHub pages is a free service for serving up content in static files, its ideal for websites that do not require their own database or other services (or simply plugs into purely online services and API's). 4 | 5 | Create a repository on GitHub for this ClojureScript project 6 | 7 | Add that repository as a remote repository to the local Git repository for this project 8 | 9 | ```shell 10 | git remote add origin 11 | 12 | ``` 13 | 14 | ## Project specific landing pages 15 | 16 | There are two approaches when adding a website to a specific project. 17 | 18 | * add files to a `gh-pages` branch (classic approach) 19 | * add files to `/docs` directory on the master branch (new approach) 20 | 21 | We will use the new approach and deploy our files in `/docs` 22 | 23 | 24 | ### Alternative: Organisation / user Landing pages 25 | 26 | For Organisations on GitHub, like ClojureBridgeLondon and Pracitalli, I use two separate Git repositories 27 | 28 | * ClojureScript repository - created by Leiningen / figwheel template 29 | * Deployment repository - only contains specific files for deployment 30 | 31 | 32 | ## Creating the files for deployment 33 | 34 | During development the build of the ClojureScript application is contained in multiple files, as this makes it easy to do fast updates of just the specific parts of the application. 35 | 36 | When we deploy, we generate a single JavaScript file that contains our application. We also minify the application to make it quick to load into the browser. 37 | 38 | > #### Note:: 39 | > In the root of your ClojureScript project (where project.clj file is) run the commands: 40 | ```shell 41 | lein clean 42 | lein fig:min 43 | ``` 44 | 45 | 46 | ## Add directory to GitHub 47 | 48 | Create the `/docs` and `/docs/cljs-out` directories in the project first. 49 | 50 | Add a `README.md` file with a message describing the purpose of the /docs directory. 51 | 52 | Commit the `README.md` file and push to your GitHub repository 53 | 54 | 55 | ## Set GitHub pages location 56 | 57 | Visit the GitHub repository website and update the Settings to point to `/docs as the source of GitHub pages 58 | 59 | Setting > GitHub Pages > Source 60 | 61 | **master branch /docs folder** 62 | 63 | ![ClojureScript websites - Github Pages source](/images/cljs-website--github-pages-source-docs-on-master.png) 64 | 65 | 66 | ## Copy the files 67 | 68 | Copy the following files into the `/docs` directory. 69 | 70 | ```shell 71 | cp resources/public/index.html docs 72 | 73 | cp -r resources/public/css docs 74 | 75 | cp -r resources/public/images images 76 | 77 | cp resources/public/cljs-out/dev-main.js docs/cljs-out/ 78 | ``` 79 | 80 | 81 | ## Commit and push the files 82 | 83 | Commit all the changes in the `/docs` directory. 84 | 85 | Push the commit to GitHub 86 | `git push origin master` 87 | 88 | Visit your live website at https://.github.io/clojurebridge-landing-page/ 89 | 90 | 91 | ## Deploying updates 92 | 93 | Any changes to your ClojureScript app that you want to deploy, then you only need to build the single javascrpt file again 94 | 95 | ```shell 96 | lein clean 97 | lein fig:min 98 | ``` 99 | 100 | Then copy the new javascript file to the `docs/cljs-out` directory 101 | 102 | ```shell 103 | cp resources/public/cljs-out/dev-main.js docs/cljs-out/ 104 | ``` 105 | 106 | Commit the new file and push to GitHub 107 | ```shell 108 | git commit -a "New version" 109 | git push origin master 110 | ``` 111 | 112 | If you make any changes to the index.html or css/styles.css files, then you will need to copy them into `/docs` directory and commit those changes too 113 | -------------------------------------------------------------------------------- /docs/first-steps/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | The Clojure Standard Library contains hundreds of functions for writing custom applications and services. 4 | 5 | `defn` function is used to define functions 6 | 7 | 8 | ```clojure 9 | (defn hello-world 10 | [name] 11 | (str "Hello" name ", welcome to Clojure") 12 | ``` 13 | 14 | Evaluate the `hello-world` function definition expression, then call the `hello-world` function with an argument 15 | 16 | ```clojure 17 | (hello-world "Jenny") 18 | ``` 19 | 20 | The last expression in a function definition is returned when the function is called. An explicity return expression is not required. 21 | 22 | !!! INFO "Function definition must be evaluated before use" 23 | In a Clojure source code file, the function definition code must be written before any code that calls that function, otherwise a symbol not found error will occur 24 | 25 | During development, experimental code can be written after it is called as long as that definition has been evaluated in the REPL. e.g. a new design for a function could be written in a rich comment, `(comment ,,,)` at the bottom of the source code file and evaluated to replace a definition further up in the source code file. If the new design for the function is preferred its code would replace the original definition. If the new design is interesting but not desired, it could be kept as part of a design journal. 26 | 27 | 28 | ??? CLOJURE-IDIOM "Avoid using declare" 29 | `declare` allows for a symbol to be defined without a value. A function name could be defined at the top of a Clojure source code file with the function definition, `defn`, occuring lower down in the file after code that calls the function. 30 | 31 | It is commonly viewed as a need to refactor the design if `declare` is required, e.g. two functions call each other. Function definitions should be defined so that `declare` is not required, typically by extracting code into other functions. 32 | 33 | 34 | ## Function arguments 35 | 36 | Argument names are defined in a vector, `[]` 37 | 38 | Functions are limited to recieving 26 arguments. In reality a minimal number should be passed or multiple values passed as a collection. 39 | 40 | `& arguments` in the function definition will take zero or more values, placing all values into a vector, `[]` 41 | 42 | `:as` to group all arguments as one collection of values 43 | 44 | !!! DESIGN "none, one, or many arguments" 45 | Practicalli recommends designing a function to recieve either no arguments, one argument or many arguments 46 | 47 | A hash-map can be considered one argument that abstracts many arguments. 48 | 49 | 50 | !!! CLOJURE-IDIOM "hash-maps as arguments" 51 | Pass a hash-map as an argument to a function provides scope for growth. 52 | 53 | Keys can be added to the hash-map to extend the capabilities of the function without breaking existing calls to the function. 54 | 55 | Using keys and values provides more context as to the purpose of the arguments passed. 56 | 57 | !!! CLOJURE-IDIOM "unnamed arguments" 58 | `_` is used as an argument name when an argument has to be passed to a function, but the function is not going to use the argument. 59 | 60 | `_` may be used to represent the request hash-map passed to handler functions in Clojure services and APIs. 61 | 62 | ## Destructure arguments 63 | 64 | Associative destructuring is the most common approach to function definition design 65 | 66 | `:keys` declaration generates local names from the matching keywords given in a vector, `[]` 67 | 68 | ```clojure 69 | (defn destructure-arguments 70 | [{:keys [a b c]}] 71 | (str "Extracted keys have the values:" a b c) 72 | ) 73 | ``` 74 | 75 | ## Function specifications 76 | 77 | `clojure.spec` can be used to instrument a function, checking the correct types of values are passed as arguments. 78 | -------------------------------------------------------------------------------- /docs/code-challenges/index.md: -------------------------------------------------------------------------------- 1 | # Coding Challenges for Clojure 2 | 3 | ![Clojure coding challenges](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/clojure-code-challenges.png) 4 | 5 | Coding challenges are an excellent way to start learning a new language. The challenges allow you to focus on the language and not be concerned about the more general engineering aspects of software development. 6 | 7 | Challenges are there to explore a language and practice your understanding of how to assemble working code. It is recommended to try different approaches to solving a challenges and even repeat the same challenges at a later date and see what additional approaches you have learned. 8 | 9 | [Exercism.io](https://exercism.io){target=_blank} and [4Ever-Clojure](https://4clojure.oxal.org/){target=_blank} are highly recommended starting point for learning Clojure and does not require any installation or setup. 4Ever-Clojure is a new implementation of 4Clojure.com. 10 | 11 | ## Approach to solving challenges 12 | 13 | Take a few minutes to digest the description of the challenge and make notes. 14 | 15 | Identify the simplest possible thing to do, solving the problem in many small pieces which encourages experimentation 16 | 17 | - experiment, write code and evaluate to see what it does (optionally create a comment with the result) 18 | - use a rich comment `(comment ,,,)` to capture multiple ideas and designs, even failed experiments can be useful (separates experiments from working code) 19 | - continually evaluate code as expressions are written, to ensure their behaviour is understood (try different argument values for functions) 20 | - try different shapes of data 21 | - transform data shapes to keep function definitions simpler 22 | 23 | Once there is a working solution, refactor or try different approaches and evaluate the merit of alternative solutions 24 | 25 | 26 | | Challenge website | Description | Requirements | 27 | |-------------------------------------------------------|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------| 28 | | [4Ever-Clojure](4clojure/) | Learning the core functions of the Clojure language | Web Browser | 29 | | [Exercism.io](exercism/) | Coding exercises with mentor support | Web Browser (local: Exercism CLI, Clojure CLI & [Clojure aware editor](/clojure/clojure-editors/) | 30 | | [ClojureScript Koans](http://clojurescriptkoans.com/) | Interactive exercises in a web browser | Web Browser | 31 | | [Simple Projects](/clojure/simple-projects/) | Challenge that can be solved in one session | [Clojure aware editor](/clojure/clojure-editors/) | 32 | | [TDD Code Kata](/clojure/simple-projects/tdd-kata/) | Challenge that can be solved in one session | [Clojure aware editor](/clojure/clojure-editors/) | 33 | | [Advent of Code](advent-of-code.md) | Yearly coding challenge with a seasonal theme | [Clojure aware editor](/clojure/clojure-editors/) | 34 | | [CodeWars](https://www.codewars.com/) | Mostly math-based coding challenges with Clojure variants | Web Browser | 35 | -------------------------------------------------------------------------------- /docs/thinking-functionally/example-hitchhikers-guide.md: -------------------------------------------------------------------------------- 1 | # Example: Hitchhikers Guide 2 | 3 | This is an example of using the threading macros and a REPL to give fast feedback as you are developing code. 4 | 5 | > #### NOTE 6 | > 7 | > Write functions that will give a list of the most used words used in a book, excluding the common English words like "the, and, it, I". Join those functions with a threading macro. 8 | 9 | Suggest you use the assumed perfectly legal copy of the [Hitch-hickers book text](http://clearwhitelight.org/hitch/hhgttg.txt) using the `slurp` function 10 | 11 | **Approximate algorithm** 12 | 13 | * Use a regular expression to create a collection of individual words - eg. **#"[a-zA-Z0-9|']+"** 14 | * Convert all the words to lower case so they match with common words source - `clojure.string/lower-case` 15 | * `Remove` the [common English words](https://www.textfixer.com/tutorials/common-english-words.txt) used in the book, leaving more context specific words 16 | * Calculate the `frequencies` of the remaining words, returning a map of word & word count pairs 17 | * `Sort-by` word count values in the map 18 | * `Reverse` the collection so the most commonly used word is the first element in the map 19 | 20 | 21 | 22 | ```clojure 23 | (def book (slurp "http://clearwhitelight.org/hitch/hhgttg.txt")) 24 | 25 | (def common-english-words 26 | (-> (slurp "https://www.textfixer.com/tutorials/common-english-words.txt") 27 | (clojure.string/split #",") 28 | set)) 29 | 30 | ;; using a function to pull in any book 31 | (defn get-book [book-url] 32 | (slurp book-url)) 33 | 34 | 35 | (defn -main [book-url] 36 | (->> (get-book book-url) 37 | (re-seq #"[a-zA-Z0-9|']+") 38 | (map #(clojure.string/lower-case %)) 39 | (remove common-english-words) 40 | frequencies 41 | (sort-by val) 42 | reverse)) 43 | 44 | ;; Call the program 45 | 46 | (-main "http://clearwhitelight.org/hitch/hhgttg.txt") 47 | ``` 48 | 49 | # Deconstructing the code in the repl 50 | 51 | To understand what each of the functions do in the `-main` function then you can simply comment out one or more expressions using in front of the expression **#_** 52 | 53 | ```clojure 54 | (defn -main [book-url] 55 | (->> (get-book book-url) 56 | #_(re-seq #"[a-zA-Z0-9|']+") 57 | #_(map #(clojure.string/lower-case %)) 58 | #_(remove common-english-words) 59 | #_frequencies 60 | #_(sort-by val) 61 | #_reverse)) 62 | 63 | ``` 64 | 65 | Now the `-main` function will only return the result of the `(get-book book-url)` function. To see what each of the other lines do, simply remove the **#_** character from the front of an expression and re-evaluate the `-main` function in the repl 66 | 67 | > **Hint** In Spacemacs / Emacs, the keybinding C-c C-p show the output in a separate buffer. Very useful when the function returns a large results set. 68 | 69 | ## Off-line sources of Hitch-hickers book and common English words 70 | 71 | ``` 72 | (def book (slurp "./hhgttg.txt")) 73 | 74 | (def common-english-words 75 | (-> (slurp "common-english-words.txt") 76 | (clojure.string/split #",") 77 | set)) 78 | ``` 79 | 80 | Original concept from Misophistful: [Understanding thread macros in clojure](https://www.youtube.com/watch?v=qxE5wDbt964) 81 | 82 | > **Hint** The `slurp` function holds the contents of the whole file in memory, so it may not be appropriate for very large files. If you are dealing with a large file, consider wrapping slurp in a lazy evaluation or use Java IO (eg. `java.io.BufferedReader`, `java.io.FileReader.`). See the [Clojure I/O cookbook](https://nakkaya.com/2010/06/15/clojure-io-cookbook/) and [The Ins & Outs of Clojure](http://blog.isaachodes.io/p/clojure-io-p1/) for examples. 83 | 84 | 85 | -------------------------------------------------------------------------------- /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/additional-projects/clojurebridge-website/add-navigation.md: -------------------------------------------------------------------------------- 1 | # Add Navigation bar 2 | 3 | To jump to specific content, we will add a navigation bar at the top of the page. 4 | 5 | The navigation will be fixed in place, so it will not move as the page is scrolled. 6 | 7 | The navigation will be responsive, so when the website is viewed on smaller screens, the navigation will show as a drop-down button (referred to as a burger as that is the rough shape of the icon typically used). 8 | 9 | 10 | 11 | ## Navigation section 12 | 13 | Create a function to define the navigation, using the `navbar` class from Bulma. 14 | 15 | 16 | ```clojure 17 | (defn navigation-top 18 | "A responsive navigation that is fixed to the top of the page" 19 | [] 20 | 21 | [:nav {:class "navbar is-fixed-top is-dark" 22 | :role "navigation" 23 | :aria-label "main navigation"} 24 | [:div {:class "container"} 25 | [:div {:class "navbar-brand"} 26 | [:a {:class "navbar-item" 27 | :href "/"} 28 | [:img {:src "images/clojurebridge-logo.png"}]] 29 | [:span {:class "navbar-burger burger" 30 | :data-target "navbarClojureBridge"} 31 | ;; Empty spans needed for navbar burger 32 | [:span][:span][:span]]] 33 | 34 | [:div {:id "navbarClojureBridge" 35 | :class "navbar-menu"} 36 | [:div {:class "navbar-start"} 37 | [:a {:class "navbar-item" 38 | :href "#overview"} "Overview"] 39 | [:a {:class "navbar-item" 40 | :href "#showcase"} "Showcase"] 41 | [:a {:class "navbar-item" 42 | :href "#learning-paths"} "Learning Paths"] 43 | [:a {:class "navbar-item" 44 | :href "#schedule"} "Schedule"] 45 | [:a {:class "navbar-item" 46 | :href "#install"} "Install"] 47 | [:a {:class "navbar-item" 48 | :href "#resources"} "Resources"] 49 | [:a {:class "navbar-item" 50 | :href "#resources"} "Coaches"] 51 | [:a {:class "navbar-item" 52 | :href "#sponsors"} "Sponsors"] 53 | 54 | [:span {:class "navbar-item"} 55 | [:a {:class "button is-inverted" 56 | :target "_blank" 57 | :href "https://github.com/ClojureBridgeLondon/landing-page-draft"} 58 | [:span {:class "icon"} 59 | [:i {:class "fab fa-github"}]] 60 | [:span "Issues/PRs"]]]]]]] 61 | ) 62 | ``` 63 | 64 | 65 | ## Fixing spacing under navbar 66 | 67 | To prevent hiding content under the navigation bar, we add the `has-navbar-fixed-top` class to the body of the `index.html` file 68 | 69 | ```html 70 | 71 | ``` 72 | 73 | 74 | ## Script to populate the drop-down menu 75 | 76 | As Bulma does not have any JavaScript functionality, we need to add a script to dynamically populate the navigation bar drop-down menu when on smaller devices. 77 | 78 | ```html 79 | 80 | 81 | 92 | ``` 93 | 94 | > #### TODO:: 95 | > The script is the approach that Bulma suggests. However, it should be possible to write ClojureScript to do this work. This JavaScript would then be removed. 96 | > 97 | > One approach is to make the navigation data part of the application state, then it can be easily used and updated. Using the application state helps make the webpage layout more dynamic, as changes to the application state will cause parts of the website to be re-drawn (re-rendered). 98 | -------------------------------------------------------------------------------- /docs/introduction/concepts/design.md: -------------------------------------------------------------------------------- 1 | # Clojure Design 2 | 3 | Clojure leads to a very component based approach to development. There are no huge and bloated frameworks in Clojure. The core is very small. Hundreds of focused libraries to use in collaboration. 4 | 5 | Boiled down to the most simplest structure, Clojure applications you write typically look like this: 6 | 7 | ```clojure 8 | ;; define a namespace 9 | (ns name-space.name) 10 | 11 | ;; define one or more immutable data structures - the fewer the better typically 12 | (def my-data-structure [[{}{}]]) 13 | 14 | ;; define behaviour that acts on data structures inside one or more functions 15 | (defn my-function [parameter] 16 | (my-behaviour parameter)) 17 | 18 | ;; Call those functions to make your application do something 19 | (my-behaviour data) 20 | ``` 21 | 22 | > **Hint** As functions always evaluate to a value, a function can be used as an argument to another function (or itself if you get recursive !!) 23 | 24 | ## Data focused design - Maps & Vectors 25 | 26 | Maps (hash-map) and vectors are two more built-in persistent data structures that are more commonly used to represent data within a Clojure application. 27 | 28 | A vector is similar to an array in that its an indexed collection and so has fast random access. Vectors are a catch all data structure that can hold any type of information, including other data structures and function calls. 29 | 30 | A hash-map is an associative data structure with key value pairs. The keys are most commonly represented with Clojure keywords, although keys can be strings, numbers, collections or functions so long as all the keys are unique. 31 | 32 | Hash-maps are a collection of key / value pairs that provide an easy way to reference data by keys. Its common to use a Clojure `keyword` type as the keys as keywords are self-referential (they point to themselves). Using keywords in a map means you can use a specific keyword as a function call on the map that returns its associated value. 33 | 34 | Some examples of using these data structures this are: 35 | 36 | ```clojure 37 | 38 | ;; A map of maps of maps with occasional vectors 39 | 40 | {:star-wars { 41 | :characters { 42 | :jedi ["Luke Skywalker" 43 | "Obiwan Kenobi"] 44 | :sith ["Darth Vader" 45 | "Darth Sideous"] 46 | :droids ["C3P0" 47 | "R2D2"]} 48 | :ships { 49 | :rebel-alliance ["Millennium Falcon" 50 | "X-wing fighter"] 51 | :imperial-empire ["Intergalactic Cruiser" 52 | "Destroyer" 53 | "Im just making these up now"]}}} 54 | ``` 55 | 56 | > #### Hint::Basic design principle 57 | > 58 | > “It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.” —Alan Perlis 59 | 60 | ## Extensibility via Macros 61 | 62 | You can extend the language and define your own constructs using Macros. 63 | 64 | The first example of this you see is from Leiningen. The `defproject` function is a macro that helps you easily define the configuration of a Clojure project. 65 | 66 | An example of a macro that is part of the core Clojure language is `defn`. When you define a function with `defn` it is syntactic sugar for defining a thing that is a function. 67 | 68 | ```clojure 69 | (defn my-function [argument] (my-behaviour argument) ) 70 | 71 | (def my-function 72 | (fn [argument] (my-behaviour argument))) 73 | ``` 74 | 75 | ## Special forms - the building blocks of Clojure 76 | 77 | The following are the building blocks of Clojure, everything else is either a macro or a function 78 | 79 | The Clojure / LISP special forms 80 | 81 | ``` 82 | def, do, if, let, loop, fn, quote, recur, set!, var 83 | ``` 84 | 85 | The forms added for Interoperability with the host platform (mainly Java / JVM) 86 | 87 | ``` 88 | monitor-enter, monitor-exit, 89 | catch, dot ('.'), finally, new, throw, try 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/thinking-functionally/homoiconicity.md: -------------------------------------------------------------------------------- 1 | # Homoiconicity 2 | 3 | Clojure is a homoiconic language, which is a term describing the fact that Clojure programs are represented by Clojure data structures. 4 | 5 | In Clojure you write your business logic as functions. A function is defined using a list structure. A function is called using a list structure, as the first element of a list is evaluated as a function call. 6 | 7 | > **Hint** Everything in Clojure is a _List_ (or vector, map, set). 8 | 9 | This is a very important difference between Clojure (and Common Lisp) and most other programming languages - Clojure is defined in terms of the evaluation of data structures and not in terms of the syntax of character streams/files. 10 | 11 | It is quite easy for Clojure programs to manipulate, transform and produce other Clojure programs. This is essentially what macros do in Clojure, they re-write Clojure for you. 12 | 13 | > **Hint** If you were going to create Skynet, it would be so much easier to do in Clojure 14 | 15 | ![Skynet logo](https://truthernews.files.wordpress.com/2015/04/skynet_wallpaper.jpg) 16 | 17 | ## An example 18 | 19 | Consider the following expression: 20 | 21 | ``` 22 | (let [x 1] 23 | (inc x)) 24 | ``` 25 | 26 | Evaluating the above code in the REPL returns `2` because the repl compiles and executes any code entered into it. But `[x 1]` is also a literal vector data structure when it appears in a different context. 27 | 28 | All Clojure code can be interpreted as data in this way. In fact, Clojure is a superset of EDN – Extensible Data Notation, a data transfer format similar to JSON. EDN supports numbers, strings, lists (1 2 3), vectors [1 2 3], maps {"key" "value"}. 29 | 30 | If this sounds and looks a lot like Clojure syntax, it’s because it is. The relationship between Clojure and EDN is similar to that of Javascript and JSON, but much more powerful. 31 | 32 | In Clojure, unlike JavaScript, all code is written in this data format. We can look at our let statement not as Clojure code, but an EDN data structure. Let’s take a closer look: 33 | 34 | ``` 35 | (let [x 1] 36 | (inc x)) 37 | ``` 38 | 39 | In this data structure, there are four different types of data. 40 | 41 | * 1 is a literal integer. 42 | * let, x, and inc are symbols. A symbol is an object representing a name – think a string, but as an atomic object and not a sequence of characters. 43 | * [x 1] is a vector containing two elements: symbol, x, and an integer, 1. Square brackets always signify vectors when talking about EDN data structures. 44 | * (inc x) is a list (a linked list data structure) containing two symbols, inc and x. 45 | 46 | When thinking about a piece of Clojure code as a data structure, we say we are talking about the form. Clojure programmers don’t normally talk about EDN, there are just two ways to think about any bit of Clojure: 1) as code that will execute or 2) as a form, a data structure composed of numbers, symbols, keywords, strings, vectors, lists, maps, etc. 47 | 48 | Symbols are particularly important. They are first class names. In Clojure, we distinguish between a variable and the name of that variable. When our code is executing, x refers to the variable established by our let binding. But when we deal with that code as a form, x is just a piece of data, it’s a name, which in Clojure is called a symbol. 49 | 50 | This is why Clojure is homoiconic. Code forms are data structures and data structures can be thought of as forms and executed as code. This transformation is quite literal, and two core operations, quote and eval are key ingredients to this potion. 51 | 52 | ## References 53 | 54 | * [The Reader - Clojure. org](http://clojure.org/reference/reader) 55 | * [Homoiconicity - Wikipedia](https://en.wikipedia.org/wiki/Homoiconicity) 56 | * [Is Clojure Homoiconic - muhuk.com](http://blog.muhuk.com/2014/09/28/is_clojure_homoiconic.html) 57 | * [Understanding Homoiconicity in Clojure - Drew Colthorp](https://spin.atomicobject.com/2013/07/23/homoiconicity-clojure-macros/) 58 | -------------------------------------------------------------------------------- /docs/code-challenges/advent-of-code.md: -------------------------------------------------------------------------------- 1 | ## Advent Of Code 2 | 3 | ![Advent of Code](/images/advent-of-code.png) 4 | 5 | [Advent of Code](https://adventofcode.com/) is the annual coding challenge with a festive theme. Each day there is a new challenge in two parts, the first fairly easy the second a little more involved. The challenges are an investment of your time to complete them all, although even trying just a few is enough to help you think in different ways. 6 | 7 | Every programming language requires regular practice to maintain your skills. A full time developer role gives lots of opportunities to practice every day, however, its often focused in around solving problems within a specific business domain, with little time to explore others. The Advent of Code puts you in a different domain, so its great for extending your coding experiences. 8 | 9 | Solving challenges in a different language is another great way to extend your experiences, so here are some tips and examples for solving the advent of code in Clojure. 10 | 11 | ## Solving challenges 12 | 13 | * Keep the solution as simple as possible. Its very easy to over-complicate the solution and end up simply confusing yourself. 14 | * Don't try and make the perfect solution. Write something that works, this will give you a nice ego boost. Then you can experiment with the code and see if you can improve your approach. 15 | * Break down the problem into the simplest thing you can solve first. Trying to solve a problem all at once will quickly have you going around in circles. 16 | * Keep all the code and make notes. I use a a design journal in my projects to document my thinking process, capture decisions that worked and those that didn't work for this project. The journal is a great way to cement learning from solving the challenge. 17 | * Challenges are only accessible from their day of the month onwards. There is a count-down clock displayed on the next challenge to open, so you know when it will be available. Don't feel pressured to keep up with the challenges though, enjoy the experience and have fun, you will learn more that way. 18 | 19 | ![Advent Of Code 2019 Day 1 challenge snippet](https://raw.githubusercontent.com/practicalli/graphic-design/live/code-challenges/advent-of-code-2019-day1.png) 20 | 21 | ## Coding video 22 | 23 | A video guide to solving the first challenge of Advent of Code from 2018, trying out different solutions at increasing levels of abstraction. With each level of abstraction it helps to think in a more functional way. 24 | 25 |

26 | 27 |

28 | 29 | ## Creating a project for the challenge 30 | 31 | ```bash 32 | clojure -T:project/create :template lib practicalli.advent-of-clojure-code/2019 33 | ``` 34 | 35 | Create a new Clojure file for each of the daily challenges. It makes sense to keep both parts of each day in the same file. 36 | 37 | !!! EXAMPLE "Practicalli Advent Of Code solutions repository" 38 | [practicalli/advent-of-clojure-code-2019](https://github.com/practicalli/advent-of-clojure-code-2019) 39 | 40 | ## Useful Resources And Examples 41 | 42 | Videos and code solutions to many challenges from 2019 and past years. 43 | 44 | * [fdlk/advent-2019](https://github.com/fdlk/advent-2019) - example Clojure solutions to the advent of code 45 | * [Awesome Advent Of Code](https://github.com/Bogdanp/awesome-advent-of-code) - a collection of solutions in various languages 46 | * [Advent of Code 2018 video walk-through of Clojure solutions by Tim Pote](https://potetm.com/videos.html) and [GitHub repository](https://github.com/potetm/advent-of-code) 47 | 48 | [#adventofcode channel in the Clojurians slack channel](https://clojurians.slack.com/messages/adventofcode) discusses challenges and solutions, especially during December when the challenge takes place. 49 | -------------------------------------------------------------------------------- /docs/thinking-functionally/partial-functions.md: -------------------------------------------------------------------------------- 1 | # Currying & Partial Functions 2 | 3 | Clojure does not support automatic currying, (+3) would result in applying + to 3, resulting with number 3 instead of a function that adds 3 as in Haskell. Therefore, in Clojure we use partial that enables the equivalent behavior. 4 | 5 | ```clojure 6 | (defn sum 7 | "Sum two numbers together" 8 | [number1 number2] 9 | (+ number1 number2)) 10 | 11 | (sum 1 2) 12 | ;; => 3 13 | ``` 14 | 15 | If you try and evaluate `sum` with a single value then you get an arity exception 16 | 17 | ```clojure 18 | (sum 1) 19 | ;; => clojure.lang.ArityException 20 | ;; => Wrong number of args (1) passed to: functional-concepts/sum 21 | 22 | ``` 23 | 24 | If we did need to call sum with fewer than the required arguments, for example if we are mapping sum over a vector, then we can use partial to help us call the sum function with the right number of arguments. 25 | 26 | Lets add the value 2 to each element in our collection 27 | 28 | ```clojure 29 | (map (partial sum 2) [1 3 5 7 9]) 30 | ``` 31 | 32 | ## Using functions on more arguments than they can normally take 33 | 34 | The `reduce` function can only work on a single collection as an argument (or a value and a collection), so an error occurs if you wish to reduce over multiple collections. 35 | 36 | ```clojure 37 | (reduce + [1 2 3 4]) 38 | ;; => 10 39 | 40 | (reduce + [1 2 3 4] [5 6 7 8]) 41 | ;; returns an error due to invalid arguments 42 | ``` 43 | 44 | However, by using partial we can take one collection at once and return the result of reduce on each of those collections. 45 | 46 | ```clojure 47 | (map (partial reduce +) [[1 2 3 4] [5 6 7 8]]) 48 | ``` 49 | 50 | In the above example we map the partial reduce function over each element of the vector, each element being a collection. 51 | 52 | # Using partial to set a default value 53 | 54 | We can use the partial function to create a default message that can be just given just the custom part. For example, if we want to have a default welcome message but include a custom part to the message at the end. 55 | 56 | First we would define a function that combines parts of the message together. 57 | 58 | ```clojure 59 | (defn join-strings 60 | "join one or more strings" 61 | [& args] 62 | (apply str args)) 63 | ``` 64 | 65 | The [& args] argument string says take all the arguments passed and refer to them by the name args. Its the & character that has the semantic meaning, so any name after the & can be used, although args is common if there is no domain specific context involved. 66 | 67 | We can simply call this function with all the words of the message. 68 | 69 | ```clojure 70 | (join-strings "Hello" " " "Clojure" " " "world") 71 | ;; ⇒ "Hello Clojure world" 72 | ``` 73 | 74 | Now we define a name called `wrap-message` that can be used to wrap the start of our message. This name binds to a partial function call to `join-strings` which send that function the default message and any custom message you add when evaluate `wrap-message` 75 | 76 | ```clojure 77 | (def wrap-message (partial join-strings "Hello Clojurians in ")) 78 | 79 | (wrap-message) 80 | ;; ⇒ "Hello Clojurians in " 81 | 82 | (wrap-message "London") 83 | ;; => "Hello Clojurians in London" 84 | ``` 85 | 86 | ## Currying in clojure 87 | 88 | Currying is the process of taking some function that accepts multiple arguments, and turning it into a sequence of functions, each accepting a single argument. Or put another way, to transform a function with multiple arguments into a chain of single-argument functions. 89 | 90 | Currying relies on having fixed argument sizes, whereas Clojure gets a lot of flexibility from variable argument lengths (variable arity). 91 | 92 | Clojure therefore has the partial function gives results similar to currying, however the `partial` function also works with variable functions. 93 | 94 | `partial` refers to supplying some number of arguments to a function, and getting back a new function that takes the rest of the arguments and returns the final result 95 | 96 | One advantage of `partial` is to avoid having to write your own anonymous functions 97 | 98 | # Useful references 99 | 100 | * [Partial function applications for humans](http://andrewberls.com/blog/post/partial-function-application-for-humans) 101 | -------------------------------------------------------------------------------- /docs/introduction/concepts/clojure-from-the-author.md: -------------------------------------------------------------------------------- 1 | # Clojure from the Author 2 | 3 | A series of important videos from Rich Hickey, the author of Clojure who spent over 2 years designing core of Clojure around the concept of simplicity. Since then Rich has stewarded the continued design and development of Clojure, along with the Cognitect team, ensuring Clojure stays true to is founding principles. 4 | 5 | !!! HINT "Try Clojure in the REPL" 6 | The videos can be watched at any time during the journey into Clojure. 7 | 8 | An effective way to learn and become comfortable with Clojure is to write code and evaluate it in the REPL. 9 | 10 | Presentations by Rich Hickey are an excellent way understand the design philosophy behind Clojure and support adoption of Clojure as a highly effective language for software development. 11 | 12 | ## Expert to Expert: Rich Hickey and Brian Beckman - Inside Clojure 13 | 14 | Discussing some of the key characteristics of the Clojure language and why those decisions were taken 15 | 16 |

17 | 18 |

19 | 20 | ## Clojure made simple 21 | 22 | Covers the major problems with software development and the challenges most programming languages fail to tackle completely. 23 | 24 | Discusses a simple approach to software development and the big picture view of how Clojure solves these problems 25 | 26 |

27 | 28 |

29 | 30 | ## Simplicity Matters 31 | 32 | !!! QUOTE Rich Hickey, Clojure Benevolent Dictator for Life 33 | As we move forward we have to take what we already have and make that [software] do more, make it do things differently, make it do things better, 34 | 35 | ... as we try to take on manipulating software we are ... challenged to understand it in order to make that happen. 36 | 37 | ... I'll contend that you will completely be dominated by complexity. I don't care what processes you are using, I don't care how well you test or anything else. 38 | 39 | Complexity will dominate what you do. 40 | 41 |

42 | 43 |

44 | 45 | ## Discussing Design 46 | 47 |

48 | 49 |

50 | 51 | ## The value of values 52 | 53 | Rich Hickey provides analysis of the changing way we think about values (not the philosophical kind) in light of the increasing complexity of information technology and the advent of Big Data 54 | 55 |

56 | 57 |

58 | 59 | Also see the related video: [Database as a value by Rich Hickey](https://youtu.be/EKdV1IgAaFc){target=_blank} 60 | 61 |

62 | 63 |

64 | 65 | ## Understanding Clojure as a programming language 66 | 67 |

68 | 69 |

70 | -------------------------------------------------------------------------------- /docs/code-challenges/exercism/bob/index.md: -------------------------------------------------------------------------------- 1 | ## Bob 2 | 3 | ![Exercism.io challenge - Bob](https://raw.githubusercontent.com/practicalli/graphic-design/live/banners/exercism/exercisim-exercise-bob-banner.png) 4 | 5 | The Bob challenge involves writing a very basics example of a text parser, something that would be used for [a text based adventure game](https://en.wikipedia.org/wiki/Text-based_game). 6 | 7 | Bob is described as a [lackadaisical](https://en.wiktionary.org/wiki/lackadaisical) teenager, so responses are very limited. To create the Bob text parser we need to identify the rules that determine Bob's response. 8 | 9 | The instructions provide some basic rules: 10 | 11 | * Bob answers 'Sure.' if you ask him a question. 12 | * He answers 'Whoa, chill out!' if you yell at him. 13 | * He answers 'Calm down, I know what I'm doing!' if you yell a question at him. 14 | * He says 'Fine. Be that way!' if you address him without actually saying anything. 15 | * He answers 'Whatever.' to anything else. 16 | 17 | It is important to also read through the supplied unit tests to elaborate on these rules. 18 | 19 | ## Create the project 20 | 21 | Download the Bob transcription exercise using the exercism CLI tool 22 | 23 | ```bash 24 | exercism download --exercise=bob --track=clojure 25 | ``` 26 | 27 | > To use the Clojure CLI tool instead of Leiningen, create a `deps.edn` file containing an empty hash-map, `{}` and clone [:fontawesome-solid-book-open: Practicalli Clojure CLI Config](clojure/clojure-cli/practicalli-config.md) to `~/.clojure/`. 28 | 29 | ## Rules derived from the Unit tests 30 | 31 | Reviewing all the examples from the unit tests, there are 5 rules for the Bob parser 32 | 33 | These rules were discovered by searching through the unit test code for each reply that Bob should return, showing the tests for each reply. 34 | 35 | Each rule also had to ensure it did not create any false positives by being true for any other reply that Bob could make, especially the whatever reply. 36 | 37 | | Name | Rule description | 38 | |-------------------|-----------------------------------------------------------------------------------------| 39 | | question | The phrase has a ? as the last alphanumeric character, not including whitespace | 40 | | shouting | The phrase has uppercase alphabetic characters, but no lower case alphabetic characters | 41 | | shouting question | A combination of question and shouting | 42 | | silence | The phrase is empty or contains characters that are not alphanumeric | 43 | | whatever | Any phrase that does not match any of the other rules | 44 | 45 | ## Design approach 46 | 47 | There are two main approaches to solving this challenge. The first is to use the [`clojure.string`](https://clojure.github.io/clojure/clojure.string-api.html) functions to check or transform the phrase given to Bob. The second approach is to use [regular expressions](/reference/standard-library/regular-expressions) with functions such as [`re-seq`](https://clojuredocs.org/clojure.core/re-seq), [`re-find`](https://clojuredocs.org/clojure.core/re-find) and [`re-matches`](https://clojuredocs.org/clojure.core/re-matches). 48 | 49 | Start by defining the rules as an expression that returns either true or false, using some of the example strings from the unit tests. 50 | 51 | Use a `let` expression to bind a name to each rule, e.g. `shouting?`, `question?`, `silence?`. Then these names can be used in a simple `cond` expression to return the appropriate phrase. Regardless of if using `clojure.string` or regular expressions, the `cond` code should be similar 52 | 53 | Once you have tried this challenge for yourself, take a look at the design journal for the [clojure.string approach](bob-string-approach.md) and the [regular expression approach](bob-regular-expression-approach.md). 54 | 55 | [Bob - clojure.string approach](bob-string-approach.md){.md-button} 56 | [Bob - regular expression approach](bob-regular-expression-approach.md){.md-button} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | --------------------------------------------------------------------------------