Code Monkey home page Code Monkey logo

clojure's People

Contributors

customcommander avatar dareno avatar dijonkitchen avatar fhdkhan avatar frwdrik avatar iarenaza avatar jeffallen avatar led avatar littleli avatar phronmophobic avatar practicalli-johnny avatar rayjolt avatar shivamarora1 avatar sritchie avatar staaikies avatar vedang avatar zmitchell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

clojure's Issues

`def` caching results

The first time the def function is called it will cache the results. This makes the call quite efficient in subsequent times.

This can cause confusion if you expect the def value to change, for example

(def not-random (rand-int 1000))

When the repl is restarted or cleared of the def, then the next evaluation of the def expression will generate the value again.

Using the REPL with Clojure CLI tools and deps.edn

Using a Clojure project from the REPL

  • start the repl (with and without rebel readline)
  • require the namespace for the code you wish to use
  • change to the namespace to call functions without their full namespace path

Understanding require and ns macro

In a REPL di
(require '[clojure.set :refer :as set])
(set union #{:a} #{:a :b :c})

' means "don't evaluate this form"
so symbols are used as is and are not resolved, and function calls are just lists
it prevents an error as clojure.set isn't bound
it's the symbolic name of something you want to load
you'd get a syntax error if you used it unquoted

The ' is not needed when used in ns as this is a macro
(ns my-namespace
(:require [clojure.set :refer :set]))

As the ns form is a macro it doesn't try to evaluate the symbols in the require form. A macro can decide whether something should be evaluated
(elaborate)

When calling a function (eg. require) the arguments are evaluated first, then passed to the function

when you call a macro (eg. ns) the args are passed as a list of symbols and input literals and the macro decides what to do with them

I mean a macro usually does evaluate things (clojure macros can use the entire clojure language as needed), but they usually just turn one list (form) into another then return it to be compiled

kata - condensing characters

Convert
("WWWWW" "BB" "BBBB" "W")
into
("5W" "2B" "4B" "W")

A possibly overcomplicated way to do this would be

(defn foo [things]
  (mapcat
    (fn [[k vs]]
      (map
        #(str (if (= k 1) "" k) (first %))
        vs))
    (group-by count things)))

(foo '("WWWWW" "BB" "BBBB" "W" "AA" "BB" "CC"))
("5W" "2B" "2A" "2B" "2C" "4B" "W")

A simple way without a transducer (not as efficient)
(def data '("WWWWW" "BB" "BBBB" "W"))
(->> data (map (juxt count first)) (map #(apply str %)))
("5W" "2B" "4B" "1W")

(def data ["WW" "BB" "CC"])
(->> a (map (juxt count first)) (map #(apply str %)))
("2W" "2B" "2C")

Using a transducer

(into [] (comp (map (juxt count first)) (map #(apply str %))) data)
["2W" "2B" "2C"]
(def data '("WWWWW" "BB" "BBBB" "W"))
(into [] (comp (map (juxt count first)) (map #(apply str %))) data)
["5W" "2B" "4B" "1W"]

Needs a conditional to return just W instead of 1W - or use another map in the transducer version

(map (fn [[n c :as pair]] (if (= n 1) [c] pair)))

Putting that map function in the transducer version of the solution:

"W" for the single letter case so I'd need an extra little transformation in my suggestion

(into [] (comp (map (juxt count first)) 
                      (map (fn [[n c :as pair]] (if (= 1 n) [c] pair))) 
                      (map #(apply str %))) a)

["5W" "2B" "4B" "W"]

clojure.core - into

f i want to conj a bunch of objects onto a vector in a map atom, and i have them in a list, is it better to write a (doseq [obj objects] (swap! v update :key conj obj) or something like (swap! v update :key #(apply conj % objects))?

that's a cumbersome way of saying: is it "better"/more idiomatic to do a bunch of swap!s or a single swap! with apply?

absolutely the latter
the former is N separate transactions (and someone else could be doing the same thing, interleaving the results)
👍
1

but really you'd probably be better off with (swap! v update :key into objects)
but be careful with the nil case - into uses a list in that case
I guess conj has the same problem (since into uses conj)
into will also automatically use transients if it can (like with vectors)

guard against the nil case with fnil like so: (swap! v update :key (fnil into []) objects), to make the type of the collection explicit.
Can easily swap out the vector in (fnil into []) for a #{} or {} depending on the type one wants.

Clojure YouTube website

A server-side web application that presents the practicalli videos using the YouTube API.

Aliases in clojure.spec

Is there a way to use spec on a map without mandating that the kw and spec have the same name? Like
{:sponsor value-conforming-to-:person-spec}

If you're working with unqualified keys, probably the simplest workaround would be an alias spec:

(s/def ::person-spec ,,, whatever ,,,)
(s/def :person/sponsor ::person-spec)
(s/def ::sponsor-map (s/keys :req-un [:person/sponsor ,,,]))

That key will be :sponsor but the spec will be :person/sponsor which is aliased to ::person-spec.

Small Puzzle: Uniquely random numbers

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.

How do you randomly assign seats without choosing the same seat twice.

Clojure namespaces

So what's the story on the 'project-name' subfolder under src/ in the standard Leiningen template? Is there a reason you don't want your source files to live directly under src/? Is there a situation where you'd want that subdir to be named something else, or to have multiple ones?
alexmiller 22:25
that ends up being the first segment of your project namespaces (edited)
generally, you're going to be mixing your code with other libraries, etc so it is a good and common practice to prefix all of the namespaces in your project with a common namespace prefix, ideally something that you "own" (reverse domain name, company name, trademark, etc)
so Clojure's own namespaces are things like clojure.string (where "clojure" is the common root)
if you just called it string, you'd be fighting with every other library and project that had a string namespace
hiredman 22:28
namespace names and file nesting in directories correspond to each other, and it is impolite to use a short non-segmented name (segments are separated by '.', each namespace segment corresponds to a nested directory) because of the possibility of collisions. the compilation model, how clojure generates bytecode in classes and names those classes is also effected by this, in that a non-segmented namespacee names result in classes in the global package (or whatever java calls it) and that is mostly considered poor practice to do on the java side of things
different tools handle the new project names differently, but with lein if you say lein new foo you get a foo.core namespace to start, but if you say lein new foo.bar/baz you get a foo.bar.baz namespace to start
seancorfield 22:39
The clj-new tool (for the new CLI/deps.edn tooling) won't let you specify a single segment project name. If you want foo.core, you must explicitly ask for it, but the readme explains that you should generally use project names like username/myproject to ensure that your namespaces follow good practices (username.project as the entry point). Where username would be your GitHub user name, for example, or your company's name or reverse domain name etc.
(unfortunately, not all project templates accept the username/project format, so you sometime have to use username.project instead, although some templates don't even accept that format either 😞 )

Running tests with Clojure deps.edn projects

Running tests

  • running tests with different test runners (each test runner probably deserves their own video - maybe do a summary of 3 most common - cognitect.test-runner, eftest, kaocha.

Relates to #172

Getting Started with Clojure CLI and tools.deps

  • Clojure CLI Getting Started - Video Script

Welcome to Practicalli, thank you for joining me.

I'm John and i'll introduce you to the Clojure CLI tools,
the simplest way of running and working with a Clojure REPL and projects.

This video assumes you have installed the Clojure CLI tools and the Practicalli deps.edn configuration. See the video on installing the Clojure CLI tools if you havent done so already.

I will show you how to run a single Clojure expression so you can quickly try a little Clojure.
How to run a feature rich REPL for a simple development environment..
How to create and run Clojure projects

Finally we will see how to run a REPL, which is a live Clojure environment in which you can immediately evaluate your code, so you get feedback as you write your code.

** Running Clojure code

Using the -e option with the clojure command you can evaluate a Clojure expression, an expression being any single piece of correct Clojure code.

For example
clojure -e "(+ 1 2 3)"

This command prints out the value returned from evaluating the Clojure code.

You can run an existing Clojure project using the -m option. The -m option sets the main namespace,
the entry point into your application

The project does need to have a -main or main function defined and have (:gen-class) in the namespace.

clojure -m simple.core

This is a very simple project and prints out "Hello World!"

** Running a Clojure REPL

Clojure development is done with a REPL. This is your live Clojure environment,
where you can run your code as you write it and get immediate feedback.

There is no external compilation of code, it all happens as soon as you evaluate the expression.

Typing clojure into a command line terminal will start a Clojure REPL
Although better REPL experience is provided via the clj command.

clj adds the rlwrap command, providing a readline for the Clojure REPL.

A readline library provides line-editing and history capabilities,
essential for interactive command line tools such as the Clojure REPL.

[run clj in a terminal]

** Extending Clojure CLI tool with Aliases
<Review the ~/.clojure/deps.edn> file from practicalli and discuss aliases.

clojure and clj commands can use aliase with the -A option, for example clojure -A:new uses the alias called :new, which in this case creates a project.

To create a Clojure project that we can run as an application, use the app template.
clojure -A:new app domain/main-namespace

The domain is the top level grouping for your project, like google, cognitect, clojure.org. It could be the company you work at, the business area, application suite the project is part of or your GitHub account or organisation name for a personal or open source project.

The main-namespace is usually the name of the specific application or service you are building.

This creates a project with a standard structure

[tree of project]

Other templates can be used such as lib when building a library of functions. There are many other templates defined by the community [provide a link / create a website with a curated list of templates]

** Rebel Readline Alias
Rebel Readline provides function signature information, documentation, editing styles and tools to manage the REPL, including a quit function.

Using an alias, the rebel readline library is added as a dependency and its main namespace specified so it will run when calling the clojure command.

Including an alias for rebel readline in $HOME/.clojure/deps.edn makes it available to all projects.

There are many useful aliases defined in the Practicalli deps.edn configuration.

** In summary
This has covered the basics of evaluating Clojure code, running a REPL and working with projects so you have a good foundation on which to learn how to write Clojure code.

Thank you for listening and see you in the next video.

Simple Finite State Machine - Rock Paper Scissors

Solving the "Rock Paper Scisors" game with a very simple state machine, defined as a clojure hash-map. Challenge taken from Codewars
https://www.codewars.com/kata/rock-paper-scissors/

Instructions

Let's play! You have to return which player won! In case of a draw return Draw!.

Examples:

(rps "scissors" "paper") 
;; => "Player 1 won!"
(rps "scissors" "rock")
;; => "Player 2 won!"
(rps "paper" "paper") 
;; => "Draw!"

The winner can be decided using a simple cond function (rather than nested if functions).

The draw state is simple, just compare if the strings for player one and player two are equal.

Defining when rock paper and scissors wins is done by defining which choices each of them beat. In the basic game, each choice will only beat one other choice and will only loose to one other choice.

Defining a simple state machine is easy, using a Clojure hash-map. For each entry in the hash-map, the choice is the key and the choice that it beats is the value.

So if Player1 chooses rock, we look up rock in the hash-map and get the value that is the thing that it beats. Compare the value retrieved from the hash-map with the value of player 2, if the values are equal then player 1 has won.

(ns rock-paper-scissors)

(def winner {"rock"     "scissors"
            "scissors" "paper"
            "paper"    "rock"})

(defn rps [p1 p2]
  (cond
  (= p1 p2) "Draw!"
  (= (get winner p1) p2) "Player 1 won!"
  (= (get winner p2) p1) "Player 2 won!"))

We could remove (= (get winner p2) p1) "Player 2 won!" and just put a default result of "Player 2 won!", because if its not a draw and player1 did not win, then player 2 must have won.

Sample Tests from Codewars

(ns rock-paper-scissors-tests
  (:require [clojure.test :refer :all]
            [rock-paper-scissors :refer [rps]]))

(deftest rps-tests
  (testing "player 1 win"
    (are [p1 p2] (= "Player 1 won!" (rps p1 p2))
      "rock" "scissors"
      "scissors" "paper"
      "paper" "rock"))
  (testing "player 2 win"
    (are [p1 p2] (= "Player 2 won!" (rps p1 p2))
      "scissors" "rock"
      "paper" "scissors"
      "rock" "paper"))
  (testing "draw"
    (are [p1 p2] (= "Draw!" (rps p1 p2))
      "rock" "rock"
      "scissors" "scissors"
      "paper" "paper"))
)

Rock Paper Scissors Spock

https://en.wikipedia.org/wiki/Rock_paper_scissors

Finite State machine for the game
https://en.wikipedia.org/wiki/Rock_paper_scissors#/media/File:Rock_Paper_Scissors_Lizard_Spock_en.svg

(def rules-finite-state-machine
  {"rock"     {"lizard" "scissors"}
   "paper"    {"spock" "rock"}
   "scissors" {"paper" "lizard"}
   "lizard"   {"spock" "paper"}
   "spock"    {"scissors" "rock"}})

Related articles

http://blog.cognitect.com/blog/2017/5/22/restate-your-ui-using-state-machines-to-simplify-user-interface-development

Foundation: using a map as a translation between two alphabets, eg. in the Clacks example. The map is used to move from a character in one alphabet state to a character in another alphabet state.

Clojure CLI tools in development tools section

Update the development tools section to recommend using Clojure CLI tools. This should provide the simplest tooling for learning Clojure and it works with all common editiors/ides.

Note that there may still be issues for Windows users

Keep the section on Leiningen, should anyone want to use that approach as an alternative.

clojure.test

  • development tests vs api namespace tests
  • using deftest- to isolate development tests (testing those functions that are low level algoritym testing functions and will become private functions, i.e. defn- functions
    See WeFarm interview code for an example

Understanding Evaluation

Pose question: What is the simplest hello world code in Clojure

"Hello world"

You could define a function

"Hello World!")

Or a more generic approach would be

      (if (string? text)
        text
        (str text)))

Clojure CLI tools - set local Maven repo (the maven .m2 cache)

Clojure CLI tools saves the jar files downloaded for dependencies to the local computer, in a directory called $HOME/.m2/repository

This is the maven cache directory by default for all projects. This cache directory can be changed by setting a new directory with :mvn/local-repo

Add :mvn/local-repo in the project deps.edn for just the current project. Or add it to $HOME/clojure/deps.edn for all projects

;; Value should be full path, otherwise jars are saved in a directory relative to the path where the clojure command was run 
:mvn/local-repo "/home/user/.cache/maven/repository"
}```

This is a separate configuration to that of remote repositories, which is done as follows
```:mvn/repos {
   "central" {:url "https://repo1.maven.org/maven2/"}
   "clojars" {:url "https://repo.clojars.org/"}
 }```

The local repository can also be set from the command line
`clojure -Sdeps '{:mvn/local-repo "$XDG_CACHE_HOME/maven/repository"}'`

`clojure -path` shows the class path and each library will have a path within the local maven repository.

If the :mvn/local-repo is not set, then it defaults to `$HOME/.m2/repository` directory.

Exercise: Park Run

Given hash-map data of park run events
When searching for fastest time, specific weather, hightest/lowest temperature, number of runners, etc
Then return the hash-map containing all the run details of the matching days

Shows the use of recur and its ability to keep track of a value as it processes a sequence of values from a collection.

data.priority-map

like a normal map you insert key/value pairs into it, but unlike a normal map when you call seq on it, it is guaranteed to return the key/value pairs in order by the value, which is the "priority" that you pick.
https://github.com/clojure/data.priority-map

Possible use cases
keep a ranking of users

  • could help to avoid using sort-by or reduce-kv each time

clojure.test - defn- testing

Cannot test defn- functions when calling them from a different namespace (i.e. the test namespace). So if going to go with sut/ alias in namespace, then need to mark defn- tests later in the process, once you have wrapped all your potential defn- tests with tests at the namespace level (as those tests will call functions that are public)
Example image in spacemacs image directory

Clojure.core - loop

Often in functional langues iteration (for loops or while loops in C) are expressed as recursive function calls and the compiler recognizes those recursive calls as being loops and compiles them as such. Clojure doesn't do that, and instead provides the less general combination of loop/recur which in usage follow the same pattern as an iterative process encoded as recursive function calls
(this might be a good point for something about for not being a loop in clojure)

Clojure's for loop is like for-comprehensions in Python, I think?

one way to think of loop in Clojure is that while it is possible to write loops similar to how they are written in imperative languages, it is definitely not typical "idiomatic" Clojure code. A purely functional loop expression does not modify any values that are visible when you enter the loop, and technically it doesn't modify the 'loop variables' either. The first time you start the loop, the symbols given in square brackets are 'bound' (similar to assigned, but only once) to the initial values shown.
Leave out the possibility of nested loop expressions for simplicity for the moment.
The loop body is evaluated, say having some conditional expressions like if or cond, but no assignment statements like you would find in imperative languages.
Either that branching ends with evaluating an expression that is not a recur expression, and that value becomes the return value of the entire loop expression.
Or it ends with evaluating a recur expression. That is similar to a recursive call to the beginning of the loop expression, with new initial values for all loop 'variables' (except they are not variables). So those loop variables are kinda sorta "assigned a value only once at the beginning of each loop iteration", not at arbitrary points in the middle.

To illustrate Andy's description
user=> (loop [acc 0 n 10]
(if (pos? n)
(recur (+ acc n) (dec n))
acc))
55

So that is maybe a 'negative' way to explain it, by the restrictions imposed on you -- you get to 'assign' (really 'bind') a value to the loop 'variables' (more technically 'symbols') at most one time per iteration through the loop, not any number of times you want like you could in imperative programming languages. Also the loop symbols are purely local to the loop -- they are out of scope when the loop returns (again, unlike in most imperative languages).

Text adventure game - moving - sequence of moves

A function takes a position
{:dir :NORTH :x 0 :y 0}
and a move instruction
"R10" (turn Right, go 10 units)returns a new position{:direction EAST :forward 10 :back 0}`

  "Given a position (direction, forward, back) and move instruction, returns a new position"
  [position action]
  (apply-delta (assoc position :direction (turn position action)) action))

Now start with a single position and a sequence of directions, ["R10", "L20", "R10"] and return the final position.

pseudocode:
var pos = {:dir :NORTH :x 0 :y 0}
for (var dir : directions) {
    pos = move(pos, dir)
}
return pos

In Clojure
`(reduce update-position initial-position)`

To see the "path" taken, use `reductions` instead 
`(reductions update-position initial-position)`

Clojure projects and dependencies

Configuration locations

  • deps.edn and where to put configs
    -- install (defaults)
    -- account (additional tools and common config)
    -- project (specific dependencies and aliases)

Adding dependencies

Adding aliases

Creating projects with clj-new

convert all values in a map that are keyword into nil

(clojure.walk/walk (fn [[k v :as m]] (if (keyword? v) [k nil] m)) identity {:valor 10 :teste :v})

it’s possible to make it work with clojure.walk, here is a Specter solution just for fun:
(def recurse-maps
(recursive-path [] p
(if-path map?
[MAP-VALS p]
STAY)))

user=> (setval [recurse-maps keyword?] nil {:a :b :c 1 :d {:a :b :c 1 :d {:a nil :b :c}}})
{:a nil, :c 1, :d {:a nil, :c 1, :d {:a nil, :b nil}}}

Or using postwalk

  (fn [form]
    (prn form)
    (if (and (map-entry? form) (keyword? (second form)))
      [(first form) nil]
      form))
  {:a 10 :b {:c :to-nil :d 73 :f {:x :bad :y :bad}}})```

Defining tests in function meta data

you can define your tests inside the meta data of a function, are there any pitfalls to doing this as a kind of documentation?

clojure.test is doing similar inside of deftest macro. You can also use clojure.test/test-var to execute your tests in the context of clojure.test

https://gist.github.com/DeLaGuardo/55ae4be5a932cf32b0f1e828e5109bb6

run it

clj -Sdeps '{:deps {some {:git/url "[email protected]:55ae4be5a932cf32b0f1e828e5109bb6.git" :sha "87da16f6ec05b7dcfa4f28590eed0894c3a2ddbf"}}}'

test runners ignoring reader comment macro

This is a bug in my understanding of the Clojure comment reader macro. Anything commented out with #_ still needs to be a valid form, otherwise how does the Clojure reader know where the comment ends :)

The CIDER test runner lets you get away with dodgy code in your source file, as it doesnt parse the source code file and just uses the expressions defined in the REPL already.

When running cognitect test runner

clojure -A:test:runner

An expression commented out with the #_ reader macro generated an error as the expression had a a missing value in a hashmap {()}

Terminal output

clojure -A:test:runner
Cloning: https://github.com/cognitect-labs/test-runner
Checking out: https://github.com/cognitect-labs/test-runner at 76568540e7f40268ad2b646110f237a60295fa3c
Downloading: org/clojure/tools.cli/0.3.5/tools.cli-0.3.5.jar from https://repo1.maven.org/maven2/

Running tests in #{"test"}
Syntax error reading source at (cervest/web_crawler.clj:93:10).
Map literal must contain an even number of forms

Full report at:
/tmp/clojure-67461614151858564.edn

The same thing happens with eftest :)

Which version of Java to use with Clojure

The team developing Clojure emphasize the LTS versions of Java, so recommend version 8 and 11 as primary right now.

The test matrix tests with Java version 12 too

Maven mirrors

the structure of maven is designed that you don't need to refresh - these are immutable, uniquely versioned artifacts

a nexus setup that aggregated a number of different repos, and our builds were setup to only check our nexus

Remote Mirrors

There may be a Maven Central Mirror closer to you region (assuming the default is in the USA somewhere).

mirror: https://maven-central-asia.storage-download.googleapis.com/repos/central/data/

Google remote mirror
https://cloudplatform.googleblog.com/2015/11/faster-builds-for-Java-developers-with-Maven-Central-mirror.html

googling for hong kong maven central proxy and came across these:
http://repo.maven.apache.org/maven2/.meta/repository-metadata.xml
https://maven.apache.org/guides/mini/guide-mirror-settings.html

Example repositories in a deps.edn file

 :mvn/local-repo "m2"
 :mvn/repos {
             "central" {:url "https://repo1.maven.org/maven2/"}
             ;"uk"      {:url "http://uk.maven.org/maven2/"}
             "clojars" {:url "https://repo.clojars.org/"}}

another example

 :mvn/local-repo "m2"
 ; https://cloudplatform.googleblog.com/2015/11/faster-builds-for-Java-developers-with-Maven-Central-mirror.html
 :mvn/repos {"central" {:url
                        "https://maven-central-asia.storage-download.googleapis.com/repos/central/data/"
                        #_"https://repo1.maven.org/maven2/"}
             "clojars" {:url "https://repo.clojars.org/"}}

NOTE: using clj -Sforce forces a classpath recompute, deleting the contents of .cpcache

tagged literals

#inst for dates
#uuid for unique idenfiers (user accounts, etc.)

Clojure CLI tool - use current path

You dont need to use a named path or even domain namespaces if you are just doing a quick hack. You can use the root directory for all your code. Probably not a great idea for a larger project than just hacking.

deps.edn
{:paths ["."]}

justcode.clj

(ns justcode)
(defn foo [x] (+ x 1))

Limiting output of an infinite sequence

(set! *print-length* 100)

Prevent lockup if *print-length* is set before running expressions that generate an infinite sequence.

Set *print-length* to nil for no limit on output of evaluation

CIDER sets this to 100 default (check this is correct)

Leiningen configuration
:repl-options map is a the key ``:init` which accepts any arbitrary Clojure form - seel Leiningen example project: https://github.com/technomancy/leiningen/blob/stable/sample.project.clj#L372.

You can add the code above to set the print-length to your project.clj or .lein/profiles.clj, which will apply it globally across all projects.

Clojure Abstraction Levels

If you have loop/recur consider looking at the whole sequence api

  • If you want to apply a transformation to each element reach for map/mapv
  • If you’re looking to remove items, look at things like distinct, set, filter, remove, keep
  • If you want to apply a side effect for each element reach for run! / doseq / doall
  • If you want to produce a single compound value from many look for specialised functions first, then at reduce
  • If you want to produce a sequence of values that depend on the previous value look at iterate
  • If you need to manipulate something stateful into being a sequence-like thing look at lazy-seq or a transducers.

There are many many more — for example: using transducers

Introducing iteration

discussing how iteration works in Clojure - its not just a for loop, clojure functions iterate

map, apply, reduce, filter, remove, ,,,

first and rest to step through a sequence of values

Using the empty sequence as a terminator

terminating early with reduced

naming unpure / side effect functions

using ! as a suffix on function names is a convention to show that the function has side effects (and is therefore not pure)

Slack discusion

hendore 15:00
I’ve noticed a convention of adding a ! suffix to function names that have side effects, so I have a few functions that insert/update a database such as create-account! add-booking! etc… This got me thinking, functions that query the database are also not pure functions as the result may be different (if someone was to edit the database) so should functions such as get-booking also share the same convention?
Also, if create-account! is called from a register function, should I also suffix the register function with ! as it has side effects?
I understand it makes no difference at all but would just like to get into good habits whilst learning
danielneal 15:25
Yep, when I’ve seen this convention, only the side-effecting functions tend be indicated by the !, rather than all impure functions. So get-booking wouldn’t have a ! but register! probably would. (edited)
hendore 15:34
Sweet 👍
dharrigan 15:38
I mostly use ! for things that my program modifies, i.e., writing to file, writing to a database, sending data across the wire. Things which I consume, I don't suffix with ! (unless I then modify and write out, external to my application)

Project Euler style challenges for new coders

Something similar to project Euler, but less brutal for beginners?
teaching how to solve algorithmic problems but on a playful manner. Something like many problems that you should be able to solve using ideas more than just coding endlessly?

Thunk - delay calculation

an expression that starts with # and does not contain a % for arguments is called a thunk

A thunk can delay calculation. Clojure also provides the delay function which is more elaborate. https://en.wikipedia.org/wiki/Thunk

An example of a Thunk

(repeatedly 5 #(rand-nth [:x :y :z])).

a lambda function that takes no other arguments to use as a parameter to the repeatedly function call.

Use external test runner

  • provides additional checks, makes sure you dont have definitions in the REPL that are not in the code files (ghost definitions can occur during refactoring code)

Definition: ghost expressions
A term (I think I just made up) for expressions that have been evaluated and are in the current running REPL, but the project files do not contain the code. Those expressions were either only written directly in the REPL or changed / deleted in the source code files.

Relates to #172

Namespaces - require

require loads a namespace (if it isn't already loaded) and can give the namespace a local alias (:as) and can also intern symbols from that namespace into the current namespace (so they are accessible without the alias) -- using :refer. refer itself does the same as require's :refer option -- but does not load the namespace first. refer-clojure is a shorthand for (refer 'clojure.core )
It's very rare to use the refer function since you normally need to load a namespace before first use, and it's easier to combine the require and refer operations in a single require with the :refer option.
refer-clojure is generally only needed when you are deliberately defining symbols in your namespace that "shadow" something in clojure.core and want to avoid the warning the compiler gives about replacing symbol definitions.
For example, https://github.com/clojure/clojure/blob/master/src/clj/clojure/string.clj#L42 -- since clojure.string defines replace and reverse that shadow (hide/replace) those two definitions in clojure.core.

Agents

A bank account with balance 100.00
Receive 20 request to withdraw $100.00

In Java you need to lock the resource as soon as the first request arrives, so that only the first one can be made and all the others fail.

I saw examples using the actor model were you can use a single actor to represent one account, and so the 20 requests would be made by the account actor, leading to the expected result
Does Clojure have any way on helping with this? I saw about the agents but i couldn’t find any information that i could digest on this.

Clojure Agents are a straightforward way to do something much like an actor, the important difference being that an actor is a method that you send data to, and an agent is data you send a function to

Set a validator so an the action won't succeed if it leaves a balance below 0 for example
you can also do this with atoms, which are much more intuitive to use

With multiple processes you need inter-process communication. It's also IPC if you ever reboot the VM (two processes, at different times, working on the same data world)

you can use refs to coordinate multiple stateful changes https://clojure.org/reference/refs

Hot loading of dependencies - Clojure CLI tools

Example of a work around for conflicts with Jackson library

clj -A:deps -Sdeps '{:deps {com.fasterxml.jackson.core/jackson-core {:mvn/version "2.7.5"}}}'

Clojure 1.10.1
user=> (require '[clojure.tools.deps.alpha.repl :refer [add-lib]])
nil
user=> (add-lib 'cheshire {:mvn/version "5.6.3"})
true
user=> (require 'cheshire.core)
nil
user=> 

Comment reader macro - more than just a line comment

#_ is the comment reader macro and is used to comment a Clojure form (Clojure code that can be evaluated). #_ is not just a line comment.

You can place #_ before the start of a form and everything inside that form will be commented

#_(def my-data [1 2 3 4 5])

#_ will comment forms that span multiple lines, for example function definitions

#_(defn my-function [args] (str "I am an experiment, so not always needed"))

#_ can also be put on the line before the comment (possibly many lines - to test). This approach can make your code more readable and keep alignment of your code consistent.

comment nested forms

#_ tells the reader to ignore the next form, it is therefore never evaluated and neither is the #_. This means that #_ can be used inside a nested form to comment just a part of the expression

In this example the third vector of values is not read by the Clojure reader and therefore is not passed as an argument to + function by map

(map + [1 2 3] [4 5 6] #_[7 8 9])

Stacking comments

The comment reader macro has the ability to stack these comments on forms

#_#_ to comment out two successive forms

For example in a let form we can comment out a name binding that is causing problems. As the name and value are both forms, then we use a stacked #_ to comment both out.
We also do the same in the body of the let, so as to not include the evaluation of the string or name2 local name in the str form.

(let [name1 "value"
       #_#_name2 "another-value]
   (str "First name is: " name1 #_#_" second name is: " name2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.